/* ==========================================================================
   Jake Rayner, portfolio + freelance studio
   Theme variables first. js/main.js swaps them via the day / night / colour
   toggle. Defaults below are night mode: off-black and off-white mono.
   Type: Satoshi everywhere, Literata 500 reserved for the wordmark and small
   "Jake" accents. No italics, no mono, square edges, no border radius.
   Surfaces are minimal iOS-style glass: a light ink-tint fill with backdrop
   blur on barely-rounded corners. No outlines, shadows or divider rules.
   ========================================================================== */

/* Registered as real colours so a theme change can fade. The body
   background is a gradient, which the browser cannot animate, but it is
   built from these variables, so animating the variables fades it. */
@property --bg{syntax:'<color>'; inherits:true; initial-value:#0b0b0c;}
@property --bg2{syntax:'<color>'; inherits:true; initial-value:#141416;}
@property --ink{syntax:'<color>'; inherits:true; initial-value:#f6f6f7;}
@property --ink-dim{syntax:'<color>'; inherits:true; initial-value:rgba(246,246,247,.62);}
@property --accent{syntax:'<color>'; inherits:true; initial-value:#f6f6f7;}
@property --accent-2{syntax:'<color>'; inherits:true; initial-value:#f6f6f7;}
@property --accent-ink{syntax:'<color>'; inherits:true; initial-value:#0b0b0c;}
@property --line{syntax:'<color>'; inherits:true; initial-value:rgba(246,246,247,.14);}
:root{transition:--bg .9s ease, --bg2 .9s ease, --ink .9s ease, --ink-dim .9s ease, --accent .9s ease, --accent-2 .9s ease, --accent-ink .9s ease, --line .9s ease;}
@media (prefers-reduced-motion:reduce){:root{transition:none;}}

:root{
  /* night mode, which is also what the site loads on first visit: solid
     off-black and off-white, no warm tint */
  --bg:#0b0b0c; --bg2:#141416;
  /* dim ink is 62%, up from 58%: 58% dipped to 4.21:1 on Day's light bg,
     under AA for the small meta text it carries. 62% clears 4.8:1 there and
     barely changes the dark themes. Same value in setTheme and the head
     snippet on every page. */
  --ink:#f6f6f7; --ink-dim:rgba(246,246,247,.62);
  --accent:#f6f6f7; --accent-ink:#0b0b0c;
  /* the accent's lighter companion: colour palettes set a real second stop,
     day and night keep it equal to the accent so monos stay flat. Surfaces
     and headline accents paint with the gradient below; AA holds because
     the darkest stop is the AA-checked accent and accent-2 is always
     lighter. */
  --accent-2:var(--accent);
  --accent-grad:linear-gradient(135deg, var(--accent), var(--accent-2));
  --line:rgba(246,246,247,.14);
  --serif:'Literata', Georgia, 'Times New Roman', serif;
  --sans:'Satoshi', 'Helvetica Neue', system-ui, sans-serif;
  /* The display face, read by the header wordmark and the hero name so the
     two always match. Day and night always show the default (--serif); JS only
     overrides these when the shuffle button is tapped in colour mode. */
  --display:var(--serif);
  --display-weight:500;
  /* balances optical size against Literata; see the FACES comment in main.js
     for how each face's value was measured */
  --display-scale:1;
  /* one source of truth for the theme toggle metrics, so the shuffle button's
     reveal (which animates width and cancels the flex gap) can never drift
     out of step with the buttons it sits between */
  --tt-size:38px; --tt-gap:4px;
  --pad:clamp(22px,3.2vw,52px);
  --glass:color-mix(in srgb, var(--ink) 6%, transparent);
  /* the pane: light enough that the page still reads through it. Used by
     the client logo cells and by a tile waiting for its image. */
  --glass-soft:color-mix(in srgb, var(--ink) 3.5%, transparent);
  /* The height of the browser's retractable chrome, worked out by the
     browser rather than measured by us: lvh is the viewport with it gone,
     svh with it showing, so this is 0 wherever there is none (a desktop)
     and the bar's own height on any phone, whatever that phone is. */
  --edge:calc(100lvh - 100svh);
  /* Fades a fixed layer out as it approaches the bottom edge of the window,
     reaching nothing by 100svh, the highest that edge can ever sit. A phone
     cuts a fixed layer off around there while the page keeps painting behind
     the bar, so by the time any cut arrives there is nothing left to cut.
     Eased over several stops rather than run straight, because a plain
     linear fade shows its own starting edge as a faint line. Where --edge is
     0 every stop lands on the window's bottom edge, so this does nothing
     at all on a desktop. */
  --edge-fade:linear-gradient(to bottom,
    rgba(0,0,0,1)   calc(100svh - 3.2 * var(--edge)),
    rgba(0,0,0,.88) calc(100svh - 2.6 * var(--edge)),
    rgba(0,0,0,.66) calc(100svh - 2.0 * var(--edge)),
    rgba(0,0,0,.40) calc(100svh - 1.4 * var(--edge)),
    rgba(0,0,0,.18) calc(100svh - 0.85 * var(--edge)),
    rgba(0,0,0,.05) calc(100svh - 0.35 * var(--edge)),
    rgba(0,0,0,0)   100svh);
  --glass-strong:color-mix(in srgb, var(--ink) 10%, transparent);
  --r:0;
}
*{box-sizing:border-box;}
html{scroll-behavior:auto;}
body{
  margin:0; color:var(--ink); font-family:var(--sans); -webkit-font-smoothing:antialiased;
  /* Two soft accent glows over the two background stops. In day and night
     the accent is the ink, so they read as a faint vignette; in colour mode
     they carry the palette hue, on top of the tint already mixed into --bg
     and --bg2 (see rollPalette). Fixed attachment, so they sit still behind
     the scroll rather than sliding with it. */
  background:linear-gradient(180deg, var(--bg) 0%, var(--bg2) 100%);
  transition:background-color .6s ease, color .6s ease;
}
/* The two accent glows live on a fixed layer of their own rather than as
   background-attachment:fixed on the body. A fixed body background makes a
   phone repaint the whole page every frame it scrolls, which was a good
   part of the jitter on Home; a fixed pseudo-element is composited once
   and just sits there. z-index -1 puts it under the content, above the
   body's own gradient. */
body::before{
  content:""; position:fixed; z-index:-1; pointer-events:none;
  /* Not inset:0. A fixed layer stops at the visible viewport, so on a phone
     it ends above the browser's bottom toolbar while the page keeps painting
     behind it. lvh, the viewport at its tallest with the toolbar retracted,
     reaches past it where it is supported.

     The layer's edges are the real hazard though, whatever its height: the
     lower glow used to sit at 108%, centred below the layer, so it was at
     its brightest exactly where the layer was cut and the seam across the
     bottom of the screen was as hard as it could be. Both glows now fade to
     nothing before they reach an edge, so no cut can show, wherever a
     browser decides to put one. */
  left:0; right:0; top:0; height:100vh; height:100lvh;
  background:
    radial-gradient(110% 46% at 88% 2%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 64%),
    radial-gradient(95% 38% at 6% 58%, color-mix(in srgb, var(--accent) 6%, transparent), transparent 64%);
  /* same fade as the dot field, so neither layer can leave an edge */
  -webkit-mask-image:var(--edge-fade);
  mask-image:var(--edge-fade);
}
::selection{background:var(--accent); color:var(--accent-ink);}
a{color:inherit;}
/* 2000px of content plus the side padding either side; every section on the
   page, work gallery included, sits inside this. */
.wrap{max-width:calc(2000px + var(--pad) * 2); margin:0 auto; padding:0 var(--pad);}
.wrap.full{max-width:none;}
img{max-width:100%; height:auto; display:block;}

h1,h2,h3{font-family:var(--sans); font-weight:500; margin:0; letter-spacing:-.03em;}
.serif{font-family:var(--serif); font-weight:500; letter-spacing:-.01em; font-optical-sizing:auto;}
/* The hero name swaps display faces on every theme change. Two things used to
   fight each other here: not clipping any face, and not letting any face move
   the layout. The fix keeps them apart entirely.
   #heroName itself now does nothing clever: line-height:normal, so the glyph
   never has an artificial box to be cut against. This used to be
   line-height:0, on the theory that a zero-height box could not move
   anything. Measured with a pixel-level ink test it turned out that ANY
   explicit line-height on this element other than "normal" clips descenders,
   0 included: Chrome derives the paintable ink area from the used
   line-height, not just its layout contribution, so a reduced line-height
   quietly truncates the glyph even though nothing sets overflow or clip-path
   anywhere. See README section 11, this cost real time to track down.
   The layout lock lives on `.hero h1` instead (below), which never changes
   font, so its own line-height is a stable reference regardless of what
   #heroName is currently rendering. */
/* font-size:calc(1em * scale) balances optical size against Literata: at the
   same declared size, capital-letter height across the original face pool
   spread from 26% shorter to 15% taller than Literata, so left alone the hero
   read as jumping in size on every shuffle even though its box never moves
   (the layout lock above is about position, this is about apparent size, they
   are different problems with different fixes). 1em here means "h1's own
   font-size", the stable reference the lock is built on. */
#heroName{font-family:var(--display); font-weight:var(--display-weight); font-size:calc(1em * var(--display-scale)); line-height:normal; will-change:opacity;}
/* retired: the mobile headline is one line now, the br stays hidden everywhere */
.hero-br{display:none;}
.dot{width:.11em; height:.11em; background:var(--accent-grad); display:inline-block; margin-left:.06em; transition:background-color .6s ease;}
.eyebrow{
  font-size:13.5px; font-weight:500;
  display:block; margin-bottom:14px;
  background:var(--accent-grad); -webkit-background-clip:text; background-clip:text; color:transparent;
}
.eyebrow .studio-brand{color:var(--accent-ink); -webkit-background-clip:border-box; background-clip:border-box;}
/* body text runs 75% of its container on request, site-wide */
.lead{color:var(--ink); font-size:18px; line-height:1.65; max-width:75%;}

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

.skip-link{
  position:fixed; top:10px; left:10px; z-index:100; background:var(--accent-grad); color:var(--accent-ink);
  font-size:13px; text-decoration:none; padding:10px 16px;
  transform:translateY(-300%); transition:transform .2s ease;
}
.skip-link:focus-visible{transform:none;}

/* Masked text rise utility, JS animates .mask-in upward into view.
   overflow:hidden is the point (it hides the pre-entrance text below the
   visible band) but a box sized to exactly one line-height is too short for
   real descenders (y, g, j, p, q), so it was permanently clipping the bottom
   of any heading that happened to contain one, not just during the reveal.
   The padding-bottom gives descenders room inside the clipped box; the
   matching negative margin-bottom cancels it for layout so nothing downstream
   moves. Applies to any heading using this utility, present or future. */
.mask{display:block; overflow:hidden; padding-bottom:.2em; margin-bottom:-.2em;}
.mask-in{display:block;}

/* line rise on page load, pure CSS so it works without JS. Nothing here
   clips: it is a short rise and fade, not a mask, so however tall or deep the
   hero's current display face is, no glyph is ever cut off. */
.line{display:block;}
.line > span{display:inline-block; opacity:0; transform:translateY(.16em); animation:riseUp .9s cubic-bezier(.2,.8,.2,1) forwards;}
@keyframes riseUp{to{opacity:1; transform:translateY(0);}}

/* one-time colour mode nudge, anchored under the header toggle. Solid
   surface, square edges, pops in with a bounce, and a nib points up at the
   palette button (which sits exactly one button-pitch right of the toggle
   group's centre, so the offset is --tt-size + --tt-gap). */

/* ---------- custom cursor ----------
   A small full-contrast ink square. Over links it scales down slightly and
   picks up a difference blend so it screens against whatever is under it. */
#cursor{
  position:fixed; top:0; left:0; width:13px; height:13px; opacity:0;
  background:var(--ink); pointer-events:none; z-index:999; mix-blend-mode:difference;
  transform:translate(-50%,-50%);
  transition:width .2s ease, height .2s ease, background-color .6s ease, opacity .3s;
}
#cursor.big{width:9px; height:9px; mix-blend-mode:difference;}
/* Over the studio banner on Home the cursor becomes the STUDIO lockup, the
   same square-edged block as the logo, reading "Visit the Studio" and
   sitting just right of the pointer. main.js adds .studio while the pointer
   is inside .nod and off any link inside it. The label span is created by
   main.js too. */
#cursor .cur-label{display:none;}
#cursor.studio{
  width:auto; height:auto; padding:.3em .58em; font-size:13px;
  background:var(--accent-grad); color:var(--accent-ink); mix-blend-mode:normal;
  transform:translate(10px,-50%);
}
#cursor.studio .cur-label{
  display:block; white-space:nowrap; line-height:1;
  font-family:'Chivo', var(--sans); font-weight:900; font-style:italic;
  text-transform:uppercase; letter-spacing:.02em; font-size:13px;
}
@media (hover:hover) and (pointer:fine){
  body, a, button{cursor:none;}
}
@media (hover:none){ #cursor{display:none;} }

/* site-wide dot texture, fixed behind everything */
/* sized to the tallest viewport for the same reason as body::before, and
   faded out along its bottom edge. The field is a fixed layer, so a phone
   cuts it off above the browser's toolbar while the page carries on
   painting behind it: dots to one side of that line, none to the other,
   which is the hard edge across the bottom of the screen. Fading them to
   nothing over the last stretch means there is no dot left to cut by the
   time the line arrives, and the field meets the plain background behind
   the toolbar without a join. */
.site-dots{
  /* width is explicit: a canvas is a replaced element, so left and right
     alone do not stretch it, and it sized itself from its 2:1 default
     aspect instead (1800px wide on a 1512px screen, 1700px on a phone),
     drawing thousands of dots nobody could see */
  position:fixed; left:0; right:0; top:0; width:100%; height:100vh; height:100lvh;
  z-index:-1; pointer-events:none;
  -webkit-mask-image:var(--edge-fade);
  mask-image:var(--edge-fade);
}

/* ---------- header: transparent, hides on scroll down, returns on scroll up ---------- */
header.nav{
  position:fixed; top:0; left:0; right:0; z-index:50;
  background:transparent; border:none;
  transition:transform .45s ease;
}
header.nav.nav-hidden{transform:translateY(-100%);}
/* The middle column is a fixed width, not auto. It used to size itself to the
   toggle's current content, so entering or leaving colour mode (which reveals
   or hides the fourth shuffle button) changed the column's width and shifted
   every nav link on the right by ~20px. The toggle already animates its own
   reveal with width:0 on the shuffle button (see .tt-rand below), so a fixed
   column just gives it constant room to do that in without moving anything
   outside itself. Width is 4 buttons plus 3 gaps at the toggle's own metrics,
   the most it is ever asked to hold. */
.nav-in{display:grid; grid-template-columns:1fr calc(var(--tt-size) * 4 + var(--tt-gap) * 3) 1fr; align-items:center; height:80px; gap:16px;}
/* the wordmark rides the same display face as the hero name, so the logo top
   left changes with every theme. Faces vary a lot in width, so it is sized in
   cap height terms and allowed to shrink rather than crowd the toggle. */
.mark{
  font-family:var(--display); font-weight:var(--display-weight);
  font-size:calc(22px * var(--display-scale)); text-decoration:none;
  display:flex; align-items:baseline; white-space:nowrap; justify-self:start;
  min-width:0; will-change:opacity;
}
.mark .dot{width:4px; height:4px; margin-left:3px;}

.theme-toggle{display:flex; gap:var(--tt-gap); justify-self:center;}
.tt-btn{
  width:var(--tt-size); height:34px; flex:0 0 auto; display:grid; place-items:center;
  padding:0; background:transparent; border:none; color:var(--ink-dim);
  transition:color .2s, background-color .2s;
}
.tt-btn:hover{color:var(--ink);}
.tt-btn[aria-pressed="true"]{background:var(--accent-grad); color:var(--accent-ink);}
.tt-btn svg{width:15px; height:15px; display:block; transform-origin:50% 50%;}
/* the shuffle icon spins one clean full turn when tapped, quick off the
   line and braking smoothly into place (the double-flip version read as
   odd). JS only ever attaches .tt-flip when reduced motion is off. */
@keyframes ttSpin{
  from{transform:rotate(0deg);}
  to{transform:rotate(360deg);}
}
.tt-btn svg.tt-flip{animation:ttSpin .65s cubic-bezier(.32,.09,.24,1) both;}
/* The shuffle button only belongs to colour mode. It stays in the flow and
   slides open from zero width, so switching modes reads as the row growing
   rather than a button popping in and shunting the header sideways. */
.tt-rand{
  width:0; overflow:hidden; opacity:0; pointer-events:none;
  transition:width .42s cubic-bezier(.2,.8,.2,1), opacity .26s ease, margin-left .42s cubic-bezier(.2,.8,.2,1);
  margin-left:calc(var(--tt-gap) * -1);
}
.tt-rand svg{transform:rotate(-90deg) scale(.6); transition:transform .42s cubic-bezier(.2,.8,.2,1);}
.theme-toggle.colour-on .tt-rand{width:var(--tt-size); opacity:1; pointer-events:auto; margin-left:0;}
.theme-toggle.colour-on .tt-rand svg{transform:none;}

.nav-right{display:flex; align-items:center; justify-content:flex-end; gap:22px;}
.nav-links{display:flex; align-items:center; gap:24px;}
.lnk{font-size:14px; text-decoration:none; color:var(--ink-dim); transition:color .2s;}
.lnk:hover{color:var(--ink);}
.lnk[aria-current="page"]{color:var(--ink);}
/* the STUDIO lockup keeps its accent-ink text when it is the current page:
   the plain page-ink treatment above would paint white on the white block */
.lnk.studio-brand[aria-current="page"]{color:var(--accent-ink);}

.menu-btn{
  display:none; background:transparent; border:none; color:var(--ink);
  padding:8px; align-items:center; justify-content:center;
  transition:color .2s;
}
.menu-btn:hover{color:var(--accent);}
.menu-ico{width:20px; height:12px; position:relative; display:inline-block;}
.menu-ico i{position:absolute; left:0; width:100%; height:1.5px; background:currentColor; transition:transform .3s ease, top .3s ease;}
.menu-ico i:first-child{top:1px;}
.menu-ico i:last-child{top:10px;}
.menu-ico.is-x i{top:5.5px;}
.menu-ico.is-x i:first-child{transform:rotate(45deg);}
.menu-ico.is-x i:last-child{transform:rotate(-45deg);}
@media (max-width:900px){
  .nav-links{display:none;}
  .menu-btn{display:flex;}
}

.magnetic{transition:transform .15s ease-out;}

/* The studio brand lockup, to Jake's spec: Chivo Black Italic uppercase on
   a solid accent block, accent-ink text. Chivo is loaded for this lockup
   only, it is not part of the display face pool. The one place italics are
   allowed. Chip and text follow the theme (accent / accentInk), which in
   Electric Navy is exactly the black-on-blue of the brand mock. */
.studio-brand{
  font-family:'Chivo', var(--sans); font-weight:900; font-style:italic;
  text-transform:uppercase; letter-spacing:.02em;
  display:inline-block; background:var(--accent-grad); color:var(--accent-ink);
  padding:.3em .58em;
  transition:background-color .6s ease, color .6s ease;
}
/* the .lnk hover would repaint the lockup text in page ink, hold it */
.lnk.studio-brand:hover{color:var(--accent-ink);}
/* On-page STUDIO wordmarks (the eyebrow above the pinned statement on Home,
   the studio hero eyebrow) run big, like a masthead. Menu instances (header
   nav, overlay) deliberately stay the same size as the other links: both
   were asked for explicitly. */
.eyebrow .studio-brand{font-size:28px;}

/* ---------- full screen menu overlay (mobile nav) ---------- */
.overlay{
  position:fixed; inset:0; z-index:60; display:flex; flex-direction:column;
  background:
    radial-gradient(1000px 600px at 85% -10%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 60%),
    linear-gradient(180deg, var(--bg2), var(--bg));
  visibility:hidden; opacity:0;
  transition:opacity .35s ease, visibility 0s linear .5s;
}
.overlay.is-open{visibility:visible; opacity:1; transition:opacity .35s ease;}
.ov-top{display:flex; align-items:center; justify-content:space-between; height:80px; width:100%;}
.ov-top .menu-btn{display:flex;}
.ov-nav{flex:1; display:flex; flex-direction:column; justify-content:center; width:100%; padding-top:16px; padding-bottom:16px;}
.ov-item{
  display:flex; align-items:baseline; gap:26px; text-decoration:none; color:var(--ink);
  padding:clamp(10px,2vh,20px) 0;
}
.ov-num{font-size:12px; color:var(--accent); min-width:30px; transition:color .6s ease;}
/* same descender-room fix as .mask above: none of the current nav labels have
   a descender, but this is the same clipping box and should not silently
   break the day a label does */
.ov-mask{display:block; overflow:hidden; padding-bottom:.2em; margin-bottom:-.2em;}
.ov-txt{
  display:inline-block; font-weight:500; letter-spacing:-.03em;
  font-size:clamp(34px, min(8vh, 10vw), 92px); line-height:1.04;
  transition:transform .35s ease, color .2s ease, opacity .2s ease;
}
.ov-item:hover .ov-txt{transform:translateX(14px); color:var(--accent);}
.ov-item[aria-current="page"] .ov-txt{color:var(--accent);}
.ov-item[aria-current="page"] .ov-txt.studio-brand{color:var(--accent-ink);}
.ov-foot{display:flex; flex-wrap:wrap; gap:12px 36px; padding-bottom:32px; font-size:13.5px; color:var(--ink-dim);}
.ov-foot a{color:var(--ink-dim); text-decoration:none; transition:color .2s;}
.ov-foot a:hover{color:var(--ink);}
html.menu-open, html.menu-open body{overflow:hidden;}

/* ---------- image placeholders, real files land later ---------- */
.ph{
  border:1px dashed var(--line);
  display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center;
  color:var(--ink-dim); font-size:12px; line-height:1.6;
  background:var(--glass); padding:16px;
}
.ph .tag{font-size:11px; font-weight:500; color:var(--accent); display:block; margin-bottom:6px; transition:color .6s ease;}
.ph .ph-note{margin-top:8px; opacity:.6; font-size:10px;}

/* ---------- section scaffold, no divider rules, just air ---------- */
main{display:block;}
section{padding:110px 0; position:relative;}
.sec-head{max-width:75%; margin-bottom:56px;}
.sec-head h2{font-size:clamp(34px,4.6vw,62px); line-height:1.02;}
.sec-head .lead{margin-top:18px;}
.zone-label{
  font-size:13.5px; font-weight:500; color:var(--accent);
  display:inline-block; margin-bottom:14px; transition:color .6s ease;
}

/* ---------- home hero ---------- */
.hero{padding:200px 0 0; position:relative;}
/* The layout lock, to Jake's Figma spec (JAKE 26, Hero 1440): the headline
   line container is a fixed 200px tall at the 165px reference size, which is
   1.212em of the h1's own font-size, so it scales with the clamp below. The
   line contents are vertically centred inside it, and the text itself is
   vertically trimmed (text-box below), so every face hangs from the same cap
   and baseline instead of from its own line box. That is what stops the
   jumping between faces. h1's own font never changes, so both references are
   stable. overflow:visible means tall or deep faces spill past this box
   freely without being cut, and because an explicit height does not grow to
   fit overflowing content, nothing below the hero ever moves. */
.hero h1{font-size:clamp(56px,11.5vw,170px); line-height:.97; height:1.212em; display:flex; align-items:center; overflow:visible; letter-spacing:-.04em;}
/* Figma's "vertical trim": the line's box becomes cap-top to baseline-bottom,
   measured from the h1's own font, a reference no display face can move.
   Centred in the fixed box above, the baseline lands in the same place for
   every face. Browsers without text-box support centre the untrimmed line
   box instead: a few px of per-face wobble, but the layout stays locked and
   nothing clips. */
.hero h1 > span{text-box:trim-both cap alphabetic;}
/* #heroName renders at line-height:normal (any tighter value clips it, see
   README section 11), which gives it its full natural ascent and descent.
   The 14px gap to the lead is from the same Figma spec: the trimmed centred
   box leaves enough slack that descenders clear it. */
/* hero body copy runs three quarters of the wrap on request (62ch read as a
   narrow column against the full-width headline), left aligned, a touch
   larger now the lines are longer. Mobile keeps full width below. */
.hero .hero-lead{margin-top:14px; font-size:20px; max-width:75%;}
.hero-meta{display:flex; flex-wrap:wrap; gap:10px 30px; margin-top:34px; font-size:13.5px; color:var(--ink-dim);}
.scroll-cue{
  display:flex; flex-direction:column; align-items:center; gap:10px; margin:66px auto 0;
  font-size:12px; color:var(--ink-dim);
}
.scroll-cue i{width:1px; height:44px; background:var(--ink-dim); transform-origin:top; animation:cueDrop 1.7s ease-in-out infinite;}
@keyframes cueDrop{0%{transform:scaleY(0);} 45%{transform:scaleY(1);} 100%{transform:scaleY(0); transform-origin:bottom;}}

/* ---------- clients strip: one line of 1:1 tiles ---------- */
.clients{margin-top:48px;}
.client-grid{display:grid; grid-auto-flow:column; grid-auto-columns:1fr; gap:1px;}
.client-cell{
  aspect-ratio:1; display:grid; place-items:center;
  background:var(--glass-soft);
  border-radius:var(--r);
  transition:background-color .3s ease, transform .35s ease;
}
.client-cell:hover{background:var(--glass);}
.client-logo{
  display:block; width:64%; height:44%;
  background:var(--ink); opacity:.85;
  -webkit-mask-repeat:no-repeat; -webkit-mask-position:center; -webkit-mask-size:contain;
  mask-repeat:no-repeat; mask-position:center; mask-size:contain;
  transition:opacity .2s ease, background-color .6s ease;
}
.client-cell:hover .client-logo{opacity:1;}

/* ---------- home work collage ----------
   A staggered collage of mixed tile sizes on a 12 column grid, on the page
   ground. "Projects" sits small on
   the left, "My Work." big on the right. Tiles reveal the brand logo and
   project on hover, and main.js drifts each one at its own speed on scroll
   (data-speed) on top of a gentle parallax inside the image. */
.works{padding:clamp(80px,9vw,150px) 0 clamp(90px,10vw,160px);}
.works + .works{padding-top:0;}
.works-head{display:grid; grid-template-columns:1fr; align-items:end; gap:8px; margin-bottom:clamp(40px,5vw,84px);}
.works-label{font-size:13.5px; font-weight:500; letter-spacing:.08em; text-transform:uppercase; align-self:start; padding-top:.6em; opacity:.72;}
.works-title{font-size:clamp(44px,6.5vw,104px); line-height:.9; letter-spacing:-.045em; text-align:left;}
/* one group per project: tiles sit tight together inside a group, and the
   big gaps fall between projects */
.col-grid{display:flex; flex-direction:column; gap:clamp(72px,9vw,170px);}
.col-group{display:grid; grid-template-columns:repeat(12,1fr); gap:clamp(14px,1.6vw,24px); align-items:start;}
.col-item{position:relative; display:block; color:inherit; text-decoration:none; grid-column:span 12;}
/* the parallax drift is desktop only (see main.js), so the compositing
   layer it wants is too: 42 promoted tiles of full-size photos is real
   memory on a phone, for an animation that never runs there */
@media (min-width:761px){ .col-item{will-change:transform;} }
.span-4{grid-column:span 4;} .span-5{grid-column:span 5;} .span-6{grid-column:span 6;} .span-7{grid-column:span 7;} .span-8{grid-column:span 8;} .span-12{grid-column:span 12;}
/* single tiles can sit against the right edge instead of the left */
.end.span-4{grid-column:9 / 13;} .end.span-5{grid-column:8 / 13;} .end.span-6{grid-column:7 / 13;} .end.span-7{grid-column:6 / 13;} .end.span-8{grid-column:5 / 13;}
.col-item.centre{grid-column:3 / span 8;}
/* a pair of portrait shots sits centred */
.span-5.c2{grid-column:2 / span 5;} .span-5.c7{grid-column:7 / span 5;}
/* the second tile in a pair starts a little lower, so pairs zigzag */
.col-item.drop{margin-top:0;}
/* tiles take the natural shape of their image: nothing is cropped, the
   spans set the width and the image sets the height, so the collage's
   rhythm comes from the images themselves */
.col-media{position:relative; overflow:hidden;}
/* While a tile's image is on its way it shows the same glass as the client
   logo cells up in the hero: light enough that the page reads through it,
   so it is a pane rather than a slab. The tile already has its final shape
   (the img carries width and height), so nothing moves when the file
   lands; main.js adds is-loaded then and the shimmer stops. Any background
   an image needs is baked into the file itself, not painted here. */
.col-img{position:relative; background:var(--glass-soft);}
.col-img::before{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(105deg, transparent 30%, color-mix(in srgb, var(--ink) 7%, transparent) 50%, transparent 70%);
  background-size:220% 100%;
  animation:tileShimmer 1.6s ease-in-out infinite;
}
.col-img.is-loaded{background:none;}
.col-img.is-loaded::before{display:none;}
@keyframes tileShimmer{
  0%{background-position:120% 0; opacity:.6;}
  50%{opacity:1;}
  100%{background-position:-120% 0; opacity:.6;}
}
.col-img img{
  width:100%; height:auto; display:block;
  /* --zoom (set by tools/build-work.mjs per image) enlarges a shot inside
     its tile; the hover growth multiplies on top */
  transform:scale(var(--zoom,1)); transform-origin:center;
  transition:transform .9s cubic-bezier(.2,.8,.2,1);
}
/* a re-cropped image (CROP in tools/build-work.mjs): the tile takes the
   cropped shape and the image covers it, so --crop-pos decides what stays */
.col-img.is-crop{aspect-ratio:var(--crop-ratio);}
.col-img.is-crop img{height:100%; object-fit:cover; object-position:var(--crop-pos);}
/* hover caption: the image stays visible. A plain black gradient rises a
   short way from the bottom edge (no blur, asked for explicitly) so the
   white logo and line of meta read on any photo. Its height is capped so a
   tall tile does not get a tall band. visibility (flipped after the fade
   out) keeps it out of the way when the tile is not hovered. */
.col-media::after{
  content:""; position:absolute; left:0; right:0; bottom:0; height:clamp(140px, 38%, 230px);
  pointer-events:none; opacity:0; visibility:hidden;
  background:linear-gradient(to top, rgba(0,0,0,.78), rgba(0,0,0,.45) 45%, rgba(0,0,0,0));
  transition:opacity .45s ease, visibility 0s linear .45s;
}
.col-item:hover .col-media::after{opacity:1; visibility:visible; transition-delay:0s;}
/* shaped shots (SHAPED in tools/build-work.mjs): the same band as a real
   element, masked by the image itself (inline, from the builder) so it
   darkens the cards and leaves the gaps between them clear. The mask is
   sized to the whole image and pinned to the bottom, so the band shows
   just the bottom of the image's shape. */
.col-media.is-shaped::after{display:none;}
.col-shade{
  position:absolute; left:0; right:0; bottom:0; height:clamp(140px, 38%, 230px);
  pointer-events:none; opacity:0; visibility:hidden;
  background:linear-gradient(to top, rgba(0,0,0,.78), rgba(0,0,0,.45) 45%, rgba(0,0,0,0));
  -webkit-mask-size:100% auto; -webkit-mask-position:center bottom; -webkit-mask-repeat:no-repeat;
  mask-size:100% auto; mask-position:center bottom; mask-repeat:no-repeat;
  transition:opacity .45s ease, visibility 0s linear .45s;
}
.col-item:hover .col-shade{opacity:1; visibility:visible; transition-delay:0s;}
.col-item:hover .col-media.is-shaped .col-img img{transform:scale(var(--zoom,1));}
.col-cap{
  position:absolute; inset:0; display:flex; flex-direction:column; justify-content:flex-end; align-items:flex-start;
  text-align:left; gap:6px; padding:clamp(16px,1.8vw,26px); color:#fff; opacity:0; pointer-events:none;
  transition:opacity .45s ease;
}
.col-item:hover .col-cap{opacity:1;}
/* the logo is the title: where a tile has one, the text name is dropped
   (they usually say the same thing); tiles without a logo keep the name */
.col-logo + .col-name{display:none;}
.col-item.is-headed .col-name{display:none;}
.col-logo{
  /* every logo is drawn at one shared height (--lh) times a small per-brand
     balance factor (--ls, set inline on each tile): thin emblems like the
     Aston Martin and Bentley wings run larger than heavy wordmarks so their
     visual weight matches. The mask scales by height and keeps its own
     aspect, left aligned, so wide lockups just run wider. */
  --lh:clamp(16px,1.4vw,22px);
  display:block; width:100%; height:calc(var(--lh) * var(--ls, 1)); background:#fff; margin-bottom:4px;
  -webkit-mask-repeat:no-repeat; -webkit-mask-position:left center; -webkit-mask-size:auto 100%;
  mask-repeat:no-repeat; mask-position:left center; mask-size:auto 100%;
}
.col-name{font-size:clamp(18px,1.6vw,24px); font-weight:500; letter-spacing:-.03em; line-height:1.02;}
.col-meta{font-size:13px; font-weight:500; line-height:1.45; max-width:44ch;}
.col-logo, .col-name, .col-meta{transform:translateY(14px); transition:transform .55s cubic-bezier(.2,.8,.2,1);}
.col-item:hover .col-logo, .col-item:hover .col-name, .col-item:hover .col-meta{transform:none;}
.col-item:hover .col-name{transition-delay:.05s;} .col-item:hover .col-meta{transition-delay:.1s;}
/* title card: the same logo and line of meta as the hover caption, shown
   above a project's images on phones (and on any touch screen where there
   is no hover), so every project reads title card then its photos. No box:
   it sits straight on the page background, flush left with the photos
   below it, and the grid gap does the spacing */
.col-title{display:none; color:inherit; text-decoration:none;}
@media (hover:none), (max-width:760px){
  .col-cap, .col-media::after, .col-shade{display:none;}
  .col-title{
    display:flex; flex-direction:column; align-items:flex-start; gap:8px;
    grid-column:span 12; padding:0;
  }
  .col-title .col-logo{background:var(--ink); height:26px; transform:none;}
  .col-title .col-logo + .col-name{display:none;}
  .col-title .col-name{font-size:26px; transform:none;}
  .col-title .col-meta{font-size:13.5px; font-weight:400; color:var(--ink-dim); opacity:1; transform:none;}
  .col-item:hover .col-img img{transform:scale(var(--zoom,1));}
}
@media (max-width:760px){
  .works-head{grid-template-columns:1fr; gap:8px;}
  .works-title{text-align:left;}
  /* phones: one column. The gap between title card and photos, between
     photos, and between projects is the page's own side margin, so the
     column reads as one evenly spaced stack */
  .col-grid{gap:var(--pad);}
  .col-group{gap:var(--pad);}
  .col-group > .col-item{grid-column:span 12; margin-top:0;}
}

/* ---------- work gallery ----------
   Full screen photos for one project, paged by swipe (the track is a
   scroll-snap carousel, so a swipe is just a native scroll) or by the
   arrows. The footer carries the project's logo, its line of caption and
   the button that asks for the case study password. No backdrop blur: a
   blur over the whole page has to be recomposited on every frame of the
   swipe, which is exactly the stutter a carousel cannot afford on a phone. */
html.lb-open, html.lb-open body{overflow:hidden;}
.lb{
  position:fixed; inset:0; z-index:100; display:flex; flex-direction:column;
  touch-action:pan-y;
  background:color-mix(in srgb, var(--bg) 94%, #000);
  opacity:0; pointer-events:none; transition:opacity .28s ease;
}
.lb.open{opacity:1; pointer-events:auto;}
/* the stage is the photo area: the track fills it and the arrows sit at
   its vertical middle, which is where the photo is centred */
.lb-stage{position:relative; flex:1 1 auto; min-height:0; display:flex;}
/* the track is moved by transform, not by scrolling: see the drag handler
   in main.js for why native scrolling could not be relied on here.
   pan-y leaves vertical gestures to the browser and hands us the rest. */
.lb-track{
  flex:1 1 auto; min-width:0; display:flex; outline:none;
  will-change:transform; touch-action:pan-y;
}
/* a photo must not start an iOS image drag or a selection mid-swipe */
.lb-slide img{-webkit-user-drag:none; user-select:none; -webkit-user-select:none; -webkit-touch-callout:none;}
.lb-slide{
  flex:0 0 100%; height:100%;
  display:flex; align-items:center; justify-content:center;
  /* the same room top and bottom, so the photo really is centred; the
     close button floats over the corner rather than pushing it down */
  padding:clamp(56px,7vh,80px) clamp(14px,5vw,90px);
}
/* the box takes the shape of the shot, so the photo fills it exactly and a
   zoomed or cropped one is clipped by it just as its tile clips it */
.lb-slide .col-img{
  aspect-ratio:var(--nat-ratio,1); max-width:100%; max-height:100%;
  overflow:hidden; margin:auto;
}
.lb-slide .col-img img{
  width:100%; height:100%; object-fit:contain;
  transform:scale(var(--zoom,1)); transition:none;
}
.lb-slide .col-img.is-crop{aspect-ratio:var(--crop-ratio);}
.lb-slide .col-img.is-crop img{object-fit:cover; object-position:var(--crop-pos);}

.lb-x, .lb-nav{
  position:absolute; z-index:2; display:grid; place-items:center;
  width:44px; height:44px; padding:0; cursor:pointer;
  background:var(--glass-strong); border:none; color:var(--ink);
  transition:opacity .2s ease, background-color .2s ease;
}
.lb-x svg, .lb-nav svg{width:20px; height:20px;}
.lb-x{top:clamp(12px,2vw,22px); right:clamp(12px,2vw,22px);}
.lb-nav{top:50%; transform:translateY(-50%);}
.lb-prev{left:clamp(8px,1.6vw,20px);}
.lb-next{right:clamp(8px,1.6vw,20px);}
.lb-nav:disabled{opacity:.22; cursor:default;}
.lb.lb-single .lb-nav{display:none;}

.lb-foot{
  flex:0 0 auto; display:flex; align-items:flex-end; justify-content:space-between; gap:22px;
  padding:clamp(14px,2vw,24px) clamp(16px,5vw,48px) clamp(18px,3vh,30px);
}
.lb-cap{display:flex; flex-direction:column; align-items:flex-start; gap:6px; min-width:0; flex:1 1 auto;}
/* .lb-logo carries display:block, which would otherwise beat the [hidden]
   default and paint an empty bar for a project with no logo (The Loose Lead) */
.lb [hidden]{display:none !important;}
.lb-logo{
  --lh:clamp(20px,1.9vw,30px);
  display:block; width:100%; max-width:min(340px,58vw);
  height:calc(var(--lh) * var(--ls,1)); background:var(--ink);
  -webkit-mask-repeat:no-repeat; -webkit-mask-position:left center; -webkit-mask-size:auto 100%;
  mask-repeat:no-repeat; mask-position:left center; mask-size:auto 100%;
}
.lb-name{font-size:clamp(19px,2vw,28px); font-weight:500; letter-spacing:-.03em; line-height:1.05;}
.lb-meta{font-size:13px; line-height:1.5; color:var(--ink-dim);}
.lb-go{
  flex:0 0 auto; background:var(--accent-grad); color:var(--accent-ink); border:none; cursor:pointer;
  font:inherit; font-size:14.5px; font-weight:500; padding:13px 22px; white-space:nowrap;
}
/* phones: the caption stacks above a full width button */
@media (max-width:640px){
  .lb-foot{flex-direction:column; align-items:stretch; gap:16px;}
  .lb-go{width:100%; padding:15px 18px; font-size:15px;}
  .lb-nav{width:38px; height:38px;}
}

/* ---------- studio nod (home) ----------
   A big statement, not a boxed strip: still just a nod, the studio owns its
   own full landing page at freelance.html, but "nod" doesn't have to mean
   small type in a glass pill. Reuses the same data-anim="words" scroll-scrub
   utility as the statement on freelance.html: words sit dim until they scroll
   into the reveal band, then brighten to full ink. */
/* padding kept lean so the pinned pitch fits inside a laptop viewport */
.nod{padding:60px 0;}
/* on Home the whole pitch linked to the Studio page; on the Studio page it is not a link */
.nod[data-studio-link]{cursor:pointer;}
/* the STUDIO lockup runs at the same size as the statement text on request */
.nod .studio-brand{font-size:clamp(20px,2.2vw,30px);}
/* full width across the wrap on request, text stays left aligned; the pin
   centres the section vertically while the words light up. Sized down from
   the old one-liner statement: this is three paragraphs of pitch now and
   the whole pinned section has to fit on screen. */
.nod-statement{
  font-weight:500; letter-spacing:-.025em;
  font-size:clamp(20px,2.2vw,30px); line-height:1.24; max-width:none; margin:16px 0 0;
}
.nod-statement p{margin:0 0 .62em;}
.nod-statement p:last-child{margin-bottom:0;}
/* the closing line is the section's call to action: "Let's talk." carries
   the accent gradient and scrolls to the contact block */
.nod-talk{
  font-weight:500; letter-spacing:-.025em;
  font-size:clamp(20px,2.2vw,30px); line-height:1.24; margin:28px 0 0;
}
.nod-talk a{
  background:var(--accent-grad); -webkit-background-clip:text; background-clip:text;
  color:transparent;
  /* the underline is drawn in accent explicitly (the clipped text itself is
     transparent), and it is what makes this read as a CTA in day and night
     where accent equals ink */
  text-decoration:underline; text-decoration-color:var(--accent);
  text-decoration-thickness:2px; text-underline-offset:6px;
}
.nod-go{
  display:inline-flex; align-items:center; margin-top:30px;
  font-size:14.5px; color:var(--accent); text-decoration:none;
  transition:transform .3s ease, color .6s ease;
}
.nod-go:hover{transform:translateX(8px);}

/* ---------- case study password veil ----------
   Clicking a gated work tile on Home opens this: the whole screen blurs
   behind a centred password box. The case pages themselves are encrypted in
   production (tools/protect-work.mjs); this veil checks the password by
   actually decrypting the target page, then navigates. */
.gate-veil{
  position:fixed; inset:0; z-index:80;
  display:flex; align-items:center; justify-content:center; padding:24px;
  background:color-mix(in srgb, var(--bg) 55%, transparent);
  -webkit-backdrop-filter:blur(20px); backdrop-filter:blur(20px);
  opacity:0; pointer-events:none; transition:opacity .25s ease;
}
.gate-veil.open{opacity:1; pointer-events:auto;}
.gate-veil-box{max-width:430px; width:100%;}
.gate-veil-box h2{font-size:26px; font-weight:500; letter-spacing:-.02em; margin-bottom:12px;}
.gate-veil-box p{font-size:14.5px; line-height:1.6; color:var(--ink-dim); margin-bottom:24px;}
.gate-veil-box p a{color:var(--ink);}
.gate-veil-row{display:flex; gap:8px;}
.gate-veil-row input{
  flex:1; min-width:0; background:var(--glass); border:none; color:var(--ink);
  font:inherit; font-size:15px; padding:13px 14px; outline:none;
}
.gate-veil-row input:focus-visible{outline:2px solid var(--accent); outline-offset:2px;}
.gate-veil-row button{
  background:var(--accent-grad); color:var(--accent-ink); border:none; cursor:pointer;
  font:inherit; font-size:14.5px; font-weight:500; padding:13px 22px;
}
.gate-veil-err{margin-top:14px !important; margin-bottom:0 !important;}
.gate-veil-req{margin-top:18px !important; margin-bottom:0 !important;}
/* the top-right escape hatch: a solid glass square like the gallery's,
   so it reads as a button on a phone and not as a stray glyph */
.gate-veil-close{
  position:absolute; top:clamp(12px,2vw,22px); right:clamp(12px,2vw,22px); z-index:2;
  display:grid; place-items:center; width:44px; height:44px; padding:0;
  background:var(--glass-strong); border:none; cursor:pointer; color:var(--ink);
  transition:background-color .2s ease, color .2s ease;
}
.gate-veil-close svg{width:20px; height:20px;}
.gate-veil-close:hover{color:var(--accent);}
.gate-veil-req a{color:var(--ink); text-decoration:none;}
.gate-veil-req a:hover{color:var(--accent);}

.client-cell{text-decoration:none;}

/* contact block body and text links, per the COPY_v2 contact spec */
.cta-body{font-size:17px; line-height:1.65; color:var(--ink-dim); max-width:75%; margin:18px 0 6px;}

/* ---------- studio landing hero ----------
   Fills the first screen on its own: the lockup, strapline, pitch and the
   two buttons sit vertically centred, and the offer starts below the fold. */
.studio-hero{min-height:100svh; display:flex; align-items:center; padding:140px 0 90px;}
.studio-hero .wrap{width:100%;}
.studio-hero .lead{font-size:clamp(17px,1.3vw,21px); max-width:68ch;}
@media (max-width:900px){ .studio-hero{min-height:auto; padding:132px 0 70px;} .studio-hero .lead{max-width:none;} }
.band .statement{
  font-weight:500; letter-spacing:-.025em;
  font-size:clamp(28px,4.2vw,54px); line-height:1.15; max-width:75%; margin:18px 0 38px;
}
.band-actions{display:flex; gap:14px; flex-wrap:wrap;}

/* ---------- big contact CTA band ---------- */
.cta{padding:150px 0;}
.cta-h{font-size:clamp(44px,8vw,120px); line-height:.98; letter-spacing:-.04em; margin:12px 0 40px;}
.cta-actions{display:flex; gap:14px; flex-wrap:wrap; align-items:center;}
.cta-meta{margin-top:28px; font-size:13.5px; color:var(--ink-dim);}

/* ---------- inner page hero ---------- */
.page-hero{padding:200px 0 70px;}
.page-hero h1{font-size:clamp(44px,8.5vw,120px); line-height:.97; letter-spacing:-.04em;}
.page-hero .lead{margin-top:26px;}

/* ---------- work index list, floating glass cards ---------- */
.case-list{display:grid; gap:26px;}
a.case-full{text-decoration:none; color:inherit;}
.case-full{
  display:grid; grid-template-columns:320px 1fr auto; gap:40px; align-items:start;
  padding:36px; background:var(--glass);
  transition:background-color .3s ease, transform .35s ease, box-shadow .35s ease;
}
a.case-full:hover{
  background:var(--glass-strong);
}
.case-thumb{aspect-ratio:4/3; font-size:11px;}
img.case-thumb{width:100%; object-fit:cover; display:block;}
.case-head{display:flex; align-items:baseline; gap:14px; flex-wrap:wrap; margin-bottom:10px;}
.case-full .co{font-weight:500; font-size:24px; letter-spacing:-.02em;}
.case-full .role{font-size:12.5px; font-weight:500; color:var(--accent); transition:color .6s ease;}
.case-full .desc{color:var(--ink); font-size:15px; line-height:1.65; margin:0 0 16px;}
.case-tags{display:flex; flex-wrap:wrap; gap:8px;}
.case-tags span{font-size:12px; padding:7px 13px; color:var(--ink-dim); background:var(--glass);}
.case-side{display:flex; flex-direction:column; align-items:flex-end; gap:16px; padding-top:8px;}
.case-full .metric{font-size:13px; color:var(--ink); white-space:nowrap;}
.case-go{font-size:12.5px; font-weight:500; color:var(--accent); white-space:nowrap; transition:color .6s ease;}
@media (max-width:900px){
  .case-full{grid-template-columns:1fr; gap:24px; padding:26px;}
  .case-thumb{aspect-ratio:16/9;}
  .case-side{flex-direction:row; justify-content:space-between; align-items:center; padding-top:0; width:100%;}
}

/* ---------- case study pages ---------- */
.case-hero{padding:190px 0 0;}
.case-back{font-size:13.5px; color:var(--ink-dim); text-decoration:none; transition:color .2s;}
.case-back:hover{color:var(--ink);}
.case-hero h1{font-size:clamp(44px,7.5vw,110px); line-height:.97; letter-spacing:-.04em; margin:22px 0 12px;}
.case-role{font-size:13.5px; font-weight:500; color:var(--accent); transition:color .6s ease;}
.meta-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-top:46px;}
.meta{
  background:var(--glass); padding:22px;
}
.meta .k{font-size:11px; font-weight:500; color:var(--ink-dim); display:block; margin-bottom:8px;}
.meta .v{font-size:14.5px; line-height:1.5;}
/* featured case header: Role, Timeline, Team, Sector, Scope, Outcome in two rows of three */
.meta-grid.six{grid-template-columns:repeat(3,1fr);}
@media (max-width:860px){ .meta-grid, .meta-grid.six{grid-template-columns:1fr 1fr;} }
@media (max-width:520px){ .meta-grid.six{grid-template-columns:1fr;} }
.case-media{margin:26px 0 0;}
.case-media .ph{aspect-ratio:16/9;}
.case-media img{width:100%; height:auto; aspect-ratio:16/9; object-fit:cover; display:block;}
/* artwork heroes are drawn to their own shape (16:9, or 4:3 on phones), so they are never cropped */
.case-media.art img{aspect-ratio:auto;}
.case-media figcaption, .case-gallery figcaption{font-size:12.5px; line-height:1.5; color:var(--ink-dim); margin-top:10px; max-width:75%;}
.case-gallery figure{margin:0;}
.case-gallery figcaption{max-width:none;}

/* ---------- case diagrams: a run of steps, left to right ----------
   Each step is a tile with a number, a name, one line and a small picture
   of what it holds; a chevron in the gap points at the next step. Phones
   stack the steps and the chevrons turn to point down. The steps rise in
   one after another when the figure scrolls into view (main.js adds
   .is-anim, then .is-in); without the script, or with reduced motion,
   they simply show. */
.flow-steps{list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(var(--n,5), minmax(0,1fr)); gap:28px;}
.flow-step{position:relative; background:var(--glass); padding:22px 20px 24px; display:flex; flex-direction:column; gap:8px; min-width:0;}
.flow-step:not(:last-child)::after{
  content:""; position:absolute; top:50%; right:-19px; width:10px; height:10px;
  border-top:1.5px solid var(--ink-dim); border-right:1.5px solid var(--ink-dim);
  transform:translateY(-50%) rotate(45deg);
}
.flow-n{font-size:11px; font-weight:500; letter-spacing:.08em; color:var(--ink-dim);}
.flow-h{font-size:17px; font-weight:500; letter-spacing:-.01em; line-height:1.25;}
.flow-p{font-size:13.5px; line-height:1.5; color:var(--ink-dim); margin:0;}
.flow-viz{margin-top:auto; padding-top:14px; min-height:58px; display:flex; flex-wrap:wrap; align-items:flex-end; gap:6px;}
.flow-viz code{font-family:ui-monospace, 'SF Mono', Menlo, monospace; font-size:11px; padding:4px 7px; background:var(--glass-strong); color:var(--ink); white-space:nowrap;}
.flow-viz .sw{width:22px; height:22px; background:var(--c);}
.flow-viz .btn-mini{width:100%; height:5px; background:var(--ink-dim); opacity:.6;}
.flow-viz .card-mini{width:30px; height:42px; border:1.5px solid var(--ink-dim);}
.flow-viz .line-mini{flex:1; display:flex; flex-direction:column; gap:6px; align-self:center;}
.flow-viz .line-mini i{display:block; height:5px; background:var(--ink-dim); opacity:.6;}
.flow-viz .line-mini i:last-child{width:60%;}
.flow-viz .dev{border:1.5px solid var(--ink-dim);}
.flow-viz .dev.phone{width:20px; height:36px;}
.flow-viz .dev.web{width:20px; height:36px; border-top-width:5px;}
.flow-viz .dev.desk{width:54px; height:34px; border-top-width:5px;}
.flow figcaption{max-width:75%;}
.flow.is-anim .flow-step{opacity:0; transform:translateY(18px); transition:opacity .6s ease, transform .7s cubic-bezier(.2,.8,.2,1); transition-delay:calc(var(--i,0) * 110ms);}
.flow.is-anim.is-in .flow-step{opacity:1; transform:none;}
@media (max-width:980px){
  .flow-steps{grid-template-columns:1fr; gap:26px;}
  .flow-step:not(:last-child)::after{top:auto; right:auto; bottom:-17px; left:28px; transform:rotate(135deg);}
  .flow-viz{min-height:0;}
}
@media (prefers-reduced-motion: reduce){
  .flow.is-anim .flow-step{opacity:1; transform:none; transition:none;}
}
.proc-row{display:grid; grid-template-columns:repeat(2,1fr); gap:16px; margin-top:36px;}
.proc-row img{width:100%; aspect-ratio:3/2; object-fit:cover; object-position:left top; display:block;}
@media (max-width:700px){ .proc-row{grid-template-columns:1fr;} }
.case-section{padding:80px 0 110px;}
.case-sub{font-size:clamp(24px,2.6vw,32px); margin-bottom:22px;}
.case-prose p{color:var(--ink); font-size:16.5px; line-height:1.75; max-width:75%; margin:0 0 18px;}
.case-prose .case-tags{margin-top:26px;}
.case-note{font-size:12.5px; color:var(--ink-dim); margin-top:30px;}
.case-actions{display:flex; gap:14px; flex-wrap:wrap; margin-top:30px;}
/* ---------- case study body (September 2026 rebuild from the portfolio deck) ----------
   A case study runs as a series of sections, each a heading, prose and its
   evidence: lists, cards, figures and rows of phone screens. Square, no
   borders or shadows, glass fills only, like everything else. */
.case-section + .case-section{padding-top:0;}
.case-kicker{display:block; font-size:12.5px; font-weight:500; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-dim); margin-bottom:12px;}
.case-part{font-size:clamp(34px,4.6vw,62px); line-height:1.02; letter-spacing:-.035em; margin:0 0 26px;}
.case-h3{font-size:clamp(20px,1.9vw,24px); font-weight:500; letter-spacing:-.02em; margin:44px 0 14px;}
.case-lists{display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-top:34px;}
.case-lists > div{background:var(--glass); padding:28px;}
.case-lists h3{font-size:13px; font-weight:500; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-dim); margin:0 0 16px;}
.case-ul{list-style:none; margin:0; padding:0; display:grid; gap:9px;}
.case-ul li{position:relative; padding-left:18px; font-size:14.5px; line-height:1.55; color:var(--ink);}
.case-ul li::before{content:""; position:absolute; left:0; top:.62em; width:6px; height:6px; background:var(--accent);}
/* a list that sits in the running text: prose size and measure */
.case-ul-prose{max-width:75%; gap:12px; margin-top:4px;}
.case-ul-prose li{font-size:16.5px; line-height:1.7;}
.case-ul-prose li::before{top:.72em;}
.case-ul-prose strong{font-weight:500;}
ol.case-ul{counter-reset:cs;}
ol.case-ul li{padding-left:26px; counter-increment:cs;}
ol.case-ul li::before{content:counter(cs); width:auto; height:auto; background:none; top:0; font-size:12px; font-weight:500; color:var(--ink-dim);}
.values.two{grid-template-columns:repeat(2,1fr);}
.values.four{grid-template-columns:repeat(4,1fr);}
.value img{display:block; width:calc(100% + 56px); max-width:none; margin:-28px -28px 22px; aspect-ratio:3/2; object-fit:cover;}
.value img.contain{object-fit:contain; background:#fff;}
.value .num{display:block; font-size:clamp(28px,3vw,40px); font-weight:500; letter-spacing:-.03em; line-height:1; margin-bottom:12px;}
.case-fig{margin:34px 0 0;}
.case-fig img{display:block; width:100%; height:auto;}
.case-fig.white img{background:#fff;}
.case-fig figcaption{font-size:12.5px; line-height:1.5; color:var(--ink-dim); margin-top:10px; max-width:75%;}
.case-fig.pair, .case-fig.trio{display:grid; grid-template-columns:repeat(2,1fr); gap:16px; align-items:start;}
.case-fig.trio{grid-template-columns:repeat(3,1fr);}
.case-fig.pair figcaption, .case-fig.trio figcaption{grid-column:1 / -1; margin-top:-6px;}
.case-fig.pair img, .case-fig.trio img{aspect-ratio:3/2; object-fit:cover;}
.case-fig.tall img{aspect-ratio:4/5; object-fit:cover;}
.case-fig.narrow img{max-width:min(100%,620px);}
/* a paragraph straight after a figure or a row of screens needs the same
   breathing room the figure has above it */
.case-fig + .case-prose, .case-screens-cap + .case-prose, .case-screens + .case-prose{margin-top:34px;}
.case-screens{display:grid; grid-template-columns:repeat(var(--n,6), minmax(0,1fr)); gap:14px; margin-top:34px;}
.case-screens img{display:block; width:100%; aspect-ratio:931/1657; object-fit:cover; object-position:top; background:#fff;}
.case-screens + figcaption, .case-screens-cap{font-size:12.5px; color:var(--ink-dim); margin-top:10px;}
.case-more{display:grid; grid-template-columns:repeat(2,1fr); gap:44px 16px; margin-top:34px;}
.case-more img{display:block; width:100%; aspect-ratio:16/10; object-fit:cover; background:#fff; margin-bottom:18px;}
.case-more h3{font-size:20px; font-weight:500; letter-spacing:-.02em; margin:0 0 10px;}
.case-more p{font-size:14.5px; line-height:1.65; color:var(--ink); margin:0 0 10px;}
.case-gallery{display:grid; grid-template-columns:repeat(2,1fr); gap:16px; margin-top:34px; align-items:start;}
.case-gallery img{display:block; width:100%; height:auto;}
.case-gallery .wide{grid-column:1 / -1;}
@media (max-width:860px){
  .values.four{grid-template-columns:repeat(2,1fr);}
  .case-screens{--n:3 !important;}
}
@media (max-width:760px){
  .case-lists, .values.two, .case-fig.pair, .case-fig.trio, .case-more, .case-gallery{grid-template-columns:1fr;}
  .case-fig figcaption, .case-media figcaption, .case-ul-prose{max-width:none;}
}
a.case-next{display:block; text-decoration:none; color:inherit;}
.case-next-in{padding-top:40px; padding-bottom:110px;}
.case-next .nl{font-size:12.5px; font-weight:500; color:var(--ink-dim);}
.case-next h2{font-size:clamp(38px,6.5vw,92px); line-height:.98; letter-spacing:-.04em; margin-top:14px; transition:color .25s ease;}
a.case-next:hover h2{color:var(--accent);}

/* ---------- about page ---------- */
.about-grid{display:grid; grid-template-columns:1.1fr .9fr; gap:60px; align-items:start;}
.about-grid .lead + .lead{margin-top:16px;}
.stat-list{display:grid; gap:16px;}
.stat{background:var(--glass); padding:24px;}
.stat .n{font-weight:500; font-size:40px; line-height:1; letter-spacing:-.02em;}
.stat .l{color:var(--ink-dim); font-size:13.5px; margin-top:6px;}
.values{display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-top:44px;}
/* About: career history from the CV, one glass row per role, newest first */
.career{display:grid; gap:8px;}
a.career-row{display:grid; grid-template-columns:minmax(150px,220px) 1fr; gap:24px; padding:22px 28px; background:var(--glass); color:inherit; text-decoration:none; transition:background .3s ease;}
a.career-row:hover{background:var(--glass-strong);}
.career-when{font-size:13px; color:var(--ink-dim); padding-top:3px;}
.career-what{display:flex; flex-direction:column; gap:4px;}
.career-co{font-size:19px; font-weight:500; letter-spacing:-.02em;}
.career-role{font-size:13.5px; font-weight:500; color:var(--accent); transition:color .6s ease;}
.career-note{font-size:14.5px; line-height:1.6; color:var(--ink); margin-top:4px;}
@media (max-width:700px){ a.career-row{grid-template-columns:1fr; gap:8px;} }
.value{background:var(--glass); padding:28px;}
.value h3, .value h4{font-weight:500; font-size:17px; letter-spacing:-.01em; margin:0 0 10px;}
.value p{color:var(--ink); font-size:14px; line-height:1.6; margin:0;}
@media (max-width:820px){ .about-grid{grid-template-columns:1fr;} .values{grid-template-columns:1fr;} }

/* ---------- freelance offer ---------- */
.offer-grid{display:grid; grid-template-columns:repeat(2,1fr); gap:16px;}
.offer{background:var(--glass); padding:34px;}
.offer .num{font-size:12px; font-weight:500; color:var(--accent); transition:color .6s ease;}
.offer h3{font-size:22px; margin:16px 0 10px; letter-spacing:-.02em;}
.offer p{color:var(--ink); font-size:14.5px; line-height:1.6; margin:0;}
@media (max-width:760px){ .offer-grid{grid-template-columns:1fr;} }

/* ---------- studio page: help cards, service grid, steps, clients ----------
   The offer for a small business owner, outcome first (help cards), then the
   full service list with an icon per service, then the three steps. All on
   the same glass surface as the rest of the site. */
/* the five offer blocks: image on one side, text on the other, alternating,
   stacked with a 16px gap like every other glass surface */
.help-list{display:grid; gap:16px;}
.help-card{background:var(--glass); display:grid; grid-template-columns:1fr 1fr; overflow:hidden;}
.help-card:nth-child(even) .help-media{order:2;}
.help-media{position:relative; min-height:320px;}
.help-media img, .help-media .ph{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; border:none;}
.help-txt{align-self:center; padding:clamp(32px,4vw,64px); display:flex; flex-direction:column; gap:14px;}
.help-num{font-size:12.5px; font-weight:500; color:var(--accent); transition:color .6s ease;}
.help-card h3{font-size:clamp(26px,2.6vw,40px); line-height:1.08; letter-spacing:-.025em;}
.help-card p{color:var(--ink); font-size:15.5px; line-height:1.65; margin:0; max-width:48ch;}
.svc-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:16px;}
.svc{background:var(--glass); padding:28px 26px 30px; display:flex; flex-direction:column; gap:18px;}
.svc-ico{
  width:44px; height:44px; flex:0 0 auto; display:grid; place-items:center;
  background:var(--accent-grad); color:var(--accent-ink);
  transition:background-color .6s ease, color .6s ease;
}
.svc-ico svg{width:22px; height:22px; display:block;}
.svc h3{font-size:18px; letter-spacing:-.015em; margin-top:6px;}
.svc p{color:var(--ink-dim); font-size:13.5px; line-height:1.55; margin:-8px 0 0;}
.steps{display:grid; grid-template-columns:repeat(3,1fr); gap:16px;}
.step{background:var(--glass); padding:34px;}
.step-top{display:flex; align-items:center; justify-content:space-between; gap:16px;}
.step-num{
  font-size:clamp(44px,5vw,72px); font-weight:500; letter-spacing:-.04em; line-height:1;
  background:var(--accent-grad); -webkit-background-clip:text; background-clip:text; color:transparent;
}
.step h4{font-weight:500; font-size:20px; letter-spacing:-.015em; margin:26px 0 10px;}
.step p{color:var(--ink); font-size:14.5px; line-height:1.6; margin:0;}
.work-feature .wf-img{position:relative; min-height:280px;}
.work-feature .wf-img img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;}
.clients-note{font-size:13.5px; color:var(--ink-dim); margin:56px 0 14px;}
.studio-logos{margin-top:0;}
.studio-logos .client-cell{aspect-ratio:3/2;}
@media (max-width:1100px){ .svc-grid{grid-template-columns:repeat(2,1fr);} }
@media (max-width:900px){
  .steps{grid-template-columns:1fr;}
  .studio-logos .client-grid{grid-auto-flow:row; grid-template-columns:repeat(3,1fr);}
}
@media (max-width:760px){
  .help-card{grid-template-columns:1fr;}
  .help-card:nth-child(even) .help-media{order:0;}
  .help-media{min-height:0; aspect-ratio:4/3;}
}
@media (max-width:640px){
  .svc-grid{grid-template-columns:1fr;}
  /* phones: icon on the left, title and detail beside it, so eight services
     read as a compact list rather than a long column of cards */
  .svc{display:grid; grid-template-columns:44px 1fr; column-gap:18px; row-gap:6px; padding:22px;}
  .svc-ico{grid-row:1 / 3;}
  .svc h3, .svc p{grid-column:2; margin:0;}
  .svc h3{align-self:center;}
}

.price-strip{display:flex; flex-wrap:wrap; gap:12px; margin-top:36px;}
.chip{font-size:13px; padding:10px 16px; color:var(--ink-dim); background:var(--glass);}
.chip b{color:var(--ink); font-weight:500;}

.work-feature{
  overflow:hidden; background:var(--glass); display:grid; grid-template-columns:1fr 1fr;
}
.work-feature .wf-txt{padding:42px;}
.work-feature .wf-img.ph{border:none; min-height:280px;}
.wf-tag{font-size:12.5px; font-weight:500; color:var(--accent); transition:color .6s ease;}
.work-feature h3{font-size:28px; margin:14px 0 12px; letter-spacing:-.02em;}
.work-feature p{color:var(--ink); font-size:15px; line-height:1.6;}
.wf-links{display:flex; gap:26px; flex-wrap:wrap;}
.wf-link{display:inline-block; margin-top:20px; font-size:13.5px; text-decoration:none; color:var(--ink); border-bottom:1px solid var(--accent); padding-bottom:2px; transition:border-color .6s ease;}
@media (max-width:760px){ .work-feature{grid-template-columns:1fr;} .work-feature .wf-img{min-height:220px;} .work-feature .wf-img.ph{min-height:180px;} }

.gallery-row{display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-top:16px;}
.gallery-row .ph{aspect-ratio:1; font-size:10px;}
@media (max-width:760px){ .gallery-row{grid-template-columns:repeat(2,1fr);} }

.soon-row{display:flex; gap:16px; margin-top:16px;}
.soon{flex:1; background:var(--glass); padding:26px; opacity:.55;}
.soon .sm{font-size:11.5px; font-weight:500; color:var(--ink-dim);}
.soon h4{font-weight:500; font-size:17px; letter-spacing:-.01em; margin:8px 0 0;}
@media (max-width:760px){ .soon-row{flex-direction:column;} }

/* ---------- contact page ---------- */
.email-xl{
  display:inline-block; font-weight:500; letter-spacing:-.03em;
  font-size:clamp(26px,5vw,70px); line-height:1.1; color:var(--ink); text-decoration:none;
  border-bottom:2px solid var(--accent); padding-bottom:8px; margin:32px 0 8px; word-break:break-word;
  transition:color .2s, border-color .6s ease;
}
.email-xl:hover{color:var(--accent);}
.contact-meta{display:flex; flex-direction:column; gap:10px; margin-top:30px; font-size:14.5px; color:var(--ink-dim);}
.contact-meta a{color:var(--ink-dim); text-decoration:none; transition:color .2s;}
.contact-meta a:hover{color:var(--ink);}

/* ---------- buttons + footer ---------- */
.btn{
  font-family:var(--sans); font-weight:500; font-size:15px; text-decoration:none;
  padding:15px 28px; background:var(--accent-grad); color:var(--accent-ink);
  display:inline-block; border:none;
  transition:transform .15s ease, background-color .6s ease, color .6s ease;
}
.btn:hover{transform:translateY(-2px);}
.btn.ghost{background:var(--glass-strong); color:var(--ink);}

footer{
  padding:38px 0; background:var(--glass);
}
.foot{display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:14px; color:var(--ink-dim); font-size:13.5px;}


/* ---------- iOS-style glass, the only surface treatment ----------
   Light fill + backdrop blur on barely-rounded corners. No outlines, no
   shadows, no inset edges.

   The client logo cells and the loading tiles are deliberately not in this
   list. They sit over the dot field, and a 16px backdrop blur smears those
   dots into a flat wash, which is what made the cells look like they filled
   in once the blur composited. They take the fill alone (--glass-soft), so
   the page still reads through them, and they cost nothing to scroll. */
.case-full, .meta, .value, .value-item, .offer, .stat, .soon, .chip, .case-tags span,
.work-feature, .btn.ghost, .help-card, .svc, .step{
  background:var(--glass);
  backdrop-filter:blur(16px) saturate(1.35);
  -webkit-backdrop-filter:blur(16px) saturate(1.35);
  border-radius:var(--r);
}
.btn{border-radius:var(--r);}
.tt-btn[aria-pressed="true"]{border-radius:var(--r);}
a.case-full:hover{background:var(--glass-strong);}
footer{
  background:var(--glass);
  backdrop-filter:blur(16px) saturate(1.35);
  -webkit-backdrop-filter:blur(16px) saturate(1.35);
}

/* ---------- mobile ----------
   Header shrinks so the menu button can never be pushed off screen (the
   toggle grows by one button in colour mode). Client logos wrap onto rows
   instead of squeezing nine across, and the hero headline keeps its jump
   over the body copy. */
@media (max-width:900px){
  :root{--tt-size:36px; --tt-gap:6px;}
  /* Mobile header: the wordmark on the left, then all four controls together
     in the top right, same size as each other and as the burger, evenly
     spaced. Flex rather than the desktop three column grid, because the
     toggle has to sit against the burger instead of centred, and because the
     row has to be free to change width when the shuffle button reveals. */
  .nav-in{display:flex; align-items:center; gap:var(--tt-gap); height:68px;}
  .mark{font-size:17px; margin-right:auto;}
  .theme-toggle{gap:var(--tt-gap); justify-self:auto;}
  .nav-right{gap:var(--tt-gap);}
  .tt-btn{width:var(--tt-size); height:var(--tt-size);}
  .tt-btn svg{width:16px; height:16px;}
  .menu-btn{width:var(--tt-size); height:var(--tt-size); padding:0;}
  /* width:0 has to be restated: the .tt-btn width above it is just as specific
     and would otherwise hold the shuffle button open. The negative margin
     swallows the flex gap as well, so closed really is zero. */
  .tt-rand{width:0; margin-left:calc(var(--tt-gap) * -1);}
  .theme-toggle.colour-on .tt-rand{width:var(--tt-size); margin-left:0;}
  .menu-btn{padding:6px;}
  .ov-top{height:68px;}

  .hero{padding:128px 0 0;}
  /* Mobile headline is one line since Jake's Figma pass (Hero 390): a flat
     57px at 390 wide (14.6vw), no per-face optical scale, and the trimmed
     cap box hugged at .72em with 28px of air above and below. The forced
     <br> and the old two-line 2.424em lock are gone. */
  .hero h1{font-size:clamp(44px,14.6vw,96px); height:.72em;}
  .hero .eyebrow{margin-bottom:28px;}
  #heroName{font-size:1em;}
  .hero .hero-lead{font-size:16.5px; margin-top:28px; max-width:none;}
  .lead, .sec-head, .case-prose p, .cta-body, .statement{max-width:none;}
  .hero-meta{font-size:12.5px; gap:8px 20px; margin-top:26px;}

  .clients{margin-top:40px;}
  .client-grid{grid-auto-flow:row; grid-template-columns:repeat(3,1fr);}
  .client-logo{width:74%; height:42%;}

  .sec-head{margin-bottom:36px;}
  section{padding:76px 0;}
  .nod{padding:76px 0;}
  .nod-statement{max-width:none;}
  .cta{padding:96px 0;}
  .page-hero{padding:132px 0 48px;}
  .case-hero{padding:128px 0 0;}
}

/* ---------- reduced motion: kill every looping or entrance animation ---------- */
@media (prefers-reduced-motion: reduce){
  .line > span{transform:none; animation:none; opacity:1;}
  .tt-rand{transition:none;}
  .scroll-cue i{animation:none; transform:none;}
  .magnetic, .btn, .ov-txt, header.nav, .col-img img, a.case-full{transition:none;}
  .col-img::before{animation:none;}
}

/* ============================================================
   Portfolio refresh, October 2026: Home project cards, the featured case
   study layout, press quotes, video embeds and the case diagrams.
   ============================================================ */

/* ---------- footer: contact on every page ---------- */
.foot-links{display:flex; flex-wrap:wrap; gap:6px 22px;}
.foot a{color:var(--ink-dim); text-decoration:none; transition:color .2s;}
.foot a:hover{color:var(--ink);}

/* ---------- Home: project header over each collage ----------
   Logo, name and years on the left, the project's tags and the case study
   link on the right; phones stack them. The earlier work shares one group,
   so each of its headers after the first gets air above it. */
.proj-head{grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:flex-end; gap:14px 28px; margin-bottom:clamp(2px,.6vw,8px); scroll-margin-top:96px;}
.proj-id{display:flex; flex-direction:column; align-items:flex-start; gap:8px; color:inherit; text-decoration:none; margin-right:auto;}
.proj-logo{display:block; height:calc(28px * var(--ls,1)); width:clamp(220px,24vw,340px); background:var(--ink); margin-bottom:6px;
  -webkit-mask-repeat:no-repeat; -webkit-mask-position:left center; -webkit-mask-size:auto 100%;
  mask-repeat:no-repeat; mask-position:left center; mask-size:auto 100%;}
.proj-name{font-size:clamp(22px,2vw,30px); font-weight:500; letter-spacing:-.03em; line-height:1.05; margin:0;}
.proj-when{font-size:13px; font-weight:500; color:var(--ink-dim);}
.proj-meta{display:flex; flex-wrap:wrap; justify-content:flex-end; align-items:center; gap:6px; max-width:1000px;}
.proj-tags{display:contents; list-style:none; margin:0; padding:0;}
.proj-tags li{font-size:12px; line-height:1.5; padding:7px 11px; background:var(--glass); color:var(--ink-dim);}
/* the case study link: a tag-sized solid button, white on the dark themes,
   with its arrow inside */
.proj-go{display:inline-flex; align-items:center; gap:8px; font-size:12px; font-weight:500; line-height:1.5; padding:7px 13px; background:var(--ink); color:var(--bg); text-decoration:none; white-space:nowrap; transition:opacity .2s ease;}
.proj-go:hover{opacity:.86;}
.proj-go i{font-style:normal; transition:transform .25s ease;}
.proj-go:hover i{transform:translateX(3px);}
.col-group > .work-label{grid-column:1 / -1; margin-bottom:clamp(8px,1.4vw,20px);}
.col-group.is-earlier > .proj-head ~ .proj-head{margin-top:clamp(40px,4.5vw,72px);}
.is-earlier .proj-logo{height:calc(23px * var(--ls,1));}
.is-earlier .proj-name{font-size:clamp(20px,1.6vw,24px);}
@media (max-width:760px){
  .proj-head{flex-direction:column; align-items:flex-start; gap:12px;}
  .proj-meta{justify-content:flex-start;}
}
.go{font-size:14.5px; font-weight:500; display:inline-flex; gap:8px; align-items:center;}
.go i{font-style:normal; transition:transform .25s ease;}
a:hover .go i, a.go:hover i{transform:translateX(4px);}
.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;}

/* ---------- featured case study layout ----------
   Each chapter is two columns: its number and title on the left, held in
   view while the text runs (sticky, native scroll), the text on the right
   at a comfortable measure. Pictures, diagrams and stats break out to the
   full width underneath. A strip of chapter links sits under the hero. */
.cs{padding:clamp(80px,8.5vw,140px) 0 0; scroll-margin-top:80px;}
.cs:last-of-type{padding-bottom:clamp(80px,8vw,120px);}
.cs-body p{font-size:17px; line-height:1.72; margin:0 0 18px;}
.cs-body p.cs-lede{font-size:clamp(19px,1.6vw,22px); line-height:1.55; letter-spacing:-.01em;}
.cs-body h3{font-size:clamp(19px,1.6vw,22px); font-weight:500; letter-spacing:-.02em; margin:42px 0 12px;}
.cs-body h3:first-child{margin-top:0;}
.cs-body a{text-underline-offset:3px;}
.cs-body .case-ul-prose{max-width:none; margin:6px 0 22px;}
.cs-full{margin:clamp(40px,4.5vw,64px) 0 0;}
.cs-full + .cs-full{margin-top:16px;}
.cs-full.diagram, .cs-full.pics{margin-left:0; margin-right:0;}
.cs-full.case-fig{margin-top:clamp(40px,4.5vw,64px);}
.cs .values{margin-top:clamp(40px,4.5vw,64px);}
.cs-actions{display:flex; flex-wrap:wrap; gap:12px; margin-top:26px;}
@media (max-width:900px){
  .cs-body p{font-size:16.5px;}
}

/* pictures in a case study: two or three side by side at one height */
.pics{display:grid; grid-template-columns:repeat(var(--n,2), minmax(0,1fr)); gap:16px; margin:0;}
.pics img{width:100%; height:100%; aspect-ratio:var(--ar,3/2); object-fit:cover;}
.pics figcaption, .cs-full figcaption{grid-column:1 / -1; font-size:12.5px; line-height:1.5; color:var(--ink-dim); margin-top:2px;}
.cs-full > img{width:100%; height:auto;}
.cs-full > figcaption{margin-top:10px;}
@media (max-width:700px){ .pics{grid-template-columns:1fr;} .pics.keep{grid-template-columns:repeat(var(--n,2), minmax(0,1fr)); gap:8px;} }

/* ---------- press quotes ---------- */
.press{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; margin:clamp(40px,4.5vw,64px) 0 0;}
.press blockquote{margin:0; padding:30px 30px 28px; background:var(--glass); display:flex; flex-direction:column; justify-content:space-between; gap:22px;}
.press q{font-size:clamp(28px,3.2vw,48px); font-weight:500; letter-spacing:-.035em; line-height:1.02; quotes:"\201C" "\201D";}
.press cite{font-style:normal; font-size:13.5px; color:var(--ink-dim);}
.press cite a{color:var(--ink); text-underline-offset:3px;}
@media (max-width:620px){ .press{grid-template-columns:1fr;} }

/* ---------- video: poster first, the player only on request ----------
   A button with the video's own poster; pressing it swaps in the YouTube
   player, muted, controls on (main.js). Nothing plays until it is asked
   to, so reduced motion never sees anything move by itself. */
.video{position:relative; aspect-ratio:16/9; background:#000; overflow:hidden;}
.video-btn{position:absolute; inset:0; width:100%; height:100%; padding:0; border:0; background:#000; cursor:pointer; color:#fff;}
.video-btn img{width:100%; height:100%; object-fit:cover; opacity:.82; transition:opacity .3s ease;}
.video-btn:hover img{opacity:1;}
.video-btn .play{position:absolute; left:50%; top:50%; width:76px; height:76px; transform:translate(-50%,-50%); background:rgba(11,11,12,.72); display:grid; place-items:center; backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);}
.video-btn .play::before{content:""; margin-left:5px; border-left:20px solid #fff; border-top:12px solid transparent; border-bottom:12px solid transparent;}
.video iframe{position:absolute; inset:0; width:100%; height:100%; border:0;}

/* ---------- case diagrams: shared parts ----------
   Boxes are glass tiles like every other surface. Arrows are a short line
   with a chevron head, drawn in the dim ink, and point with .r .l .u .d. */
.diagram{margin:clamp(40px,4.5vw,64px) 0 0;}
.diagram figcaption{font-size:12.5px; line-height:1.5; color:var(--ink-dim); margin-top:12px;}
.d-box{background:var(--glass); padding:18px 18px 20px; min-width:0;}
.d-box b{display:block; font-size:16px; font-weight:500; letter-spacing:-.01em; line-height:1.3;}
.d-box span{display:block; font-size:13px; line-height:1.5; color:var(--ink-dim); margin-top:6px;}
.d-box.solid{background:var(--ink); color:var(--bg);}
.d-box.solid span{color:color-mix(in srgb, var(--bg) 70%, transparent);}
.arw{position:relative; display:flex; align-items:center; justify-content:center; gap:8px; font-size:12px; font-weight:500; color:var(--ink-dim); min-width:28px; min-height:28px;}
.arw::before{content:""; flex:none; width:18px; height:18px; border-top:1.5px solid var(--ink-dim); border-right:1.5px solid var(--ink-dim); transform:rotate(45deg); margin-right:6px;}
.arw.l::before{transform:rotate(-135deg); margin:0 0 0 6px;}
.arw.u::before{transform:rotate(-45deg); margin:6px 0 0;}
.arw.d::before{transform:rotate(135deg); margin:0 0 6px;}
.arw.u, .arw.d{flex-direction:column; gap:4px;}
.diagram.is-anim .d-part{opacity:0; transform:translateY(16px); transition:opacity .6s ease, transform .7s cubic-bezier(.2,.8,.2,1); transition-delay:calc(var(--i,0) * 90ms);}
.diagram.is-anim.is-in .d-part{opacity:1; transform:none;}
@media (prefers-reduced-motion: reduce){ .diagram.is-anim .d-part{opacity:1; transform:none; transition:none;} }

/* D1: Scout, Analyse, Implement, Transfer. Four steps round a loop with
   arrows in the gaps, and a way out from Transfer to the owning department. */
.cycle{display:grid; grid-template-columns:minmax(0,1fr) 92px minmax(0,1fr); grid-template-rows:auto 64px auto 56px auto; max-width:880px;}
.cycle .c-a{grid-area:1/1;} .cycle .c-ab{grid-area:1/2;} .cycle .c-b{grid-area:1/3;}
.cycle .c-bc{grid-area:2/3;} .cycle .c-c{grid-area:3/3;} .cycle .c-cd{grid-area:3/2;}
.cycle .c-d{grid-area:3/1;} .cycle .c-da{grid-area:2/1;} .cycle .c-de{grid-area:4/1;} .cycle .c-e{grid-area:5/1;}
.cycle .c-e{align-self:start;}
@media (max-width:620px){
  .cycle{grid-template-columns:minmax(0,1fr) 40px minmax(0,1fr);}
  .cycle .arw{font-size:11px; text-align:center;}
  .cycle .d-box{padding:14px;}
  .cycle .d-box b{font-size:15px;}
}

/* D2: the delivery loop. Business forum into a backlog, three sprint lanes
   staggered one sprint apart, test findings back into design, then the
   sign-off gate before production. */
.loop{display:grid; gap:14px;}
.loop-row{display:grid; grid-template-columns:minmax(0,1fr) 44px minmax(0,1fr) 44px minmax(0,1fr); align-items:stretch;}
.loop-down{justify-self:start; margin-left:24px;}
.lanes{background:var(--glass-soft); padding:20px; display:grid; gap:10px;}
.lanes-axis{display:flex; justify-content:space-between; font-size:12px; font-weight:500; color:var(--ink-dim); margin-bottom:4px;}
.lane{display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px;}
.lane .d-box{grid-column:1 / span 3;}
.lane.off1 .d-box{grid-column:2 / span 3;}
.lane.off2 .d-box{grid-column:3 / span 3;}
.lane-back{display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px;}
.lane-back .arw{grid-column:2 / span 3; justify-content:flex-start;}
@media (max-width:760px){
  .loop-row{grid-template-columns:1fr; gap:6px;}
  .loop-row .arw{transform:rotate(90deg); justify-self:start; margin-left:20px;}
  .loop-row .arw::before{margin:0;}
  .lanes{padding:12px;}
  .lane{gap:6px;}
  .lane .d-box{padding:12px;}
  .lane .d-box b{font-size:14px;}
  .lane .d-box span{font-size:12px;}
}

/* D3: the HMI interaction model. One wide screen in three zones, the
   physical controls under it, and the two drive mode themes. */
.hmi{display:grid; gap:16px;}
.hmi-screen{background:var(--glass); padding:16px; display:grid; grid-template-columns:minmax(0,1fr) 34px minmax(0,1.35fr) 34px minmax(0,1fr); align-items:stretch;}
.hmi-zone{background:var(--glass); padding:18px; display:flex; flex-direction:column; gap:6px; min-height:150px;}
.hmi-zone b{font-size:16px; font-weight:500;}
.hmi-zone span{font-size:13px; line-height:1.5; color:var(--ink-dim);}
.hmi-zone.home{background:var(--glass-strong);}
.hmi-chips{display:flex; flex-wrap:wrap; gap:8px; margin-top:auto;}
.hmi-chips i{font-style:normal; font-size:12.5px; font-weight:500; padding:8px 11px; background:var(--ink); color:var(--bg);}
.hmi-swipe{display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; font-size:16px; color:var(--ink-dim);}
.hmi-swipe small{font-size:10.5px; letter-spacing:.06em; text-transform:uppercase; writing-mode:vertical-rl; transform:rotate(180deg);}
.hmi-under{display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1fr); gap:16px;}
.hmi-controls, .hmi-modes{background:var(--glass-soft); padding:18px;}
.hmi-row{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:10px;}
.hmi-ctrl{background:var(--glass); padding:16px 12px; text-align:center; font-size:13.5px; font-weight:500;}
.hmi-ctrl svg{display:block; width:28px; height:28px; margin:0 auto 10px; fill:none; stroke:var(--ink-dim); stroke-width:1.6; stroke-linecap:square;}
.hmi-note{display:block; font-size:12.5px; color:var(--ink-dim); margin-top:12px;}
.hmi-modes .hmi-row{grid-template-columns:1fr 1fr;}
.hmi-mode{height:62px; display:flex; align-items:flex-end; padding:10px 12px; font-size:13px; font-weight:500; color:#fff;}
.hmi-mode.gt{background:linear-gradient(120deg,#0f2a2a,#2f6e6a);}
.hmi-mode.sport{background:linear-gradient(120deg,#2a0f0b,#b23a1e);}
@media (max-width:760px){
  .hmi-screen{grid-template-columns:1fr; gap:8px;}
  .hmi-swipe{flex-direction:row;}
  .hmi-swipe small{writing-mode:horizontal-tb; transform:none;}
  .hmi-zone{min-height:0;}
  .hmi-under{grid-template-columns:1fr;}
}

/* five header tiles (no team given): the last one takes the spare column */
.meta-grid.six .meta:nth-child(5):last-child{grid-column:span 2;}
@media (max-width:520px){ .meta-grid.six .meta:nth-child(5):last-child{grid-column:auto;} }

/* a tall picture beside two stacked ones, so mixed shapes sit together
   without gaps; one caption under the set */
.bento{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.5fr); grid-template-rows:1fr 1fr; gap:16px; margin:0;}
.bento img{width:100%; height:100%; object-fit:cover; min-height:0;}
.bento img:first-child{grid-row:span 2;}
.bento figcaption{grid-column:1 / -1; font-size:12.5px; line-height:1.5; color:var(--ink-dim);}
@media (max-width:700px){ .bento{grid-template-columns:1fr; grid-template-rows:none;} .bento img:first-child{grid-row:auto;} }

/* "Earlier work" over the title on the older case studies */
.case-back + .case-earlier{margin:26px 0 0;}
.case-earlier + h1{margin-top:6px;}

/* ---------- case study hero video ----------
   Full width, muted, looping, over its poster: a video
   file (.hv video) or a YouTube film (.hv[data-yt]), each with the
   player's own controls. It starts muted when it comes into view, stops
   when it leaves, and never starts by itself under reduced motion or data
   saver (main.js). */
.hv{position:relative; aspect-ratio:16/9; overflow:hidden; background:#000;}
.case-media .hv > img, .hv video{position:absolute; inset:0; width:100%; height:100%; aspect-ratio:auto; object-fit:cover;}
.hv iframe{position:absolute; inset:0; width:100%; height:100%; border:0;}
@media (max-width:760px){ .hv.r43-small{aspect-ratio:4/3;} }

/* D1 on wider screens: four linked loops, numbered, handing on to each
   other, with the way out to the owning department and the dashed return
   to Scout. Drawn in the theme's ink so it follows day, night and colour.
   Phones keep the boxed loop (.cycle.sait-small), which reads at that size. */
.sait{display:block; width:75%; height:auto; font-family:var(--sans); overflow:visible;}
.sait .sait-line{fill:none; stroke:var(--ink-dim); stroke-width:1.6;}
.sait .sait-line.is-dash{stroke-dasharray:6 7;}
.sait .sait-head{fill:none; stroke:var(--ink-dim); stroke-width:1.6; stroke-linejoin:miter;}
.sait .sait-ico{fill:none; stroke:var(--ink-dim); stroke-width:1.5; stroke-linecap:square;}
.sait .sait-h{fill:var(--ink); font-size:26px; font-weight:500; letter-spacing:-.02em;}
.sait .sait-p{fill:var(--ink-dim); font-size:15px;}
.sait .sait-badge{fill:var(--ink);}
.sait .sait-n{fill:var(--bg); font-size:15px; font-weight:500;}
.sait .sait-box{fill:var(--ink);}
.sait .sait-boxt{fill:var(--bg); font-size:15px; font-weight:500;}
.sait .sait-tag{fill:var(--ink-dim); font-size:14px; font-weight:500;}
.cycle.sait-small{display:none;}
@media (max-width:760px){
  .sait{display:none;}
  .cycle.sait-small{display:grid;}
}

/* ============================================================
   Case study pages, distilled (1 October 2026, asked for).
   One column: each heading sits straight above its text, nothing sticks
   while scrolling, and pictures get the same air above and below. The
   header is the title, a one-line outcome and a compact row of facts in
   place of the six glass tiles. Stats are plain numbers with short labels.
   ============================================================ */
.case-hero h1{margin-bottom:0;}
.case-sum{font-size:clamp(20px,1.9vw,28px); line-height:1.35; letter-spacing:-.015em; margin:22px 0 0; text-wrap:pretty;}
.facts{display:grid; grid-template-columns:repeat(2,minmax(0,max-content)); gap:18px clamp(28px,3.5vw,56px); margin:clamp(32px,3.5vw,48px) 0 0;}
.facts div{min-width:0;}
.facts dt{font-size:12px; font-weight:500; color:var(--ink-dim); margin:0 0 6px;}
.facts dd{margin:0; font-size:14.5px; line-height:1.5;}
.case-hero .case-media{margin-top:clamp(40px,4.5vw,64px);}

.cs{padding-top:clamp(72px,7.5vw,128px);}
.cs-grid{display:block;}
.cs-head{position:static; margin:0 0 20px;}
.cs-head h2{font-size:clamp(26px,2.6vw,38px); line-height:1.08; text-wrap:balance;}
.cs-body h3{margin-top:36px;}
/* text that carries on after a picture or diagram gets the same gap back */
.cs .wrap > * + .cs-grid{margin-top:clamp(40px,4.5vw,64px);}
.cs-full, .cs-full.case-fig{margin-top:clamp(40px,4.5vw,64px);}

.stats{list-style:none; padding:0; margin:clamp(40px,4.5vw,64px) 0 0; display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr)); gap:28px clamp(24px,3vw,48px);}
.cs-body + .stats, .cs-grid + .stats{margin-top:clamp(32px,3.5vw,48px);}
.stats b{display:block; font-size:clamp(30px,3.2vw,46px); font-weight:500; letter-spacing:-.035em; line-height:1; font-variant-numeric:tabular-nums;}
.stats span{display:block; font-size:13.5px; line-height:1.45; color:var(--ink-dim); margin-top:10px;}

.outcome-list li{font-size:16.5px; line-height:1.7;}
.press-h{font-size:13.5px; font-weight:500; color:var(--ink-dim); margin:clamp(56px,6vw,88px) 0 16px;}
.press-h + .press{margin-top:0;}
.cs-tags{margin-top:clamp(40px,4.5vw,56px);}

/* Case study text width (1 October 2026, asked for): left aligned and
   full width, stopping 20% short of the right edge. No character caps.
   Pictures, diagrams, stats and quotes still run the full width; phones
   use the whole width. */
:root{--read:80%;}
.cs-grid, .outcome-list, .press-h, .cs-tags,
.case-sub, .case-prose, .case-ul-prose, .case-note{max-width:var(--read); margin-left:0; margin-right:0;}
.case-prose p{max-width:none;}
.case-sum{max-width:var(--read);}
.cs-body p, .case-prose p{font-size:18px;}
@media (max-width:900px){ :root{--read:100%;} }

/* ---------- side by side (1 October 2026) ----------
   .split: a section's text beside its pictures or stats, alternating
   sides with .flip; several short texts stack in .split-text. Both
   stack on phones, text first. */
.split{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:clamp(32px,4.5vw,80px); align-items:start;}
.cs .wrap > * + .split, .cs .wrap > .split + *{margin-top:clamp(40px,4.5vw,64px);}
.split > .cs-grid, .split-text > .cs-grid{max-width:none; margin:0;}
.split-text > .cs-grid + .cs-grid{margin-top:clamp(40px,4vw,56px);}
.split-media .dg-ctas{margin:0;}
.split.flip > .split-media{order:-1;}
.split-media{display:grid; gap:16px; min-width:0;}
.split-media > figure{margin:0;}
.split-media img{width:100%; height:auto;}
.split-media .pics{grid-template-columns:repeat(2,minmax(0,1fr));}
.split-media.stack .pics{grid-template-columns:1fr;}
.split-media.stack .pics img{aspect-ratio:auto;}
.split-media .stats{margin:0; grid-template-columns:1fr 1fr; row-gap:36px;}
@media (max-width:900px){
  .split{grid-template-columns:1fr;}
  .split.flip > .split-media{order:0;}
}
/* Home project tags and View case study button: one size and one fixed
   height, so the button sits in line with the tags (asked for) */
.proj-tags li, .proj-go{
  box-sizing:border-box; display:inline-flex; align-items:center;
  height:38px; padding:0 14px; font-size:13.5px; line-height:1;
}

/* Home, matched to the Figma frame (October 2026): ten and eleven column
   pictures, the eleven against the right edge, and the earlier projects
   sitting 48px apart. */
@media (min-width:761px){
  .span-10{grid-column:span 10;} .span-11{grid-column:span 11;}
  .end.span-11{grid-column:2 / 13;} .end.span-10{grid-column:3 / 13;}
}
.col-group.is-earlier > .proj-head ~ .proj-head{margin-top:24px;}

/* ---------- gallery: every picture and video for the project ----------
   Small thumbnails in a tight grid, all cropped to 4:3. Each opens the
   full file in the lightbox (main.js); videos carry a play mark. */
.gallery .case-sub{max-width:none;}
.thumbs{list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(auto-fill,minmax(200px,1fr)); gap:10px;}
.thumb{position:relative; display:block; aspect-ratio:4/3; overflow:hidden; background:var(--glass-soft);}
.thumb img{width:100%; height:100%; object-fit:cover; transition:transform .5s cubic-bezier(.2,.8,.2,1), opacity .3s ease;}
.thumb .play{position:absolute; left:50%; top:50%; width:34px; height:34px; transform:translate(-50%,-50%); background:rgba(11,11,12,.72); display:grid; place-items:center;}
.thumb .play::before{content:""; margin-left:3px; border-left:10px solid #fff; border-top:6px solid transparent; border-bottom:6px solid transparent;}
@media (max-width:620px){ .thumbs{grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px;} }

.glb{position:fixed; inset:0; z-index:200; background:rgba(8,8,9,.94); display:none; align-items:center; justify-content:center; padding:56px 64px;}
.glb.open{display:flex;}
.glb-stage{max-width:100%; max-height:100%; display:flex; align-items:center; justify-content:center;}
.glb-stage img, .glb-stage video{max-width:min(1600px,100%); max-height:calc(100vh - 112px); width:auto; height:auto; display:block;}
.glb-stage iframe{width:min(1400px,calc(100vw - 128px)); aspect-ratio:16/9; height:auto; max-height:calc(100vh - 112px); border:0;}
.glb button{position:absolute; background:rgba(255,255,255,.08); color:#fff; border:0; width:48px; height:48px; cursor:pointer; display:grid; place-items:center; font:inherit; font-size:22px; line-height:1;}
.glb button:hover{background:rgba(255,255,255,.16);}
.glb-x{top:12px; right:12px;}
.glb-prev{left:8px; top:50%; transform:translateY(-50%);}
.glb-next{right:8px; top:50%; transform:translateY(-50%);}
.glb-count{position:absolute; bottom:16px; left:50%; transform:translateX(-50%); color:rgba(255,255,255,.7); font-size:13px;}
html.glb-open{overflow:hidden;}
@media (max-width:620px){ .glb{padding:56px 8px;} .glb-prev, .glb-next{top:auto; bottom:8px; transform:none;} }

/* No zoom on hover, anywhere (asked for, October 2026): hovering a work
   tile only brings up its overlay caption. */
.col-item:hover .col-img img, .thumb:hover img{transform:scale(var(--zoom,1));}

/* Aston Martin outcome: a full-width band of large stats */
.stats.band{grid-template-columns:repeat(4,minmax(0,1fr)); gap:32px clamp(24px,3vw,56px); margin-top:clamp(48px,5vw,80px);}
.stats.band b{font-size:clamp(34px,3.9vw,64px); white-space:nowrap;}
.stats.band span{font-size:15px; margin-top:14px;}
.stats em{display:block; font-style:normal; font-size:17px; font-weight:500; letter-spacing:-.01em; color:var(--ink); margin-top:12px;}
.stats em + span{margin-top:6px;}
@media (max-width:900px){ .stats.band{grid-template-columns:1fr 1fr;} }
.press{align-items:stretch;}
.press blockquote{justify-content:space-between;}

/* HMI diagram: three drive modes */
.hmi-modes .hmi-row.three{grid-template-columns:repeat(3,minmax(0,1fr));}
.hmi-mode.sportplus{background:linear-gradient(120deg,#2a0b0b,#d0142c);}
.split-media.pair{grid-template-columns:1fr 1fr; align-items:start;}
/* HMI diagram, matched to the Figma frame (289:9931): drive mode tiles
   as tall as the control tiles beside them, icons in full ink */
.hmi-mode{height:87px;}
.hmi-ctrl{min-height:87px;}
.hmi-ctrl svg{stroke:var(--ink);}
/* a full-width film opening a section, then its text below */
.cs .app-film{margin-top:0;}
.cs-body p.cs-lede + p.cs-lede{margin-top:18px;}

/* ---------- press quotes (Aston Martin outcome) ----------
   Large pull quotes in the site's display face, the publication in small
   caps beneath, each linking to its review. A grid on desktop, stacked
   on phones. */
.press-quotes{list-style:none; margin:clamp(48px,5vw,80px) 0 0; padding:0; display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr)); gap:16px;}
.press-quotes li{display:flex;}
.pq{display:flex; flex-direction:column; justify-content:space-between; gap:28px; width:100%; padding:clamp(24px,2.4vw,36px); background:var(--glass); color:var(--ink); text-decoration:none; transition:background-color .25s ease;}
.pq:hover{background:var(--glass-strong);}
.pq blockquote{margin:0;}
.pq p{margin:0; font-family:var(--display); font-weight:var(--display-weight); font-size:calc(clamp(24px,2.3vw,34px) * var(--display-scale)); line-height:1.15; letter-spacing:-.01em; text-wrap:balance;}
.pq p::before{content:"\201C";} .pq p::after{content:"\201D";}
.pq cite{font-style:normal; font-size:15px; font-weight:500; font-variant-caps:all-small-caps; letter-spacing:.08em; color:var(--ink-dim);}
.pq:hover cite{color:var(--ink);}
@media (max-width:620px){ .press-quotes{grid-template-columns:1fr;} }
.nowrap{white-space:nowrap;}

/* About: the intro runs the reading width (80%, full on smaller screens)
   in one column, under a smaller title; My Values stacks one glass block
   per value, heading then text */
.about-hero h1{font-size:clamp(40px,6vw,88px);}
.about-grid{grid-template-columns:1fr;}
.about-grid .lead{max-width:var(--read);}
.value-list{display:grid; gap:16px;}
.value-item{padding:clamp(24px,3vw,44px);}
.value-item p{max-width:var(--read);}
.value-item h3{font-weight:500; font-size:clamp(22px,2vw,28px); letter-spacing:-.02em; line-height:1.15; margin:0 0 14px;}
.value-item p{font-size:16px; line-height:1.65; margin:0;}
.value-item p + p{margin-top:14px;}

/* Debenhams design system points: a 3 by 2 grid of glass cards, a figure
   on top, then a short heading and one plain paragraph; the flow diagram
   follows with room to breathe */
.ds-points{margin-bottom:clamp(40px,4.5vw,64px);}
.ds-points .value{padding:clamp(24px,2.4vw,34px);}
.ds-points .value h3{font-size:clamp(17px,1.4vw,20px);}
.ds-points .value p{font-size:15px; color:var(--ink);}
@media (min-width:821px){ .ds-points{grid-template-columns:repeat(2,1fr);} }
/* Debenhams test wins: five across, full width, under the full-width text */
.stats.band.five{grid-template-columns:repeat(5,minmax(0,1fr)); text-align:center;}
.stats.band.five b{font-size:clamp(32px,3.4vw,56px);}
@media (max-width:1100px){ .stats.band.five{grid-template-columns:repeat(3,minmax(0,1fr));} }
@media (max-width:620px){ .stats.band.five{grid-template-columns:1fr 1fr;} .stats.band.five li:last-child{grid-column:1 / -1;} }
