/* ==========================================================================
   MatterXP — site.css
   Rebuilt to match the Wix Studio design.

   Light-dominant site. White pages, mid-grey bands for "who we are",
   capability detail and the footer. Coloured gradient heroes mark the
   section you are in: orange for Projects, blue for Insights and Contact.
   The wordmark is a slab serif with an orange full stop; everything else
   is a geometric sans.
   ========================================================================== */

/* ----------------------------------------------------------- 0. Typefaces
   Self-hosted: faster than Google Fonts, no third-party request on every
   page load, and nothing to disclose in a cookie policy. 188KB for the set. */
/* Body and UI copy is Helvetica Neue, a system face on macOS and iOS that
   falls back to Helvetica then Arial elsewhere. Nothing to download.

   Rokkitt stands in for American Typewriter, which is an Apple system font and
   cannot be redistributed. Apple devices use the real face via the stack below;
   everyone else gets Rokkitt, the closest free match in weight and proportion. */
@font-face { font-family:"Typewriter Fallback"; src:url("../fonts/rokkitt-latin-300-normal.woff2") format("woff2"); font-weight:300; font-display:swap; }
@font-face { font-family:"Typewriter Fallback"; src:url("../fonts/rokkitt-latin-400-normal.woff2") format("woff2"); font-weight:400; font-display:swap; }
@font-face { font-family:"Typewriter Fallback"; src:url("../fonts/rokkitt-latin-500-normal.woff2") format("woff2"); font-weight:500; font-display:swap; }



/* --------------------------------------------------------------- 1. Tokens */
:root {
  /* Brand ------------------------------------------------------------- */
  --orange:      #DD6B0F;   /* primary   */
  --blue:        #008EFF;   /* secondary */
  --rose:        #FF5778;   /* secondary */

  /* Darkened for small text on light backgrounds (WCAG AA) */
  --orange-ink:  #AA520B;
  --blue-ink:    #006BC1;
  /* Hero fields: white small text needs 4.5:1, which the pure brand tones
     do not reach. These deepened shades do, and still read as the brand. */
  --orange-deep: #BB5A0C;
  --blue-deep:   #0077D6;

  /* Surfaces ---------------------------------------------------------- */
  --paper:       #FFFFFF;   /* page                       */
  --paper-soft:  #F0F0F0;   /* light band behind cards    */
  --slate:       #5E5E5E;   /* dark bands + footer        */
  --slate-deep:  #4F4F4F;   /* header bar                 */
  --rule:        #E2E2E2;   /* hairlines on light         */
  --rule-dark:   rgba(255,255,255,.22);

  /* Ink --------------------------------------------------------------- */
  --ink:         #2B2B2B;   /* body on light              */
  --ink-soft:    #595959;   /* secondary on light         */
  --on-dark:     #FBFBFB;
  --on-dark-soft:rgba(255,255,255,.80);

  /* Type -------------------------------------------------------------- */
  /* Swap these three lines for the real brand faces — nothing else needs
     to change. --display is the slab used by the wordmark. */
  /* American Typewriter ships with macOS and iOS, so Apple devices render the
     real brand face. Rockwell covers Windows machines with Office installed.
     "Typewriter Fallback" (Rokkitt, self-hosted) catches everything else. */
  --display: "American Typewriter", Rockwell, "Typewriter Fallback", Georgia, serif;
  --sans:    "Helvetica Neue", Helvetica, Arial, sans-serif;
  --text:    "Helvetica Neue", Helvetica, Arial, sans-serif;

  --t-hero:  clamp(3.4rem, 9.5vw, 8.5rem);   /* the big "matter."      */
  --t-over:  clamp(1.5rem, 3.4vw, 3rem);     /* "Creating moments that" */
  --t-xl:    clamp(2.2rem, 5vw, 4rem);       /* page titles            */
  --t-lg:    clamp(1.5rem, 2.6vw, 2.15rem);  /* section heads          */
  --t-md:    clamp(1rem, 1.15vw, 1.0625rem); /* body                   */
  --t-sm:    0.9375rem;
  --t-xs:    0.75rem;

  /* Space -------------------------------------------------------------- */
  --gutter:  clamp(1.25rem, 5vw, 5rem);
  --max:     1340px;
  --band:    clamp(3.5rem, 8vw, 7rem);
  --header:  clamp(56px, 6vw, 74px);
}

/* ------------------------------------------------------------- 2. Baseline */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: var(--t-md);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img, svg, video { display: block; max-width: 100%; }
/* Any explicit `display` beats the hidden attribute's own display:none, so
   anything with a display rule of its own stays on screen when hidden is set.
   This keeps `hidden` meaning hidden. */
[hidden] { display: none !important; }
/* The masthead is fixed, so anything scrolled to by an anchor or by focus
   would otherwise land underneath it. This keeps the target clear of it —
   the Cookie Policy link in the terms, and the contact confirmation. */
[id] { scroll-margin-top: calc(var(--header) + 1.5rem); }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

h1, h2, h3, h4 {
  font-family: var(--sans);
  font-weight: 300;
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 1.25em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

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

.skip {
  position: absolute; left: var(--gutter); top: -200px; z-index: 300;
  background: var(--orange); color: #fff;
  padding: .75rem 1.1rem; font-size: var(--t-sm);
}
.skip:focus { top: .75rem; }

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

.band        { padding-block: var(--band); }
.band--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }
/* Where the copy in one section reads straight on into the next, the usual
   band spacing opens a gap that breaks the thought in two. This pair closes
   it down to the paragraph rhythm, so the two read as one run of text.

   The gap is set here rather than left to the last paragraph's margin,
   because `p:last-child` has that margin stripped. 1.25em matches the
   paragraph margin, and resolves the same because both inherit the body
   size. */
.band--runs-on    { padding-bottom: 0; }
.band--follows-on { padding-top: 1.25em; }
.band--soft  { background: var(--paper-soft); }
.band--slate { background: var(--slate); color: var(--on-dark); }
.band--slate h1, .band--slate h2, .band--slate h3 { color: var(--on-dark); }

.measure { max-width: 60ch; }
.muted   { color: var(--ink-soft); }
.band--slate .muted { color: var(--on-dark-soft); }

/* The signature split: a light heading on the left, body copy on the right.
   Used on every editorial section of the site. */
.split {
  display: grid; gap: clamp(1.25rem, 3vw, 3.5rem);
  grid-template-columns: 1fr;
}
@media (min-width: 60rem) {
  .split { grid-template-columns: minmax(0,0.9fr) minmax(0,1.1fr); align-items: start; }
}
/* An empty cell is only there to hold the left column open in the two-column
   layout. Once the grid stacks it is an extra row, and its gap shows up as
   space nobody asked for. */
@media (max-width: 59.9375rem) {
  .split > div:empty { display: none; }
}
.split__head { font-size: clamp(1.15rem, 1.7vw, 1.45rem); font-weight: 400; opacity: .72; }
.split__head strong {
  display: block; margin-top: .3em; opacity: 1;
  font-size: clamp(1.5rem, 2.6vw, 2.15rem); font-weight: 300; letter-spacing: -0.02em;
}
.split__body > p { max-width: 62ch; }

/* Links that read as links inside body copy */
.split__body a:not(.btn), .article a:not(.btn):not(.pull) {
  color: var(--orange-ink);
  border-bottom: 1px solid currentColor;
}
.band--slate .split__body a:not(.btn) { color: #fff; }

.pull {
  display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--sans); font-size: var(--t-sm); font-weight: 500;
  padding-bottom: .3rem; border-bottom: 1px solid currentColor;
  transition: gap .25s ease, color .25s ease;
}
.pull:hover { gap: 1rem; color: var(--orange-ink); }
.band--slate .pull:hover { color: #fff; }

.btn {
  display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--sans); font-size: var(--t-sm); font-weight: 500;
  background: var(--orange); color: #fff;
  padding: .9rem 1.6rem; border-radius: 2px;
  transition: background .25s ease, transform .25s ease;
}
.btn:hover { background: #c45d0c; transform: translateY(-1px); }

/* Reveal on scroll */
.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity .7s ease, transform .7s cubic-bezier(.16,1,.3,1); }
.js .reveal.is-in { opacity: 1; transform: none; }

/* ---------------------------------------------------------------- 4. Chrome */
.masthead {
  position: fixed; inset: 0 0 auto; z-index: 200;
  /* 60% transparent, so the hero and the coloured page heads read through it.
     The blur keeps nav labels legible over busy photography. */
  background: color-mix(in srgb, var(--slate-deep) 40%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: background .3s ease;
}
/* Browsers without color-mix get a plain rgba equivalent of the same tone. */
@supports not (background: color-mix(in srgb, red 40%, transparent)) {
  .masthead { background: rgba(79, 79, 79, .4); }
}
.masthead__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; height: var(--header);
}

/* Wordmark: slab serif, lowercase, orange full stop */
.wordmark {
  font-family: var(--display);
  font-weight: 500; font-size: clamp(1.38rem, 2vw, 1.69rem);  /* 25% up */
  letter-spacing: -0.01em; color: #fff; white-space: nowrap;
}
.wordmark i {
  /* The brand dot is a true circle; a slab-serif full stop is squared off.
     The glyph stays in the markup for copy-paste and is pushed out of view. */
  font-style: normal; display: inline-block;
  width: .2em; height: .2em; border-radius: 50%;
  background: var(--orange); margin-left: .06em;
  text-indent: -10em; overflow: hidden;
}

.nav { display: flex; align-items: center; gap: clamp(2rem, 4.2vw, 4rem); }
.nav a {
  font-family: var(--sans); font-size: var(--t-sm); font-weight: 400;
  color: rgba(255,255,255,.88); position: relative; padding-block: .4rem;
  transition: color .2s ease;
}
.nav a:hover,
.nav a:focus-visible { color: var(--orange); }
/* The current page keeps a rule so it is identifiable without relying on
   colour alone; hover deliberately has none. */
.nav a[aria-current="page"] { color: #fff; }
.nav a[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; bottom: .15rem;
  width: 100%; height: 1px; background: currentColor;
}

.social { display: flex; align-items: center; gap: .85rem; }
.social a { color: rgba(255,255,255,.75); transition: color .2s ease; }
.social a:hover { color: #fff; }
.social svg { width: 15px; height: 15px; fill: currentColor; }

.burger { display: none; width: 42px; height: 42px; align-items: center; justify-content: center; }
.burger span { display: block; width: 22px; height: 1.5px; background: #fff; position: relative; transition: background .2s; }
.burger span::before, .burger span::after {
  content: ""; position: absolute; left: 0; width: 22px; height: 1.5px; background: #fff;
  transition: transform .3s cubic-bezier(.16,1,.3,1);
}
.burger span::before { top: -7px; }
.burger span::after  { top: 7px; }
.burger[aria-expanded="true"] span { background: transparent; }
.burger[aria-expanded="true"] span::before { transform: translateY(7px) rotate(45deg); }
.burger[aria-expanded="true"] span::after  { transform: translateY(-7px) rotate(-45deg); }

/* ------------------------------------------------------------------ 5. Hero
   Full-bleed photography, white type centred low. The overline is light
   geometric sans, the payoff is the slab. */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  min-height: min(100svh, 880px);
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  padding: calc(var(--header) + 2rem) var(--gutter) clamp(3rem, 8vh, 6rem);
}
.hero__media { position: absolute; inset: 0; z-index: -2; background: #1a1a1a; }
.hero__media img, .hero__media video {
  width: 100%; height: 100%; object-fit: cover;
  /* The slow push-in gives a still poster some life; on a real video it just
     reads as a gentle move. */
  animation: heroZoom 18s ease-out both;
}

/* The still is not shown while the film loads. Cutting from a photograph to
   moving footage a second later reads as a glitch, so the hero holds on its
   own dark ground instead and the film fades up out of it.

   It is kept in the markup for the one case that still needs it: a viewer who
   has asked for reduced motion, who gets the photograph and no film at all.
   If the embed is blocked the hero stays dark, which the white type and the
   scrim are already built for. */
.hero__still { display: none; }

/* An iframe has no object-fit, so it cannot simply be told to cover the box.
   Instead it is sized to a 16:9 rectangle that is always at least as large as
   the viewport in both directions and then centred, which crops the overspill
   exactly the way object-fit: cover would. 177.78vh is 16/9 of the height. */
.hero__film {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 100vw; height: 56.25vw;
  min-width: 177.78vh; min-height: 100%;
  border: 0; pointer-events: none;

  /* Hidden until the player reports itself running, because a blocked or
     failed embed paints an opaque error box. site.js adds .is-ready on the
     player's own message; if the film never arrives the class never lands.
     The fade is short so the film reads as arriving, not as a cross-dissolve
     from something else. */
  opacity: 0; transition: opacity .45s ease;
}
.hero__film.is-ready { opacity: 1; }

/* Nobody should have to sit through a push-in and a film at once. */
.hero__film { animation: none; }

/* A viewer who has asked for less motion gets the photograph instead, and no
   film at all — so this is the one place the still is shown. */
@media (prefers-reduced-motion: reduce) {
  .hero__film  { display: none; }
  .hero__still { display: block; }
}
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(to bottom, rgba(0,0,0,.42) 0%, rgba(0,0,0,.18) 38%, rgba(0,0,0,.52) 100%);
}
@keyframes heroZoom { from { transform: scale(1.08); } to { transform: scale(1); } }

.hero__title { color: #fff; }
.hero__over {
  display: block; font-family: var(--sans); font-weight: 300;
  font-size: var(--t-over); letter-spacing: -0.01em; line-height: 1.15;
}
.hero__mark {
  display: block; font-family: var(--display); font-weight: 400;
  font-size: var(--t-hero); line-height: .98; letter-spacing: -0.02em;
  margin-top: .08em;
}
.hero__mark i {
  font-style: normal; display: inline-block;
  width: .11em; height: .11em; border-radius: 50%;
  background: var(--orange); margin-left: .03em;
  text-indent: -10em; overflow: hidden;
}

.hero__line { display: block; overflow: hidden; }
.hero__line > span { display: block; animation: rise 1.1s cubic-bezier(.16,1,.3,1) both; }
.hero__line:nth-child(1) > span { animation-delay: .25s; }
.hero__line:nth-child(2) > span { animation-delay: .4s; }
@keyframes rise { from { transform: translateY(110%); } to { transform: none; } }

/* Scroll cue */
.hero__cue {
  position: absolute; left: 50%; bottom: clamp(1.25rem, 4vh, 2.5rem);
  transform: translateX(-50%);
  width: 26px; height: 26px; z-index: 2; color: #fff; opacity: .85;
  animation: cue 2.4s ease-in-out infinite;
}
.hero__cue svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 1.5; }
@keyframes cue { 0%,100% { transform: translate(-50%,0); } 50% { transform: translate(-50%,8px); } }

/* ------------------------------------------------- 6. Coloured page heroes
   Projects = orange, Insights and Contact = blue. The colour fades to the
   page white so the grid below sits on an unbroken sheet. */
.pagehead {
  position: relative;
  padding: calc(var(--header) + clamp(3rem,7vw,5.5rem)) 0 clamp(3.5rem, 8vw, 7rem);
  color: #fff;
}
.pagehead::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(176deg, var(--hue-deep, var(--orange-deep)) 0%, var(--hue-deep, var(--orange-deep)) 48%, var(--hue, var(--orange)) 68%, #fff 100%);
}
.pagehead--blue { --hue: var(--blue); --hue-deep: var(--blue-deep); }
.pagehead--slate { --hue: var(--slate); --hue-deep: var(--slate); }
.pagehead__title { font-size: var(--t-xl); font-weight: 300; margin-bottom: .35rem; }
/* Projects and Insights are single words set very large; 25% down sits them
   better against the body copy alongside. */
.pagehead__title--sm { font-size: calc(var(--t-xl) * .75); }
.pagehead__sub   { font-size: var(--t-lg); font-weight: 300; font-family: var(--sans); line-height: 1.25; letter-spacing: -0.015em; max-width: 36ch; }
/* Where the title has been taken down, the line under it has to come down too
   or the two end up near enough the same size and the hierarchy goes flat.
   Scoped to the reduced heads, so Contact, the case studies and the articles
   keep the original pairing. */
.pagehead__title--sm + .pagehead__sub { font-size: calc(var(--t-lg) * .8); }
.pagehead .split__body { color: rgba(255,255,255,.95); }
.pagehead .split__body a { color: #fff; }

/* On the lower half of the fade the text must go dark to stay legible */
.pagehead--fade .split__body { color: var(--ink); }

/* ------------------------------------------------------------- 7. Marquee */
.marquee { overflow: hidden; padding-block: clamp(2rem, 4vw, 3.25rem); }
.marquee__track {
  display: flex; width: max-content; align-items: center;
  gap: clamp(2.5rem, 6vw, 6rem);
  animation: slide 52s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
/* A client without a logo file runs as type. It is set to sit at about the
   optical weight of a logo in the same row, so a part-filled carousel does
   not look like two different things stitched together. */
.marquee__item {
  font-family: var(--sans); font-weight: 600;
  font-size: clamp(1rem, 1.7vw, 1.45rem); letter-spacing: -0.02em;
  color: var(--ink); white-space: nowrap; opacity: .62;
  transition: opacity .3s ease;
}
.marquee:hover .marquee__item { opacity: 1; }

/* Logos arrive at every aspect ratio, from a square roundel to a wordmark
   seven times wider than it is tall. Each is given the same box and fitted
   inside it, so a wide wordmark is limited by width and a square mark by
   height, and the set reads as evenly weighted. Pinning height alone would
   make Fairline enormous and Volkswagen a dot.

   Greyscale stops eighteen competing brand palettes fighting the page;
   colour returns when the carousel is paused on hover. */
.marquee__logo {
  width: clamp(88px, 10vw, 130px);
  height: clamp(30px, 3.6vw, 44px);
  flex: none; object-fit: contain; object-position: center;
  filter: grayscale(1); opacity: .62;
  transition: filter .3s ease, opacity .3s ease;
}
.marquee:hover .marquee__logo { filter: grayscale(0); opacity: 1; }

@keyframes slide { to { transform: translateX(-50%); } }

/* The track is twice the list, so with motion turned off it would sit at a
   seam. Showing the first half only keeps it a tidy static row. */
@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
  .marquee__track > [aria-hidden="true"] { display: none; }
}

/* -------------------------------------------------------- 8. Capabilities
   Four across on a light band: white cards, thin line icon, label beneath. */
.caps {
  display: grid; gap: clamp(.5rem, 1vw, .9rem);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 56rem) { .caps { grid-template-columns: repeat(4, 1fr); } }

.caps__item {
  background: var(--paper);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1.5rem; text-align: center;
  padding: clamp(1.75rem, 3vw, 2.75rem) 1rem;
  min-height: clamp(11rem, 16vw, 14.5rem);
  transition: transform .35s cubic-bezier(.16,1,.3,1), box-shadow .35s ease;
}
.caps__item:hover { transform: translateY(-4px); box-shadow: 0 10px 30px rgba(0,0,0,.07); }
.caps__item h3 {
  font-family: var(--text); font-size: var(--t-sm); font-weight: 400;
  letter-spacing: 0; line-height: 1.4; color: var(--ink-soft);
}
.cap-icon { width: clamp(52px, 5vw, 66px); height: clamp(52px, 5vw, 66px); }
.cap-icon svg { width: 100%; height: 100%; }
.cap-icon .ln { fill: none; stroke: var(--ink); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.cap-icon .ac { fill: none; stroke: var(--orange); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.cap-icon .acf { fill: var(--orange); stroke: none; }

/* ------------------------------------------------- 8b. Icon motion
   The Wix site uses animated GIFs here. These are SVG + CSS instead: sharper
   at any size, a fraction of the weight, and they stop dead for anyone who
   has asked for reduced motion. Each icon keeps the same idea as its GIF. */
.ic { width: 100%; height: 100%; display: block; }
/* No transform-box override here: SVG defaults to view-box, so the px
   transform-origins below are read in viewBox coordinates, which is what the
   cog and the ripple need to pivot on the right point. */

/* Two icons use a nested <svg> as a clipping viewport (cheaper than a clipPath,
   and no id to collide when the icon appears twice on a page). The global
   `svg { display:block; max-width:100% }` reset above would resize those inner
   viewports and let their contents escape, so undo it here. */
.ic svg { display: inline; max-width: none; overflow: hidden; }

/* Creative Strategy — rays flick out, filament glows on the same beat */
.ic--bulb .rays path { transform-origin: 50% 50%; animation: rayOut 2.8s ease-in-out infinite; }
.ic--bulb .rays path:nth-child(1) { animation-delay: 0s; }
.ic--bulb .rays path:nth-child(2) { animation-delay: .1s; }
.ic--bulb .rays path:nth-child(3) { animation-delay: .1s; }
.ic--bulb .rays path:nth-child(4) { animation-delay: .2s; }
.ic--bulb .rays path:nth-child(5) { animation-delay: .2s; }
.ic--bulb .rays path:nth-child(6) { animation-delay: .3s; }
.ic--bulb .rays path:nth-child(7) { animation-delay: .3s; }
.ic--bulb .glow { animation: glow 2.8s ease-in-out infinite; }
@keyframes rayOut {
  0%, 55%, 100% { opacity: .25; transform: scale(.78); }
  22%           { opacity: 1;   transform: scale(1.12); }
}
@keyframes glow { 0%, 55%, 100% { opacity: .45; } 22% { opacity: 1; } }

/* Virtual & Hybrid — the waveform scrolls behind the bezel */
.ic--screen .wave { animation: waveScroll 3.4s linear infinite; }
@keyframes waveScroll { from { transform: translateX(0); } to { transform: translateX(-44px); } }

/* Broadcast & Streaming — arcs ripple outward from the mast */
.ic--cast .ripple > g { transform-origin: 32px 28px; animation: ripple 2.4s ease-out infinite; }
.ic--cast .r1 { animation-delay: 0s; }
.ic--cast .r2 { animation-delay: .45s; }
.ic--cast .r3 { animation-delay: .9s; }
@keyframes ripple {
  0%        { opacity: 0;   transform: scale(.45); }
  28%       { opacity: 1;   transform: scale(.72); }
  70%, 100% { opacity: 0;   transform: scale(1); }
}

/* Production & Logistics — the cog turns, the hub stays put */
.ic--cog .spin { transform-origin: 32px 29px; animation: spin 14s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* 3D Environmental Design — the leading edge traces the box */
.ic--cube .trace { stroke-dasharray: 52; animation: trace 3.6s ease-in-out infinite; }
@keyframes trace {
  0%        { stroke-dashoffset: 52; opacity: .2; }
  35%, 65%  { stroke-dashoffset: 0;  opacity: 1; }
  100%      { stroke-dashoffset: -52; opacity: .2; }
}

/* Content Creation — the nib works, the line draws in behind it */
.ic--pen .nib { transform-origin: 32px 32px; animation: nib 3.2s ease-in-out infinite; }
.ic--pen .stroke { stroke-dasharray: 38; animation: penLine 3.2s ease-in-out infinite; }
@keyframes nib { 0%, 100% { transform: translate(-4px, 2px); } 50% { transform: translate(4px, -2px); } }
@keyframes penLine {
  0%       { stroke-dashoffset: 38; }
  55%, 85% { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: 38; }
}

/* Digital Design & Gamification — inputs light in sequence */
.ic--pad .keys > * { animation: press 1.9s ease-in-out infinite; }
.ic--pad .k1 { animation-delay: 0s; }
.ic--pad .k2 { animation-delay: .3s; }
.ic--pad .k3 { animation-delay: .6s; }
@keyframes press { 0%, 60%, 100% { opacity: .3; } 20% { opacity: 1; } }

/* AR / VR / XR — a highlight sweeps across the visor */
.ic--vr .sweep { animation: sweep 2.6s ease-in-out infinite; }
@keyframes sweep {
  0%, 100% { transform: translateX(-11px); opacity: .35; }
  50%      { transform: translateX(11px);  opacity: 1; }
}

/* Capability detail — two across on slate, icon above centred copy */
.capdetail { display: grid; gap: clamp(2.5rem, 5vw, 4.5rem); text-align: center; }
@media (min-width: 52rem) { .capdetail { grid-template-columns: repeat(2, 1fr); } }
.capdetail__item { display: flex; flex-direction: column; align-items: center; gap: 1.1rem; }
.capdetail__item h3 { font-size: var(--t-lg); font-weight: 300; }
.capdetail__item p { font-size: var(--t-sm); color: var(--on-dark-soft); max-width: 44ch; }
.capdetail .cap-icon { width: clamp(58px, 6vw, 76px); height: clamp(58px, 6vw, 76px); }
.capdetail .cap-icon .ln { stroke: rgba(255,255,255,.85); }

/* --------------------------------------------------------------- 9. Values */
.values { display: grid; gap: clamp(1.75rem, 3vw, 2.75rem); }
.values__item h3 {
  font-family: var(--sans); font-size: 1.1rem; font-weight: 600;
  letter-spacing: 0; margin-bottom: .4rem;
}
.values__item p { font-size: var(--t-sm); }

/* ----------------------------------------------------------------- 10. Grid
   Two-column image grid used by Projects and Insights. Caption sits under
   the frame: name, then category in a lighter tone. */
.grid {
  display: grid; gap: clamp(1.5rem, 3vw, 2.75rem) clamp(1.25rem, 2.5vw, 2.25rem);
  grid-template-columns: 1fr;
}
@media (min-width: 42rem) { .grid { grid-template-columns: repeat(2, 1fr); } }

.card { display: block; }
.card__frame {
  display: block; position: relative; overflow: hidden;
  aspect-ratio: 3 / 2; background: var(--paper-soft);
}
.card__frame img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .8s cubic-bezier(.16,1,.3,1);
}
.card:hover .card__frame img { transform: scale(1.04); }
.card__meta { display: block; padding-top: .9rem; }
.card__name {
  display: block; font-family: var(--sans); font-weight: 500;
  font-size: 1.05rem; letter-spacing: -0.01em;
}
.card__kind { display: block; font-size: var(--t-sm); color: var(--ink-soft); margin-top: .15rem; }

/* ------------------------------------------------------------ 11. Longform */
.article { max-width: 70ch; margin-inline: auto; }
.article > * { margin-bottom: 1.3em; }
.article p { font-size: 1.0625rem; line-height: 1.75; }
.article h2 {
  font-family: var(--sans); font-size: clamp(1.35rem, 2.3vw, 1.85rem);
  font-weight: 400; margin-top: 2.1em; margin-bottom: .65em;
}
.article__standfirst {
  font-family: var(--sans); font-weight: 300;
  font-size: clamp(1.15rem, 1.9vw, 1.45rem); line-height: 1.45;
  color: var(--ink); border-left: 3px solid var(--orange);
  padding-left: clamp(1rem, 2vw, 1.5rem); margin-bottom: 2.2em;
}
.article figure {
  margin: 2.4em 0;
  width: min(100vw - var(--gutter)*2, 1040px);
  margin-inline: calc(50% - min(50vw - var(--gutter), 520px));
}
.article figure img { width: 100%; height: auto; }
.article figcaption {
  font-size: var(--t-xs); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-soft); margin-top: .75rem;
}
.article blockquote {
  margin: 2em 0; padding: 0 0 0 clamp(1rem,2vw,1.5rem);
  border-left: 3px solid var(--blue);
  font-family: var(--sans); font-weight: 300;
  font-size: clamp(1.2rem, 2vw, 1.6rem); line-height: 1.3;
}
.article blockquote cite {
  display: block; margin-top: .8rem; font-family: var(--text); font-style: normal;
  font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}
.byline {
  display: flex; flex-wrap: wrap; gap: .4rem 1.5rem;
  font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft);
  border-block: 1px solid var(--rule); padding-block: .9rem; margin-bottom: 2.2rem;
}

/* Numbered trend list */
.trends { list-style: none; counter-reset: t; margin: 2.2em 0; padding: 0; }
.trends li {
  counter-increment: t; display: grid; gap: .3rem 1.25rem;
  grid-template-columns: auto 1fr; padding-block: 1.4rem;
  border-top: 1px solid var(--rule);
}
.trends li:last-child { border-bottom: 1px solid var(--rule); }
.trends li::before {
  content: counter(t, decimal-leading-zero); grid-row: 1 / 3;
  font-family: var(--sans); font-weight: 600;
  font-size: clamp(1.5rem, 3vw, 2.3rem); line-height: 1; color: var(--orange);
  font-variant-numeric: tabular-nums;
}
.trends h3 { font-family: var(--sans); font-size: 1.1rem; font-weight: 500; align-self: center; }
.trends p { margin: 0; font-size: var(--t-sm); color: var(--ink-soft); }

/* ------------------------------------------------------- 11b. Legal pages */
/* Built on .article, so the measure, paragraph size and leading are shared
   with the insight articles. The differences are all about scanning: tighter
   headings, a rule between sections, and real bullets in lists. */
.article--legal { counter-reset: s; }
.article--legal h2 {
  counter-increment: s;
  font-size: clamp(1.1rem, 1.7vw, 1.3rem); font-weight: 500;
  margin-top: 2.6em; margin-bottom: .6em;
  padding-top: 1.5em; border-top: 1px solid var(--rule);
}
.article--legal h2:first-of-type { margin-top: .4em; padding-top: 0; border-top: 0; }

/* The terms are numbered in the source document. The counter writes those
   numbers so they cannot drift out of sequence when a clause is inserted. */
.article--legal--numbered h2::before {
  content: counter(s) ". "; color: var(--orange); font-weight: 600;
}

.article--legal ul { margin: 0 0 1.3em; padding-left: 1.3em; }
.article--legal li { font-size: 1.0625rem; line-height: 1.75; margin-bottom: .35em; }

.article--legal dl {
  margin: 0 0 1.3em; display: grid; gap: .35rem 1.5rem;
  grid-template-columns: auto 1fr; align-items: baseline;
}
.article--legal dt { font-size: var(--t-sm); color: var(--ink-soft); }
.article--legal dd { margin: 0; font-size: 1.0625rem; }

/* A cookie name is the thing the reader is scanning for, so it does not
   inherit the muted <dt> treatment used for plain labels like "Email". */
.article--legal code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .95rem; color: var(--ink);
  background: var(--paper-soft); padding: .15em .45em; border-radius: 2px;
}

/* Links inside legal copy need to be obvious, so they are underlined rather
   than relying on colour alone. */
.article--legal p a:not(.pull),
.article--legal li a,
.article--legal dd a {
  color: var(--orange-ink); border-bottom: 1px solid var(--rule);
}
.article--legal p a:not(.pull):hover,
.article--legal li a:hover,
.article--legal dd a:hover { border-bottom-color: currentColor; }

.stamp {
  font-size: var(--t-xs); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------------------------------------------------------- 12. Case study */
.case { display: grid; gap: clamp(2rem, 4vw, 3.5rem); }
@media (min-width: 60rem) { .case { grid-template-columns: minmax(0,0.85fr) minmax(0,1.15fr); } }
.case__gallery { display: grid; gap: clamp(.75rem, 1.5vw, 1.25rem); }
@media (min-width: 44rem) {
  .case__gallery { grid-template-columns: repeat(2, 1fr); }
  .case__gallery figure:first-child { grid-column: 1 / -1; }
  /* With exactly two figures, running the first full width would leave the
     second stranded in a half-empty row. Let that pair sit side by side. */
  .case__gallery:has(figure:nth-child(2):last-child) figure:first-child {
    grid-column: auto;
  }
}
.case__gallery { align-items: start; }
.case__gallery figure { margin: 0; }
.case__gallery .card__frame { aspect-ratio: 3 / 2; }
/* A portrait photograph forced into the landscape frame gets centre-cropped
   to a letterbox, which can lose most of the picture. write-cases.py measures
   each file and tags the upright ones so they keep an upright frame. */
.case__gallery .card__frame--tall { aspect-ratio: 4 / 5; }

.detail { display: grid; gap: .3rem; padding-block: 1rem; border-top: 1px solid var(--rule); }
.detail dt { font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }
.detail dd { margin: 0; font-size: var(--t-sm); }
.band--slate .detail    { border-top-color: var(--rule-dark); }
.band--slate .detail dt { color: rgba(255,255,255,.65); }
.band--slate .detail dd a { border-bottom: 1px solid rgba(255,255,255,.4); }

/* -------------------------------------------------------------- 13. Contact */
.form { display: grid; gap: 1.5rem 1.75rem; grid-template-columns: 1fr; }
@media (min-width: 40rem) { .form { grid-template-columns: repeat(2, 1fr); } }
.field { display: grid; gap: .45rem; }
.field--wide { grid-column: 1 / -1; }
.field label {
  font-size: var(--t-sm); color: rgba(255,255,255,.92);
}
.field input, .field textarea, .field select {
  font: inherit; font-size: var(--t-sm);
  background: rgba(255,255,255,.14); color: #fff;
  border: 1px solid rgba(255,255,255,.45); border-radius: 2px;
  padding: .7rem .8rem;
  transition: border-color .2s ease, background .2s ease;
}
.field input::placeholder, .field textarea::placeholder { color: rgba(255,255,255,.6); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: #fff; background: rgba(255,255,255,.22);
}
.field textarea { resize: vertical; min-height: 7rem; }
.field select option { background: var(--slate); color: #fff; }
.form__send { grid-column: 1 / -1; display: flex; justify-content: flex-end; }
.btn--ghost {
  background: transparent; border: 1px solid rgba(255,255,255,.7); color: #fff;
  min-width: 190px; justify-content: center;
}
.btn--ghost:hover { background: #fff; color: var(--blue-ink); }
.btn[disabled] { opacity: .55; cursor: progress; transform: none; }
.btn--ghost[disabled]:hover { background: transparent; color: #fff; }

/* The spam trap. Taken out of the layout and off the tab order, but not with
   display:none — some bots skip hidden fields, and this one works by being
   filled in. */
.hp {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}

.form__note {
  grid-column: 1 / -1; margin: 0;
  font-size: var(--t-xs); color: var(--on-dark-soft); max-width: 52ch;
}
.form__note a { border-bottom: 1px solid rgba(255,255,255,.45); }
.form__note a:hover { border-bottom-color: #fff; }

/* Empty until the form has something to report, so it takes no room and is
   announced only when it actually says something. */
.form__status { grid-column: 1 / -1; margin: 0; font-size: var(--t-sm); }
.form__status:empty { display: none; }
.form__status--ok    { color: #fff; }
.form__status--error { color: #FFC9A3; }

.form__done h2 {
  font-size: var(--t-lg); font-weight: 300; margin-bottom: 1rem;
}
.form__done p { max-width: 46ch; }
.form__done p + p { margin-top: .9rem; }
.form__done:focus { outline: none; }
.form__done a { border-bottom: 1px solid rgba(255,255,255,.45); }

.contact-split { display: grid; gap: clamp(2rem,4vw,3.5rem); }
@media (min-width: 58rem) { .contact-split { grid-template-columns: 0.8fr 1.2fr; } }

/* --------------------------------------------------------------- 14. Footer */
.colophon { background: var(--slate); color: var(--on-dark); padding-block: clamp(3rem, 6vw, 5rem) 2rem; }
.colophon .wordmark { font-size: 1.3rem; display: inline-block; margin-bottom: 2rem; }
.colophon__grid { display: grid; gap: 2.25rem; grid-template-columns: 1fr; }
@media (min-width: 46rem) { .colophon__grid { grid-template-columns: 1.3fr 1fr 1fr 1fr; } }

.colophon__cta {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(1.5rem, 2.6vw, 2.05rem); line-height: 1.2; letter-spacing: -0.01em;
  color: #fff; margin: 0;
}
.colophon address { font-style: normal; font-size: var(--t-sm); color: var(--on-dark-soft); line-height: 1.9; }
.colophon ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.colophon a { font-size: var(--t-sm); color: var(--on-dark-soft); transition: color .2s ease; }
.colophon a:hover { color: #fff; }
.colophon .social { margin-top: 1.25rem; }
.colophon .social a { color: rgba(255,255,255,.7); }

.legal {
  border-top: 1px solid var(--rule-dark); margin-top: 2.5rem; padding-top: 1.5rem;
  display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; justify-content: space-between;
  font-size: var(--t-xs); color: rgba(255,255,255,.6);
}
.legal p { margin: 0; }
.legal ul { display: flex; flex-wrap: wrap; gap: 1.25rem; list-style: none; margin: 0; padding: 0; }
.legal a { color: rgba(255,255,255,.6); font-size: var(--t-xs); }
.legal a:hover { color: #fff; }

/* --------------------------------------------------------------- 15. Mobile */
@media (max-width: 56rem) {
  .burger { display: inline-flex; }
  .masthead .social { display: none; }
  .nav {
    position: fixed; inset: 0; z-index: 190;
    flex-direction: column; align-items: flex-start; justify-content: center;
    gap: 1.25rem; padding: var(--gutter);
    background: var(--slate-deep);
    clip-path: inset(0 0 100% 0);
    transition: clip-path .5s cubic-bezier(.16,1,.3,1);
  }
  .nav.is-open { clip-path: inset(0 0 0 0); }
  .nav a { font-family: var(--sans); font-size: 1.7rem; font-weight: 300; color: #fff; }
  .masthead__inner { position: relative; z-index: 195; }
}

/* ------------------------------------------- 16. Reduced motion and print */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .marquee__track { animation: none; }
}

@media print {
  .masthead, .hero__media, .hero__cue { display: none; }
  body { background: #fff; color: #000; }
}
