:root {
  --bg: #f7f7f5;
  --ink: #111111;
  --muted: #55595e;
  --faint: #8a8f95;
  --line: #e2e3e0;
  --green: #1ec28b;
  --green-deep: #0f4a3a;

  --mint: #bdebd6;
  --pink: #fbc9dc;
  --yellow: #fde98a;
  --lavender: #babfff;
  --coral: #ff6b5e;
  --blue: #3a6de0;
  --purple: #a690ee;
  --sky: #c4dcfb;

  --radius: 10px;
  --shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.35);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

a { color: inherit; }

.container {
  width: min(1560px, 100% - 48px);
  margin: 0 auto;
}

/* ---------- Header ---------- */

header { padding: 28px 0 0; }

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: var(--ink);
}

.wordmark svg {
  display: block;
  height: 32px;
  width: auto;
}

.nav {
  display: inline-flex;
  align-items: center;
  gap: 34px;
}

.nav a {
  color: var(--ink);
  text-decoration: none;
  font-size: 16px;
  font-weight: 500;
  padding: 6px 0;
  border-bottom: 2px solid transparent;
  transition: border-color 150ms ease;
}

.nav a:hover { border-bottom-color: var(--green); }

/* ---------- Buttons ---------- */

.btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 16px 26px;
  border-radius: 999px;
  border: 1.5px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 500;
  font-size: 16px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 120ms ease, background 150ms ease, color 150ms ease;
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: var(--ink);
  color: #fff;
}

.btn--primary:hover { background: #000; }

.btn .arrow { font-size: 18px; line-height: 0; }

/* ---------- Hero ---------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(32px, 5vw, 96px);
  padding: clamp(40px, 6vw, 80px) 0 clamp(40px, 5vw, 72px);
}

.eyebrow {
  margin: 0 0 22px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}

h1 {
  margin: 0 0 28px;
  font-size: clamp(48px, 5.5vw, 92px);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 0.96;
}

h1 .period {
  display: inline-block;
  width: 0.28em;
  height: 0.28em;
  border-radius: 50%;
  background: var(--green);
  margin-left: 0.08em;
  vertical-align: 0.02em;
}

.lede {
  margin: 0 0 40px;
  font-size: clamp(18px, 1.55vw, 24px);
  line-height: 1.5;
  color: var(--muted);
  max-width: 34ch;
}

/* The lede's em dash hides a note: hover (or focus) it and a small bubble rises above it. */
.ai-tip {
  position: relative;
  display: inline-block;
  padding: 0 0.1em;
  border-radius: 4px;
  outline: none;
  transition: color 150ms ease;
}
.ai-tip:hover,
.ai-tip:focus-visible { color: var(--green); }
.ai-tip:focus-visible { box-shadow: 0 0 0 2px var(--green); }

.ai-tip-bubble {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 14px);
  z-index: 5;
  width: max-content;
  max-width: min(320px, calc(100vw - 48px));
  padding: 16px 18px 17px;
  border-radius: var(--radius);
  background: #fff;
  color: var(--muted);
  box-shadow: 0 20px 44px -18px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.06);
  font-size: 14px;
  line-height: 1.5;
  text-align: left;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 6px);
  transition: opacity 180ms ease, transform 180ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.ai-tip-bubble::after {                               /* the tail, pointing at the dash */
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  background: #fff;
  border-radius: 0 0 3px 0;
  transform: rotate(45deg);
  box-shadow: 3px 3px 6px -3px rgba(0, 0, 0, 0.12);
}
.ai-tip-bubble b {
  display: block;
  margin-bottom: 8px;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.15;
  color: var(--ink);
}
/* The question mark's own dot is clipped away and the green one dropped in its place, as on the
   contact page's heading (see .qmark). The contact glyph's pixel nudge is for its big heading. */
.ai-tip-bubble b .qmark-glyph {
  transform: none;
  clip-path: inset(0 0 0.46em 0);                     /* the stem is cut a little higher to make room for the bigger dot */
}
.ai-tip-bubble b .qmark .period {
  position: absolute;
  width: 0.25em;                                      /* a size up on the glyph's own dot, to read at this scale */
  height: 0.25em;
  left: 0.173em;
  bottom: 0.1em;                                      /* on the baseline, a hair of air below the stem */
  border-radius: 50%;
  background: var(--green);
}
.ai-tip:hover .ai-tip-bubble,
.ai-tip:focus-visible .ai-tip-bubble {
  opacity: 1;
  transform: translate(-50%, 0);
  transition-delay: 120ms;                            /* brushing past the dash should not flash it */
}
@media (max-width: 720px) {
  .ai-tip-bubble { left: -8px; transform: translate(0, 6px); }
  .ai-tip-bubble::after { left: 14px; margin-left: 0; }
  .ai-tip:hover .ai-tip-bubble,
  .ai-tip:focus-visible .ai-tip-bubble { transform: translate(0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .ai-tip-bubble { transition: opacity 180ms ease; transform: translate(-50%, 0); }
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* ---------- Custom cursor (assets/cursor.js) ---------- */

/* While our arrow is showing, the native pointer is hidden everywhere */
html.cursor-custom,
html.cursor-custom * { cursor: none !important; }

.cursor {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2147483647;
  pointer-events: none;
  will-change: transform;
}
.cursor svg { display: block; overflow: visible; }
.cursor path {
  fill: #000;
  stroke: #fff;
  stroke-width: 1.6;
  stroke-linejoin: round;
  paint-order: stroke;                                  /* the outline sits outside the black, as on macOS */
}
.cursor-dot { display: block; border-radius: 50%; }
.cursor.is-hidden,
.cursor.is-away,
.cursor.is-eaten { visibility: hidden; }

@media (pointer: coarse) { .cursor { display: none; } }

/* ---------- Tile mosaic ---------- */

.mosaic {
  position: relative;
  justify-self: center;
  /* Square, so its width is its height: never wider than the column or 720px, and on short
     viewports no taller than what fits above the fold (header 79px + hero padding 80px above it,
     plus ~32px of breathing room below). Tall viewports stop at the 720px cap. */
  width: min(100%, 720px, calc(100vh - 192px));
  /* 3D scene: the tile grid is a flat plane in it, so a flying dot can pass in front of or behind it */
  perspective: 1200px;
  transform-style: preserve-3d;
}

.tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

/* Each cell is a cube viewed head-on. mosaic.js adds a second face on one side
   and turns the cube 90deg to reveal it, then the old face is discarded. */
.tile {
  position: relative;
  aspect-ratio: 1 / 1;
  perspective: 900px;
  transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);   /* finale: tiles slide together and apart */
}

.tile.is-turning { z-index: 2; }

.cube {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

.face {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.28), 0 1px 2px rgba(0, 0, 0, 0.05);
  display: grid;
  place-items: center;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.face > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.tile .circle {
  width: 58%;
  height: 58%;
  border-radius: 50%;
}

.t-mint { background: var(--mint); }
.t-pink { background: var(--pink); }
.t-yellow { background: var(--yellow); }
.t-lavender { background: var(--lavender); }
.t-sky { background: var(--sky); }
.t-grey { background: #eceeec; }
.t-mint2 { background: #c7efe0; }

.c-green { background: var(--green-deep); color: var(--green-deep); }
.c-coral { background: var(--coral); color: var(--coral); }
.c-blue { background: var(--blue); color: var(--blue); }
.c-purple { background: var(--purple); color: var(--purple); }

/* Extra accent shapes used by the bench tiles */

.tile .ring {
  width: 58%;
  height: 58%;
  border-radius: 50%;
  background: transparent;
  border: clamp(10px, 1.4vw, 22px) solid currentColor;
}

.tile .half {
  width: 58%;
  height: 29%;
  border-radius: 999px 999px 0 0;
}

.tile .diamond {
  width: 42%;
  height: 42%;
  border-radius: 14%;
  transform: rotate(45deg);
}

.tile .squircle {
  width: 52%;
  height: 52%;
  border-radius: 30%;
}

/* Mock devices used until real screenshots drop in (replace with <img>) */

.mock {
  position: absolute;
  background: #fff;
  box-shadow: 0 16px 30px -12px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.mock-laptop {
  left: 18%;
  right: -6%;
  top: 16%;
  bottom: 14%;
  border-radius: 8px 0 0 8px;
  border: 5px solid #1c1c1c;
  border-right: 0;
  padding: 16px 14px;
  font-size: clamp(10px, 1.1vw, 15px);
}

.mock-laptop .brand {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.55em;
  font-weight: 700;
  color: var(--green-deep);
  margin-bottom: 1.2em;
}

.mock-laptop .brand::before {
  content: "";
  width: 0.9em;
  height: 0.9em;
  border-radius: 3px;
  background: var(--green);
}

.mock-laptop h4 {
  margin: 0 0 0.35em;
  font-size: 1.05em;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--green-deep);
  font-weight: 800;
}

.mock-laptop p {
  margin: 0 0 0.8em;
  font-size: 0.55em;
  color: #6e7a75;
  line-height: 1.3;
}

.mock-laptop .btns {
  display: flex;
  gap: 0.4em;
}

.mock-laptop .btns i {
  display: block;
  width: 2.4em;
  height: 0.9em;
  border-radius: 999px;
  background: var(--green);
}

.mock-laptop .btns i + i {
  background: transparent;
  border: 1px solid #cfd8d3;
}

/* A real screenshot in the laptop: it fills the screen, pinned to the top-left corner so the
   site's own header and the start of the page show, and is cropped on the right and below. */
.mock-laptop--shot { padding: 0; }
.mock-laptop--shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 0 0;
}

/* Screenshot variants of the other frames: a browser window keeps its title bar and the shot
   fills the rest; the tablet drops its padding and fills its screen. */
.mock-browser--shot img {
  display: block;
  width: 100%;
  height: calc(100% - 1.7em);
  object-fit: cover;
  object-position: 0 0;
}
.mock-tablet.mock-tablet--shot { padding: 0; }   /* outranks .mock-tablet, which is declared later */
.mock-tablet--shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 0 0;
}

/* Landscape tablet across the middle of the tile, sitting back from the edges so the whole device
   shows. The screen keeps the screenshot's own aspect, so the whole page width is visible. */
.mock-tablet.mock-tablet--wide {
  left: 11%;
  right: 11%;
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
  aspect-ratio: 1000 / 564;
  /* iPad-ish: an even bezel all round (padding, so the camera can sit in it), rounded body,
     a faint edge highlight and a camera dot on the long edge */
  border: 0;
  padding: 0.2em;
  border-radius: 0.7em;
  background: #1c1c1e;
  box-shadow: 0 16px 30px -12px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(255, 255, 255, 0.09);
}
.mock-tablet--wide::before {
  content: "";
  position: absolute;
  top: 0.05em;
  left: 50%;
  width: 0.1em;
  height: 0.1em;
  margin-left: -0.05em;
  border-radius: 50%;
  background: #3a3a3c;
  box-shadow: inset 0 0 0 0.05em #0a0a0a;
}
.mock-tablet--wide img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 0.5em;
  object-fit: cover;
  object-position: 50% 50%;
}

/* Sequence variant, an 18s loop. The gift page shows, a second photo crossfades over it (the
   page's own slider moving on), then the checkout slides in from the right like a page change.
   The checkout is a stack: its gift-box state, its open calendar and then its details step fade
   in on top while it holds. On the way back it slides out to reveal the first gift page; the crossfaded layers all
   drop out while they are covered or off screen, so the loop joins without a jump. */
.mock-tablet--seq .screen {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 0.5em;
}
.mock-tablet--seq .shot,
.mock-tablet--seq .shot--next img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  border-radius: 0;
}
.mock-tablet--seq .shot--fade {
  opacity: 0;
  animation: tablet-fade-page 18s ease-in-out infinite;
}
.mock-tablet--seq .shot--next {
  transform: translateX(100%);
  box-shadow: -1em 0 2em rgba(0, 0, 0, 0.25);
  animation: tablet-next-page 18s ease-in-out infinite;
}
.mock-tablet--seq .fade-1 { opacity: 0; animation: tablet-fade-1 18s ease-in-out infinite; }
.mock-tablet--seq .fade-2 { opacity: 0; animation: tablet-fade-2 18s ease-in-out infinite; }
.mock-tablet--seq .fade-3 { opacity: 0; animation: tablet-fade-3 18s ease-in-out infinite; }
@keyframes tablet-fade-page {
  0%, 11.1%   { opacity: 0; }   /* gift page one, 2s */
  13.9%, 85%  { opacity: 1; }   /* fade over 0.5s, then the second photo */
  87%, 100%   { opacity: 0; }   /* drops out under the checkout */
}
@keyframes tablet-next-page {
  0%, 25%      { transform: translateX(100%); }  /* 4.5s on the gift pages */
  27.8%, 88.9% { transform: translateX(0); }     /* slides in over 0.5s, holds 11s */
  91.7%, 100%  { transform: translateX(100%); }  /* slides out, gift page one again */
}
@keyframes tablet-fade-1 {
  0%, 38.9%   { opacity: 0; }   /* checkout as it arrives, 2s */
  41.7%, 94%  { opacity: 1; }   /* gift box selected, 2s */
  96%, 100%   { opacity: 0; }   /* resets while off screen */
}
@keyframes tablet-fade-2 {
  0%, 52.8%   { opacity: 0; }
  55.6%, 94%  { opacity: 1; }   /* calendar open, 2.5s */
  96%, 100%   { opacity: 0; }
}
@keyframes tablet-fade-3 {
  0%, 69.4%   { opacity: 0; }
  72.2%, 94%  { opacity: 1; }   /* details step, 3s */
  96%, 100%   { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .mock-tablet--seq .shot--fade,
  .mock-tablet--seq .shot--next,
  .mock-tablet--seq .fade-1,
  .mock-tablet--seq .fade-2,
  .mock-tablet--seq .fade-3 { animation: none; }
}

/* Scrolling laptop, as the phone: a full-page capture scrolls behind the site's fixed masthead.
   Here the page's own transparent masthead is baked into the hero at the top, and the solid
   scrolled-state bar is layered on, fading in as the scroll begins, as the real site does. */
.mock-laptop--scroll { padding: 0; }
.mock-laptop--scroll .screen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  container-type: size;
}
.mock-laptop--scroll .page {
  display: block;
  width: 100%;
  height: auto;
  animation: phone-scroll 30s ease-in-out infinite alternate;
}
.mock-laptop--scroll .masthead {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  z-index: 1;
  opacity: 0;
  animation: laptop-masthead 30s ease-in-out infinite alternate;
}
@keyframes laptop-masthead {
  0%        { opacity: 0; }
  5%, 100%  { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .mock-laptop--scroll .page,
  .mock-laptop--scroll .masthead { animation: none; }
}

.mock-phone {
  left: 22%;
  right: 22%;
  top: 12%;
  bottom: -8%;
  border-radius: 28px 28px 0 0;
  background: #141414;
  border: 5px solid #262626;
  border-bottom: 0;
  color: #fff;
  padding: 18px 12px 0;
  font-size: clamp(10px, 1.1vw, 14px);
}

/* Screenshot variant: the shot fills the screen from the top and the notch floats over it */
.mock-phone--shot { padding: 0; }
.mock-phone--shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;
}
/* Scrolling variant: a full-page capture scrolls up and down behind the site's own fixed
   masthead, which is a separate capture layered on top. The screen is a size container so the
   scroll distance (page height minus screen height) can be written in CSS. */
.mock-phone--scroll { padding: 0; }
.mock-phone--scroll .screen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  container-type: size;
}
.mock-phone--scroll .page {
  display: block;
  width: 100%;
  height: auto;
  animation: phone-scroll 26s ease-in-out infinite alternate;
}
.mock-phone--scroll .masthead {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  z-index: 1;
}
@keyframes phone-scroll {
  from { transform: translateY(0); }
  to   { transform: translateY(calc(100cqh - 100%)); }
}
@media (prefers-reduced-motion: reduce) {
  .mock-phone--scroll .page { animation: none; }
}

.mock-phone--shot .notch,
.mock-phone--scroll .notch {
  position: absolute;
  top: 0.8em;
  left: 32%;
  right: 32%;
  margin: 0;
  z-index: 1;
}
.mock-phone--scroll .notch { display: none; }   /* it would sit over the site's own logo in the masthead */

.mock-phone .notch {
  width: 36%;
  height: 0.7em;
  border-radius: 999px;
  background: #000;
  margin: 0 auto 1.2em;
}

.mock-phone h4 {
  margin: 0 0 0.9em;
  font-size: 1.1em;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.mock-phone .stats {
  display: flex;
  justify-content: space-between;
  font-size: 0.6em;
  color: #9aa;
  margin-bottom: 0.8em;
}

.mock-phone .stats b { display: block; color: #fff; font-size: 1.25em; }

.mock-phone svg { width: 100%; height: auto; display: block; }

.mock-mug {
  position: absolute;
  left: 28%;
  right: 30%;
  top: 34%;
  bottom: 12%;
  background: #fff;
  border-radius: 8px 8px 22px 22px;
  box-shadow: 0 12px 22px -10px rgba(0, 0, 0, 0.35);
}

.mock-mug::before {
  content: "";
  position: absolute;
  right: -34%;
  top: 14%;
  width: 40%;
  height: 52%;
  border: 0.55em solid #fff;
  border-left: 0;
  border-radius: 0 999px 999px 0;
}

.mock-mug::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 46%;
  width: 26%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--green-deep);
  transform: translate(-50%, -50%);
}

/* Leaves: each sways a degree or so either side of its resting tilt, about its centre so it stays
   exactly where it was drawn, on its own period and phase so the two never move in step. */
.mock-leaf {
  position: absolute;
  left: 8%;
  top: 8%;
  width: 34%;
  height: 40%;
  background: #6fb98a;
  border-radius: 0 60% 0 60%;
  --tilt: -20deg;
  --sway: 1.5deg;
  transform: rotate(var(--tilt));
  opacity: 0.9;
  animation: leaf-sway 4.6s ease-in-out infinite alternate;
}

.mock-leaf + .mock-leaf {
  left: 14%;
  top: 22%;
  width: 26%;
  height: 32%;
  background: #4f9b6e;
  --tilt: 25deg;
  --sway: 1.2deg;
  animation-duration: 3.7s;
  animation-delay: -1.9s;
}

@keyframes leaf-sway {
  from { transform: rotate(calc(var(--tilt) - var(--sway))); }
  to   { transform: rotate(calc(var(--tilt) + var(--sway))); }
}
@media (prefers-reduced-motion: reduce) {
  .mock-leaf { animation: none; }
}

.mock-card {
  left: 22%;
  right: 8%;
  top: 18%;
  bottom: 10%;
  border-radius: 8px;
  padding: 1.2em 1em;
  font-size: clamp(10px, 1.1vw, 14px);
  text-align: left;
}

.mock-card .tag {
  display: inline-block;
  font-size: 0.6em;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: #fff;
  background: #1a7c4d;
  padding: 0.3em 0.6em;
  border-radius: 3px;
  margin-bottom: 1.4em;
}

.mock-card h4 {
  max-width: 72%;
  margin: 0 0 0.5em;
  font-size: 1.15em;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: #1c2b2a;
}

.mock-card p {
  max-width: 68%;
  margin: 0;
  font-size: 0.55em;
  color: #7a8583;
  line-height: 1.35;
}

.mock-card .bottle {
  position: absolute;
  right: 4%;
  bottom: -6%;
  width: 18%;
  height: 46%;
  background: linear-gradient(#e6f3ee, #cfe6dc);
  border-radius: 6px 6px 4px 4px;
}

.mock-card .bottle::before {
  content: "";
  position: absolute;
  left: 28%;
  top: -34%;
  width: 44%;
  height: 40%;
  background: #1a7c4d;
  border-radius: 3px 3px 0 0;
}

.mock-tablet {
  left: -8%;
  right: 12%;
  top: 14%;
  bottom: -8%;
  border-radius: 0 14px 0 0;
  border: 6px solid #1c1c1c;
  border-left: 0;
  border-bottom: 0;
  padding: 1.4em 1.2em 0 2.2em;
  font-size: clamp(10px, 1.1vw, 15px);
  background: #fff;
}

.mock-tablet h4 {
  max-width: 64%;
  margin: 0 0 0.4em;
  font-size: 1.15em;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: #1c2b2a;
}

.mock-tablet p {
  margin: 0 0 0.9em;
  font-size: 0.55em;
  color: #7a8583;
  line-height: 1.35;
  max-width: 62%;
}

.mock-tablet .btns i {
  display: inline-block;
  width: 2.4em;
  height: 0.9em;
  border-radius: 999px;
  background: var(--green);
}

.mock-tablet .bowl {
  position: absolute;
  right: -16%;
  top: 6%;
  width: 44%;
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle at 40% 40%, #f6d27a 0 18%, transparent 19%),
    radial-gradient(circle at 62% 58%, #e0684e 0 14%, transparent 15%),
    radial-gradient(circle at 48% 66%, #7fbf7a 0 16%, transparent 17%),
    radial-gradient(circle at 66% 36%, #c96b3d 0 12%, transparent 13%),
    #f1f1ee;
  box-shadow: inset 0 0 0 0.5em #fff, 0 8px 16px -8px rgba(0, 0, 0, 0.3);
}

/* Browser window (shop) */

.mock-browser {
  left: 12%;
  right: -10%;
  top: 18%;
  bottom: -6%;
  border-radius: 10px 0 0 0;
  font-size: clamp(10px, 1.1vw, 14px);
  text-align: left;
}

.mock-browser .bar {
  display: flex;
  align-items: center;
  gap: 0.35em;
  height: 1.7em;
  padding: 0 0.8em;
  background: #f1f1ee;
}

.mock-browser .bar i {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: #d6d6d0;
}

.mock-browser .body { padding: 1.1em 1.2em; }

.mock-browser h4 {
  margin: 0 0 0.4em;
  font-size: 1.05em;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: #3b2a5a;
}

.mock-browser p {
  max-width: 72%;
  margin: 0 0 0.9em;
  font-size: 0.55em;
  color: #7a7585;
  line-height: 1.3;
}

.mock-browser .balloons {
  display: flex;
  gap: 0.6em;
}

.mock-browser .balloons i {
  position: relative;
  width: 1.5em;
  height: 1.9em;
  border-radius: 50% 50% 50% 50% / 45% 45% 55% 55%;
  background: var(--coral);
}

.mock-browser .balloons i::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.9em;
  width: 1px;
  height: 0.9em;
  background: #c9c4d4;
}

.mock-browser .balloons i:nth-child(2) { background: var(--purple); }
.mock-browser .balloons i:nth-child(3) { background: var(--yellow); }

/* Phone (swipe-card app) */

.mock-phone--app h4 { margin-bottom: 0.7em; }

.mock-phone .deck {
  position: relative;
  height: 7.5em;
}

.mock-phone .deck i {
  position: absolute;
  inset: 0;
  border-radius: 0.8em;
  background: #fff;
  font-style: normal;
}

.mock-phone .deck i:nth-child(1) { transform: translateY(1.1em) scale(0.86); opacity: 0.4; }
.mock-phone .deck i:nth-child(2) { transform: translateY(0.55em) scale(0.93); opacity: 0.7; }

.mock-phone .deck i:nth-child(3) {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.15em;
  padding: 0.8em;
  background: linear-gradient(160deg, #fff 40%, var(--lavender));
  color: #1c1c2b;
}

.mock-phone .deck b {
  font-size: 0.55em;
  font-weight: 800;
  color: #5b48c7;
}

.mock-phone .deck span {
  font-size: 0.72em;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

/* Hosting dashboard card */

.mock-dash {
  left: 14%;
  right: 14%;
  top: 20%;
  bottom: -10%;
  border-radius: 10px 10px 0 0;
  padding: 1.1em 1.2em;
  font-size: clamp(10px, 1.1vw, 14px);
  text-align: left;
}

.mock-dash .status {
  display: flex;
  align-items: center;
  gap: 0.45em;
  margin-bottom: 1em;
  font-size: 0.6em;
  font-weight: 700;
  color: #1a7c4d;
}

.mock-dash .status::before {
  content: "";
  width: 0.7em;
  height: 0.7em;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 0.3em rgba(30, 194, 139, 0.25);
  animation: dash-pulse 2.4s ease-in-out infinite;   /* a slight heartbeat: the halo swells a touch */
}
@keyframes dash-pulse {
  0%, 100% { box-shadow: 0 0 0 0.3em rgba(30, 194, 139, 0.25); transform: scale(1); }
  50%      { box-shadow: 0 0 0 0.42em rgba(30, 194, 139, 0.18); transform: scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
  .mock-dash .status::before { animation: none; }
}

.mock-dash h4 {
  margin: 0 0 0.7em;
  font-size: 1.1em;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: #1c2b2a;
}

/* Bars: a live ticker. Two 12-month cycles sit side by side in an overflow-hidden strip and every
   bar jumps left one slot at a time (no tween: steps(12) over the 12-slot travel), so the pattern
   shifts along like new months arriving. After 12 jumps bar 13 sits where bar 1 began and the loop
   restarts invisibly. Heights repeat every 12 via nth-child(12n+k), kept in --h so the grow-in
   keyframe can end on each bar's own height.
   The bar arriving in the rightmost slot grows up from nothing: bar 12 is the newest at the start of
   each loop, bar 13 arrives on jump 1, bar 14 on jump 2, and so on, so each gets a grow animation
   delayed by that many jumps. Bar 24 is never on screen (its jump is the loop restart). */
.mock-dash .bars {
  --bar-gap: 0.25em;
  --loop: 16s;
  --jump: calc(var(--loop) / 12);
  display: flex;
  align-items: flex-end;
  height: 2.8em;
  overflow: hidden;
}

.mock-dash .bars i {
  --h: 100%;
  flex: 0 0 auto;
  width: calc((100% - 11 * var(--bar-gap)) / 12);
  margin-right: var(--bar-gap);
  height: var(--h);
  border-radius: 2px 2px 0 0;
  background: var(--green);
  animation: dash-bars var(--loop) steps(12, jump-end) infinite;
}

.mock-dash .bars i:nth-child(12n+2)  { --h: 78%; }
.mock-dash .bars i:nth-child(12n+4)  { --h: 88%; }
.mock-dash .bars i:nth-child(12n+5)  { --h: 70%; }
.mock-dash .bars i:nth-child(12n+7)  { --h: 92%; }
.mock-dash .bars i:nth-child(12n+9)  { --h: 64%; }
.mock-dash .bars i:nth-child(12n+10) { --h: 84%; }
.mock-dash .bars i:nth-child(12n+12) { --h: 76%; }

.mock-dash .bars i:nth-child(n+12):nth-child(-n+23) {
  animation:
    dash-bars var(--loop) steps(12, jump-end) infinite,
    dash-grow var(--loop) cubic-bezier(0.22, 1, 0.36, 1) infinite;
  animation-delay: 0s, calc(var(--arrive, 0) * var(--jump));
}
.mock-dash .bars i:nth-child(13) { --arrive: 1; }
.mock-dash .bars i:nth-child(14) { --arrive: 2; }
.mock-dash .bars i:nth-child(15) { --arrive: 3; }
.mock-dash .bars i:nth-child(16) { --arrive: 4; }
.mock-dash .bars i:nth-child(17) { --arrive: 5; }
.mock-dash .bars i:nth-child(18) { --arrive: 6; }
.mock-dash .bars i:nth-child(19) { --arrive: 7; }
.mock-dash .bars i:nth-child(20) { --arrive: 8; }
.mock-dash .bars i:nth-child(21) { --arrive: 9; }
.mock-dash .bars i:nth-child(22) { --arrive: 10; }
.mock-dash .bars i:nth-child(23) { --arrive: 11; }

@keyframes dash-bars {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-1200% - 12 * var(--bar-gap))); }   /* 1200% of a bar's own width = 12 bars */
}
@keyframes dash-grow {
  0%        { height: 0; }
  4%, 100%  { height: var(--h); }   /* 4% of the loop = about 0.64s of growth, then hold */
}
@media (prefers-reduced-motion: reduce) {
  .mock-dash .bars i { animation: none; }
}

.mock-dash small {
  display: block;
  margin-top: 0.7em;
  font-size: 0.5em;
  color: #7a8583;
}

/* Automation flow card */

.mock-flow {
  left: -6%;
  right: 12%;
  top: 20%;
  bottom: 20%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-radius: 0 10px 10px 0;
  padding: 1em 1.2em 1em 1.6em;
  font-size: clamp(10px, 1.1vw, 14px);
  text-align: left;
}

.mock-flow .node {
  display: flex;
  align-items: center;
  gap: 0.6em;
  font-size: 0.62em;
  font-weight: 700;
  color: #1c2b2a;
}

/* The green square marks the current step: it starts on "New order", then moves down to
   "Sync to CRM" and "Slack alert" before returning to the top. One keyframe, offset per node
   (nodes and lines are all divs, so the nodes are children 1, 3 and 5). */
.mock-flow .node i {
  width: 1.9em;
  height: 1.9em;
  border-radius: 0.55em;
  background: var(--lavender);
  animation: flow-step 6s ease-in-out infinite;
}
.mock-flow .node:nth-child(3) i { animation-delay: 2s; }
.mock-flow .node:nth-child(5) i { animation-delay: 4s; }

@keyframes flow-step {
  0%            { background: var(--lavender); }
  4%, 29%       { background: var(--green); }
  33.333%, 100% { background: var(--lavender); }
}
@media (prefers-reduced-motion: reduce) {
  .mock-flow .node i { animation: none; }
  .mock-flow .node:first-child i { background: var(--green); }
}

.mock-flow .line {
  width: 2px;
  height: 0.9em;
  margin: 0.15em 0 0.15em calc(0.62em * 0.95);
  background: #d9dcd8;
}

/* Ambient dots: each glides back and forth along its own short track (--gx/--gy from its home spot).
   They sit a hair behind the tile plane so the pink one tucks under the grid corner as before. */

/* The dots' group: the same box as the mosaic, so the dots' offsets are unchanged, and part of the
   3D scene. mosaic.js pushes the group far behind the plane while the finale's cube turns (scaled
   about the vanishing point, so the dots do not appear to move), keeping every dot behind the block. */
.floats {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  pointer-events: none;
}

.float {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  animation: glide var(--glide) ease-in-out infinite alternate;
  animation-delay: var(--phase);
}

@keyframes glide {
  from { transform: translate3d(0, 0, -1px); }
  to   { transform: translate3d(var(--gx), var(--gy), -1px); }
}

.float--pink {
  width: 50px;
  height: 50px;
  background: var(--pink);
  left: -30px;
  top: 18px;
  --gx: 22px; --gy: 76px; --glide: 9s; --phase: -3s;
}

.float--yellow {
  width: 52px;
  height: 52px;
  background: var(--yellow);
  left: -54px;
  top: 52%;
  --gx: -8px; --gy: -96px; --glide: 11s; --phase: -7s;
}

.float--blue {
  width: 52px;
  height: 52px;
  background: #8fb4ff;
  right: -78px;
  top: 52%;
  --gx: 14px; --gy: 84px; --glide: 10s; --phase: -2s;
}

.float--teal {
  width: 50px;
  height: 50px;
  background: #4fd7b2;
  right: -12px;
  bottom: 10%;
  --gx: -64px; --gy: 36px; --glide: 12s; --phase: -5s;
}

/* The flying dot: a tile's circle that has shrunk, lifted off and is travelling to another tile.
   mosaic.js positions it with translate3d, so it is depth-sorted against the tile plane. */
.fly {
  position: absolute;
  left: 0;
  top: 0;
  width: 50px;
  height: 50px;
  margin: -25px 0 0 -25px;
  border-radius: 50%;
  pointer-events: none;
  will-change: transform;
}

/* Finale: each tile shows one ninth of the logo on a light grey face. Inner corners are square so
   the slices meet cleanly once the gaps close; the grid's four outer corners keep their radius. */
.face--logo {
  border-radius: 0;
  background: #eceeec;
  box-shadow: none;                                   /* the slices meet edge to edge: no seams */
}

.tile:nth-child(1) .face--logo { border-top-left-radius: var(--radius); }
.tile:nth-child(3) .face--logo { border-top-right-radius: var(--radius); }
.tile:nth-child(7) .face--logo { border-bottom-left-radius: var(--radius); }
.tile:nth-child(9) .face--logo { border-bottom-right-radius: var(--radius); }

.face--logo svg,
.fly-logo svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* The assembled logo in flight, from the grid to the heading's full stop */
.fly-logo {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
  will-change: transform;
}

/* Finale: while the logo laps it, the closed grid is swapped for a solid cube with the same grey
   front face (mosaic.js builds and turns it); the real tiles sit hidden underneath. The sides are
   shaded as if lit from the top-left, in pairs that a half turn about the diagonal swaps. */
.grid-cube {
  position: absolute;
  left: 0;
  top: 0;
  transform-style: preserve-3d;
  pointer-events: none;
  will-change: transform;
}
.grid-cube > span {
  position: absolute;
  inset: 0;
  background: #eceeec;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.grid-cube > .gc-front,
.grid-cube > .gc-back { border-radius: var(--radius); }
.grid-cube > .gc-top,
.grid-cube > .gc-left { background: #e2e5e2; }
.grid-cube > .gc-right,
.grid-cube > .gc-bottom { background: #d4d7d4; }
/* Watermark: the Boost arrow on the sides, a shade darker than the face it sits on */
.grid-cube > span > svg {
  position: absolute;
  inset: 20%;
  width: 60%;
  height: 60%;
  color: rgba(0, 0, 0, 0.0375);
}

h1 .period {
  position: relative;
  transition: opacity 1.6s, transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}
h1 .period.is-hidden { opacity: 0; }

/* A full stop as an easter egg (assets/dot-cursor.js, on any dot carrying data-dot-egg): clickable
   until it has been clicked, when it hops up out of the way, splits off a new dot in its own colour
   that goes and eats the pointer, then drops back. */
.period.is-clickable { cursor: pointer; }
.period.is-raised {
  transform: translateY(-50px);
  transition: opacity 1.6s, transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.period.is-standing-in { visibility: hidden; }    /* a goo blob is drawing it for the moment */

/* The gooey split: two blobs under a blur + alpha-threshold filter read as one stretching body */
.goo {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2147483646;
  pointer-events: none;
}
.goo > span {
  position: absolute;
  border-radius: 50%;
  will-change: transform;
}

/* The split-off dot in flight, opening like a pacman to eat the pointer */
.pac {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2147483647;                                /* above the cursor: it is swallowing it */
  pointer-events: none;
  will-change: transform;
}
.pac svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* The way back: the shards the cursor dot bursts into (dot-cursor.js restore) */
.shards {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2147483646;                                /* just under the cursor: the arrow is breaking out through them */
  pointer-events: none;
}
.shards > span {
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 50%;
  will-change: transform, opacity;
}
.cursor-dot { will-change: transform; }                /* it throbs before it bursts */
h1 .period.is-landed {
  transition: none;                                   /* the logo lands on it: it is simply there */
}

/* The last word of the heading, so the landing dot can bump it (see mosaic.js landingNudge). */
h1 .nudge {
  display: inline-block;
  will-change: transform;
}

/* A second dot on top in the logo's green: fully on when the logo lands, then slowly fades away to
   reveal the heading's own green underneath. */
h1 .period::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #018366;
  opacity: 0;
  transition: opacity 4s ease-in-out;
}
h1 .period.is-logo-green::after {
  opacity: 1;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .float { animation: none; }
}

/* ---------- Tools strip ---------- */

.clients {
  padding: clamp(24px, 3vw, 40px) 0 30px;
}

.clients .eyebrow { margin-bottom: 22px; }

/* The strip is a marquee: two identical copies of the list sit side by side and the track slides
   left by exactly one copy's width (each copy carries the inter-logo gap as right padding, so the
   seam is just another gap), then starts over at a pixel-identical frame. Both edges fade out
   under a mask inside the container. */
.clients-marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
}

.clients-track {
  display: flex;
  width: max-content;
  animation: clients-marquee 60s linear infinite;
  will-change: transform;
}
.clients-marquee:hover .clients-track { animation-play-state: paused; }

@keyframes clients-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.clients-row {
  --gap: 56px;
  list-style: none;
  margin: 0;
  padding: 0 var(--gap) 0 0;
  display: flex;
  align-items: center;
  gap: var(--gap);
  flex: none;
}

/* Each logo sets its own height (--h) so the wordmarks carry similar visual weight. They sit in
   grey at rest and come up in their own colours on hover. */
.clients-row img {
  display: block;
  height: var(--h, 36px);
  width: auto;
  filter: grayscale(1);
  opacity: 0.7;
  transition: filter 0.3s, opacity 0.3s;
}
.clients-row li:hover img { filter: none; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .clients-track { animation: none; }
}

/* ---------- Testimonial ---------- */

/* Two columns, as the services head: the section's own heading and intro on the left, and the
   quote on the right. The quote's first sentence is set as a heading, ending on the brand's green
   full stop, with the rest of it in muted body text beneath, then the credit. */
.testimonial {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  align-items: start;
  gap: 40px clamp(40px, 6vw, 120px);
  padding: clamp(56px, 7vw, 110px) 0;
  border-bottom: 1px solid var(--line);
}

/* Carousel dots as an orbit. The ring is a circle of radius --r tilted back by --tilt and turned by
   --theta (a third of a turn per slide, so the current slide's dot is at the front, nearest the
   camera). Each dot undoes the ring's rotation so it always faces the camera, and scales up when it
   is at the front. --theta and --s are registered so they transition smoothly; browsers without
   @property simply snap. */
@property --theta { syntax: '<angle>'; inherits: true; initial-value: 0deg; }
@property --s     { syntax: '<number>'; inherits: false; initial-value: 0.3; }

.tm-orbit {
  --r: 90px;
  --tilt: 64deg;                 /* positive: the ring's front (the current dot) sits high and nearest */
  --spin: -35deg;                /* and the ring is turned in the screen plane so that front point is top-left */
  --dot: 112px;
  /* The ring's centre sits so the graphic's left edge is 20px in from the text's left edge below. */
  --cx: calc(var(--dot) * 0.5 + 70px);
  position: relative;
  height: 248px;
  margin: 0 0 24px;
  perspective: 700px;
  perspective-origin: var(--cx) 50%;
}

.tm-ring {
  --theta: 0deg;
  position: absolute;
  left: var(--cx);
  top: 56%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  transform: rotateZ(var(--spin)) rotateX(var(--tilt)) rotateY(var(--theta));
  transition: --theta 1s cubic-bezier(0.65, 0, 0.3, 1);
}

.tm-dot {
  --s: 0.3;
  --a: 0deg;
  position: absolute;
  left: 0;
  top: 0;
  width: var(--dot);
  height: var(--dot);
  margin: calc(var(--dot) / -2) 0 0 calc(var(--dot) / -2);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transform-style: preserve-3d;
  transform: rotateY(var(--a)) translateZ(var(--r)) rotateY(calc(-1 * (var(--a) + var(--theta)))) rotateX(calc(-1 * var(--tilt))) rotateZ(calc(-1 * var(--spin))) scale(var(--s));
  transition: --s 1s cubic-bezier(0.65, 0, 0.3, 1);
}
.tm-dot.is-front { --s: 1; cursor: default; }
.tm-dot:focus-visible { outline: none; }
.tm-dot:focus-visible .tm-dot-body { box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--green); }

/* The dot proper is a sphere: its silhouette is a circle whatever it does, so the disc itself stays
   facing the camera (it is the dot's billboard) and drifts gently along its own short track like
   the hero's ambient dots. It is flat colour, as the brand's shapes are: the mark sliding round
   its surface is what gives it its roundness. */
.tm-dot-body {
  --c: var(--mint);
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
  background: var(--c);
  perspective: calc(var(--dot) * 2.4);   /* for the mark, which sits on the sphere's surface */
  animation: glide var(--glide) ease-in-out infinite alternate;
  animation-delay: var(--phase);
}
.tm-dot--mint   .tm-dot-body { --c: var(--mint);   --gx: 6px;   --gy: -8px; --glide: 7s; --phase: -2s; }
.tm-dot--yellow .tm-dot-body { --c: var(--yellow); --gx: -22px; --gy: 14px; --glide: 9s; --phase: -5s; }
.tm-dot--pink   .tm-dot-body { --c: var(--pink);   --gx: 18px;  --gy: 20px; --glide: 8s; --phase: -1s; }

/* The quotation mark is painted on the sphere's front. The sphere turns with the ring (it keeps
   the mark facing outward from the orbit's centre), so the mark swings round to face the camera
   as its dot comes to the front, slides off round the side as it leaves, and is hidden on the
   far side: it is rotated by the dot's angle round the orbit and pushed out to the sphere's
   radius, with the disc clipping it and its back face culled. */
.tm-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 36%;
  aspect-ratio: 42 / 32;
  margin: -13.7% 0 0 -18%;      /* half the height (36% * 32/42) and half the width */
  fill: var(--green-deep);
  transform: rotateY(calc(var(--a) + var(--theta))) translateZ(calc(var(--dot) / 2));
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.tm-dot--yellow .tm-mark { fill: #6e5200; }
.tm-dot--pink   .tm-mark { fill: #8c2a55; }

@media (prefers-reduced-motion: reduce) {
  .tm-ring, .tm-dot { transition: none; }
  .tm-dot-body { animation: none; }
}

.tm-intro h2 {
  margin: 0;
  max-width: 30ch;
  font-size: clamp(17px, 1.3vw, 20px);
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.45;
  color: var(--muted);
  text-wrap: pretty;
}

.period--inline,
.tm-intro h2 .period,
.tm-lead .period {
  display: inline-block;
  width: 0.26em;
  height: 0.26em;
  border-radius: 50%;
  background: var(--green);
  margin-left: 0.06em;
  transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);   /* the easter egg's hop back down */
}

/* Each slide's full stop takes its orbit dot's colour at the hero full stop's intensity: the brand
   green itself for mint, a golden yellow, and a pink a shade softer than the work heading's. */
.tm--mint   .tm-lead .period { background: var(--green); }
.tm--yellow .tm-lead .period { background: #f7c52a; }
.tm--pink   .tm-lead .period { background: #ff8fb8; }

/* Arrows under the intro, as the reference: round hairline buttons that fill on hover. */
.tm-nav {
  display: flex;
  gap: 10px;
  margin-top: 28px;
}

.tm-arrow {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}
.tm-arrow:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.tm-arrow:focus-visible { outline: 2px solid var(--green); outline-offset: 3px; }
.tm-arrow .arrow { transition: transform 150ms ease; }
.tm-arrow[data-dir="1"]:hover .arrow { transform: translateX(3px); }
.tm-arrow[data-dir="-1"]:hover .arrow { transform: translateX(-3px); }

/* The slides share one grid cell, so the block is as tall as the tallest quote and never jumps.
   The active one fades up into place; the others sit hidden beneath it. */
.tm-slides {
  display: grid;
  padding-top: clamp(0px, 1vw, 10px);   /* sits a touch below the eyebrow line opposite */
}

.tm {
  grid-area: 1 / 1;
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1), visibility 0s linear 0.5s;
}
.tm.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}
@media (prefers-reduced-motion: reduce) {
  .tm { transition: none; }
}

.tm-text { margin: 0 0 32px; }

.tm-lead {
  margin: 0 0 22px;
  max-width: 19ch;               /* three lines at full size, as the mock */
  font-size: clamp(30px, 2.9vw, 46px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.04;
  text-wrap: balance;
}

.tm-rest {
  margin: 0;
  max-width: 42ch;
  font-size: clamp(17px, 1.35vw, 21px);
  line-height: 1.5;
  color: var(--muted);
}

/* Name on the left; an optional "View project" link sits bottom-right on the same line. */
.tm-by {
  margin: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
  max-width: 42ch;               /* right edge lines up with .tm-rest */
  font-size: clamp(17px, 1.35vw, 21px);   /* same ch unit as .tm-rest */
}

.tm-link {
  margin-left: auto;
  font-size: 14px;
  font-weight: 400;
  color: var(--muted);
  opacity: 0.75;
  transition: opacity 150ms ease, color 150ms ease;
}
.tm-link:hover,
.tm-link:focus-visible { opacity: 1; color: var(--ink); }

.tm-who {
  display: flex;
  flex-direction: column;
  gap: 2px;
  line-height: 1.3;
}

.tm-who b {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.tm-who span {
  font-size: 15px;
  color: var(--muted);
}

@media (max-width: 1080px) {
  .testimonial { grid-template-columns: 1fr; gap: 36px; }
  .tm-slides { padding-top: 0; }
  .tm-orbit { height: 200px; --r: 80px; --dot: 96px; }
  .tm-nav { margin-top: 24px; }
}

/* ---------- Services ---------- */

.services {
  padding: clamp(56px, 7vw, 110px) 0 clamp(40px, 5vw, 80px);
}

.services-head {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) auto;
  align-items: end;
  gap: 32px clamp(32px, 5vw, 80px);
  margin-bottom: clamp(40px, 5vw, 72px);
}

.services-head h2 {
  margin: 0;
  font-size: clamp(34px, 3.4vw, 54px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.02;
  text-wrap: balance;
}

.services-head h2 .period {
  display: inline-block;
  width: 0.26em;
  height: 0.26em;
  border-radius: 50%;
  background: var(--green);
  margin-left: 0.06em;
}

.services-head p {
  margin: 0;
  font-size: clamp(16px, 1.2vw, 19px);
  line-height: 1.55;
  color: var(--muted);
  max-width: 44ch;
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
  font-size: 15px;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

.link-arrow .arrow {
  transition: transform 150ms ease;
}

.link-arrow:hover .arrow { transform: translateX(4px); }

.services-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.service {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(28px, 2.8vw, 44px) clamp(20px, 2vw, 32px) clamp(28px, 2.8vw, 40px);
  border-left: 1px solid var(--line);
}

.service:first-child { border-left: 0; padding-left: 0; }
.service:last-child { padding-right: 0; }

.service .shape,
.contact-facts .shape {
  position: relative;
  width: 112px;
  height: 112px;
  margin-bottom: 28px;
  display: grid;
  place-items: center;
  transition: transform 260ms cubic-bezier(.2,.8,.2,1);
}

.service:hover .shape { transform: translateY(-4px) rotate(-3deg); }
.contact-facts .shape:hover { transform: translateY(-4px) rotate(-3deg); }

.service .shape::before,
.contact-facts .shape::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}

.service .shape svg,
.contact-facts .shape svg {
  position: relative;
  z-index: 1;
  width: 44px;
  height: 44px;
  color: var(--ink);
}

.shape--triangle::before {
  background: var(--mint);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  transform: rotate(-12deg);
}

.shape--square::before {
  background: var(--yellow);
  border-radius: 6px;
  inset: 10px;
  transform: rotate(10deg);
}

.shape--half::before {
  background: var(--pink);
  border-radius: 999px;
  inset: 0;
  clip-path: inset(0 0 50% 0);
  transform: translateY(25%);
}

.shape--hex::before {
  background: #a9c2ff;
  clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
  transform: rotate(14deg);
  inset: 4px;
}

.shape--half svg { transform: translateY(10px); }

.service h3 {
  margin: 0 0 10px;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.service p {
  margin: 0 0 24px;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--muted);
  max-width: 28ch;
}

.service .link-arrow { margin-top: auto; }

/* stretch the whole card as the click target */
.service .link-arrow::after {
  content: "";
  position: absolute;
  inset: 0;
}

@media (max-width: 1080px) {
  .services-head { grid-template-columns: 1fr 1fr; }
  .services-head .link-arrow { grid-column: 1 / -1; justify-self: start; }
  .services-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .service:nth-child(3) { border-left: 0; padding-left: 0; }
  .service:nth-child(2) { padding-right: 0; }
  .service:nth-child(-n + 2) { border-bottom: 1px solid var(--line); }
}

@media (max-width: 720px) {
  .services-head { grid-template-columns: 1fr; }
  .services-grid { grid-template-columns: 1fr; }
  .service { border-left: 0; padding-left: 0; padding-right: 0; }
  .service:not(:last-child) { border-bottom: 1px solid var(--line); }
  .service .shape { width: 96px; height: 96px; margin-bottom: 22px; }
  .service p { max-width: 40ch; }
}

/* ---------- Footer ---------- */

footer {
  padding: 22px 0 32px;
  color: var(--faint);
  font-size: 13px;
}

.foot {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  align-items: center;
  justify-content: space-between;
}

.foot a { color: var(--muted); text-decoration: none; }
.foot a:hover { color: var(--ink); }

/* ---------- Responsive ---------- */

@media (max-width: 1720px) {
  .float--blue { right: -24px; }
}

@media (max-width: 1080px) {
  .hero {
    grid-template-columns: 1fr;
    gap: 56px;
  }
  .mosaic { width: min(100%, 640px); justify-self: start; }
  .lede { max-width: 48ch; }
  .float--pink, .float--yellow { display: none; }
}

@media (max-width: 720px) {
  .container { width: min(1560px, 100% - 32px); }
  .nav { display: none; }
  .wordmark svg { height: 27px; }
  .btn { padding: 14px 20px; font-size: 15px; }
  h1 { font-size: clamp(44px, 12vw, 64px); }
  .tiles { gap: 8px; }
  .face { border-radius: 8px; }
  .float { display: none; }
  .clients-row { --gap: 36px; }
  .clients-row img { height: calc(var(--h, 36px) * 0.8); }
}

/* ---------- Shared bits for inner pages ---------- */

.chip {
  display: inline-block;
  padding: 5px 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.85);
  color: var(--ink);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 24px 48px;
  margin-bottom: clamp(32px, 4vw, 56px);
}

.section-head h2 {
  margin: 0;
  font-size: clamp(34px, 3.6vw, 60px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.0;
  text-wrap: balance;
}

.section-head h2 .period,
.page-head h1 .period {
  display: inline-block;
  width: 0.26em;
  height: 0.26em;
  border-radius: 50%;
  background: var(--green);
  margin-left: 0.06em;
}

.section-head h2 .period.period--pink { background: #ff6fa5; }

.section-head .aside {
  display: flex;
  align-items: center;
  gap: 28px;
}

.scribble {
  position: relative;
  font-family: "Caveat", "Segoe Script", cursive;
  font-size: 24px;
  font-weight: 500;
  line-height: 1.05;
  color: var(--ink);
  text-align: center;
  transform: rotate(-5deg);
  padding-right: 44px;
}

.scribble svg {
  position: absolute;
  right: 0;
  top: 100%;
  width: 46px;
  height: auto;
  transform: rotate(-10deg);
}

/* ---------- Work cards ---------- */

/* Our work sits between the clients strip and the testimonial: the extra 40px on top clears the
   strip's shallow bottom padding, and the hairline beneath hands over to the testimonial. */
.work {
  padding: calc(clamp(40px, 6vw, 96px) + 40px) 0 clamp(56px, 7vw, 110px);
  border-bottom: 1px solid var(--line);
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}

.work-card {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 46fr) minmax(0, 54fr);
  min-height: 380px;
  border-radius: 18px;
  overflow: hidden;
  background: var(--card-bg, #eee);
  transition: transform 260ms cubic-bezier(.2,.8,.2,1), box-shadow 260ms ease;
}

.work-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 30px 50px -30px rgba(0, 0, 0, 0.35);
}

.tone-mint { --card-bg: #d7f1e5; --card-deep: #bdebd6; }
.tone-pink { --card-bg: #fde0ea; --card-deep: #fbc9dc; }
.tone-yellow { --card-bg: #fdf0b4; --card-deep: #fde98a; }
.tone-lavender { --card-bg: #dfe1ff; --card-deep: #babfff; }
.tone-sky { --card-bg: #dbe9ff; --card-deep: #c2d7ff; }

.work-card-copy {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.4vw, 36px);
}

.work-card-copy .chip { align-self: flex-start; margin-bottom: 18px; }

.work-card h3 {
  margin: 0 0 12px;
  font-size: clamp(24px, 2vw, 30px);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.08;
}

.work-card p {
  margin: 0 0 24px;
  font-size: 16px;
  line-height: 1.5;
  color: #3f444a;
  max-width: 28ch;
}

.work-card .link-arrow { margin-top: auto; }

.work-card .link-arrow::after {
  content: "";
  position: absolute;
  inset: 0;
}

.work-card-visual {
  position: relative;
  min-height: 240px;
}

.work-card-visual .shot {
  position: absolute;
  left: 4%;
  top: 14%;
  width: 118%;
}

.work-card-visual .shot--phones {
  left: 12%;
  top: 10%;
  width: 70%;
}

.work-card-visual .shot--phone {
  left: 22%;
  top: 10%;
  width: 46%;
}

/* ---------- Device mock-ups ---------- */

.shot { --accent: #0f4a3a; font-size: 10px; }

.shot .shot-frame {
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 30px 50px -24px rgba(0, 0, 0, 0.4);
  overflow: hidden;
}

.shot--laptop .shot-frame {
  border: 0.9em solid #1c1c1c;
  border-bottom-width: 1.1em;
  border-radius: 1.4em 1.4em 0.4em 0.4em;
}

.shot--laptop .shot-base {
  height: 1.2em;
  margin: 0 -3%;
  background: linear-gradient(#2a2a2a, #111);
  border-radius: 0 0 1em 1em;
}

.shot-bar {
  display: flex;
  gap: 0.5em;
  padding: 0.9em 1.1em;
  background: #f1f1ef;
  border-bottom: 1px solid #e3e3e0;
}

.shot-bar i {
  width: 0.8em;
  height: 0.8em;
  border-radius: 50%;
  background: #d5d5d2;
}

.shot-page { padding: 1.2em 1.6em 1.6em; }

.shot-nav {
  display: flex;
  align-items: center;
  gap: 0.9em;
  margin-bottom: 1.8em;
}

.shot-nav i {
  width: 2.6em;
  height: 0.9em;
  border-radius: 3px;
  background: var(--accent);
}

.shot-nav span {
  width: 2em;
  height: 0.45em;
  border-radius: 999px;
  background: #d9d9d6;
}

.shot-nav b {
  margin-left: auto;
  width: 3.4em;
  height: 1.2em;
  border-radius: 999px;
  background: var(--accent);
}

.shot-hero {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 1.4em;
  align-items: center;
}

.shot-copy h4 {
  margin: 0 0 0.5em;
  font-size: 1.9em;
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.03em;
  color: var(--accent);
}

.shot-copy p {
  margin: 0 0 1em;
  font-size: 0.95em;
  line-height: 1.4;
  color: #6a6f6c;
}

.shot-btn {
  display: inline-block;
  padding: 0.55em 1.1em;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.85em;
  font-weight: 600;
  white-space: nowrap;
}

.shot-art {
  aspect-ratio: 4 / 3;
  border-radius: 0.8em;
  background:
    radial-gradient(circle at 70% 30%, rgba(255, 255, 255, 0.55) 0 22%, transparent 23%),
    linear-gradient(135deg, color-mix(in srgb, var(--accent) 22%, #fff), color-mix(in srgb, var(--accent) 55%, #fff));
}

.shot-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.9em;
  margin-top: 1.6em;
}

.shot-row span {
  height: 3.4em;
  border-radius: 0.6em;
  background: #f1f1ef;
}

.shot-page--shop .shot-row span { background: color-mix(in srgb, var(--accent) 10%, #fff); height: 4.4em; }
.shot-page--trade .shot-art { background: linear-gradient(160deg, #cfe3b8, #6f9a4e); }
.shot-page--clinic .shot-art { background: radial-gradient(circle at 50% 45%, #f4e8dc 0 40%, transparent 41%), linear-gradient(135deg, #d7f1e5, #9fd9bf); }

/* phones */

.shot--phone .shot-frame,
.shot--phones .shot-frame {
  border: 0.7em solid #1c1c1c;
  border-radius: 3em;
  background: #fff;
  aspect-ratio: 9 / 18.5;
}

.shot--phones {
  position: relative;
  aspect-ratio: 1 / 1;
}

.shot--phones .shot-frame {
  position: absolute;
  width: 48%;
  left: 36%;
  top: 0;
}

.shot--phones .shot-frame--back {
  left: 0;
  top: 12%;
  transform: rotate(-6deg);
  opacity: 0.95;
}

.shot-phone-screen {
  padding: 1.6em 1.1em 1.2em;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #fff;
}

.shot-status {
  width: 36%;
  height: 0.7em;
  border-radius: 999px;
  background: #1c1c1c;
  margin: 0 auto 1.4em;
}

.shot-phone-screen h4 {
  margin: 0 0 0.5em;
  font-size: 1.5em;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--accent);
}

.shot-phone-screen p {
  margin: 0 0 1em;
  font-size: 0.85em;
  line-height: 1.4;
  color: #6a6f6c;
}

.shot-cards {
  display: grid;
  gap: 0.6em;
  margin-bottom: 1.2em;
}

.shot-cards span {
  height: 3em;
  border-radius: 0.7em;
  background: color-mix(in srgb, var(--accent) 12%, #fff);
}

.shot-cards span:nth-child(2) { background: color-mix(in srgb, var(--accent) 30%, #fff); }

.shot-phone-screen .shot-btn { margin-top: auto; text-align: center; }

.shot-page--app .shot-cards span { height: 3.6em; }

/* larger sizes for project pages */

.shot.shot--lg { font-size: clamp(10px, 1.05vw, 15px); }
.shot.shot--md { font-size: clamp(9px, 0.8vw, 12px); }

/* A real capture in a device: the full-page screenshot scrolls behind the site's fixed header. Each page is a
   stack of layers: the capture, optionally the header in its top-of-page state (transparent, shown while the
   page sits at the top) and the solid scrolled-state bar, which takes over as the scroll begins. */
.shot--real .shot-screen {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  container-type: size;
  background: #fff;
}
.shot--real .shot-pg { position: absolute; inset: 0; }
.shot--real .shot-page-img {
  display: block;
  width: 100%;
  height: auto;
  animation: shot-scroll 30s linear infinite alternate;
}
@keyframes shot-scroll {
  0%, 4% { transform: translateY(0); animation-timing-function: ease-in-out; }
  100%   { transform: translateY(calc(100cqh - 100%)); }
}
.shot--real .shot-masthead,
.shot--real .shot-masthead-top {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  z-index: 1;
}
.shot--real .shot-masthead {
  opacity: 0;
  animation: shot-bar 30s linear infinite alternate;
}
.shot--real .shot-masthead-top {
  animation: shot-top 30s linear infinite alternate;
}
@keyframes shot-bar {
  0%, 4%    { opacity: 0; }
  6%, 100%  { opacity: 1; }
}
@keyframes shot-top {
  0%, 4%    { opacity: 1; }
  6%, 100%  { opacity: 0; }
}

/* Scrollable variant (the case-study hero): each page is a real scroll container run by assets/screens.js
   and open to the visitor's wheel, touch or vertical drag. The CSS animations above are switched off; the
   script marks the page in play with .shot-pg--active and .shot-pg--scrolled once it is away from its top. */
.shot--scrollable .shot-scroll {
  position: absolute;
  inset: 0;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-width: none;
  cursor: ns-resize;
}
.shot--scrollable .shot-scroll::-webkit-scrollbar { display: none; }
/* !important: the two-page keyframes above are set per layer with deeper selectors than this variant. */
.shot--scrollable .shot-page-img { animation: none !important; pointer-events: none; -webkit-user-drag: none; user-select: none; }
.shot--scrollable .shot-masthead,
.shot--scrollable .shot-masthead-top { animation: none !important; pointer-events: none; transition: opacity 0.4s linear; }
.shot--scrollable .shot-screen--two .shot-pg { animation: none !important; opacity: 0; transition: opacity 0.8s ease; pointer-events: none; }
.shot--scrollable .shot-screen--two .shot-pg--active { opacity: 1; pointer-events: auto; }
.shot--scrollable .shot-pg--scrolled .shot-masthead-top { opacity: 0; }
.shot--scrollable .shot-pg--scrolled .shot-masthead { opacity: 1; }

/* Two pages on one screen: the first scrolls down, cross-fades to the second sitting at its top, which
   scrolls down in turn, then fades back to the first at its top. One cycle is --cycle; each page gets a
   little under half of it to scroll. Each page's header goes from its top state to the solid bar as its
   scroll begins, and the bar drops out before the cross-fade. */
.shot--real .shot-screen--two { --cycle: 64s; }
/* Not the scrollable variant: there the script sets opacity through .shot-pg--active, and this rule's :nth-child
   would outrank it and keep the second page hidden. */
.shot--real:not(.shot--scrollable) .shot-screen--two .shot-pg:nth-child(2) { opacity: 0; }
.shot--real .shot-screen--two .shot-page-img,
.shot--real .shot-screen--two .shot-masthead,
.shot--real .shot-screen--two .shot-masthead-top {
  animation: none var(--cycle) linear infinite;
}
.shot--real .shot-screen--two .shot-pg:nth-child(1) .shot-page-img     { animation-name: two-page-a; }
.shot--real .shot-screen--two .shot-pg:nth-child(1) .shot-masthead-top { animation-name: two-top-a; }
.shot--real .shot-screen--two .shot-pg:nth-child(1) .shot-masthead     { animation-name: two-bar-a; }
.shot--real .shot-screen--two .shot-pg:nth-child(2) .shot-page-img     { animation-name: two-page-b; }
.shot--real .shot-screen--two .shot-pg:nth-child(2) .shot-masthead-top { animation-name: two-top-b; }
.shot--real .shot-screen--two .shot-pg:nth-child(2) .shot-masthead     { animation-name: two-bar-b; }
.shot--real .shot-screen--two .shot-pg:nth-child(2) { animation: two-pg-b var(--cycle) linear infinite; }

@keyframes two-page-a {
  0%, 2%  { transform: translateY(0); animation-timing-function: ease-in-out; }
  44%     { transform: translateY(calc(100cqh - 100%)); }
  50%     { transform: translateY(calc(100cqh - 100%)); }
  50.01%  { transform: translateY(0); }
  100%    { transform: translateY(0); }
}
@keyframes two-page-b {
  0%, 52% { transform: translateY(0); animation-timing-function: ease-in-out; }
  94%, 100% { transform: translateY(calc(100cqh - 100%)); }
}
/* Page B sits over A and fades in for the first cross-fade, out for the second. */
@keyframes two-pg-b {
  0%, 44%   { opacity: 0; }
  50%, 94%  { opacity: 1; }
  100%      { opacity: 0; }
}
/* Header states swap quickly as each scroll starts, as the live site does on its first scroll. */
@keyframes two-top-a {
  0%, 2%    { opacity: 1; }
  3%, 94%   { opacity: 0; }
  100%      { opacity: 1; }
}
@keyframes two-bar-a {
  0%, 2%    { opacity: 0; }
  3%, 42%   { opacity: 1; }
  45%, 100% { opacity: 0; }
}
@keyframes two-top-b {
  0%, 52%   { opacity: 1; }
  53%, 100% { opacity: 0; }
}
@keyframes two-bar-b {
  0%, 52%   { opacity: 0; }
  53%, 92%  { opacity: 1; }
  95%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .shot--real .shot-page-img,
  .shot--real .shot-masthead,
  .shot--real .shot-masthead-top,
  .shot--real .shot-screen--two .shot-pg:nth-child(2) { animation: none; }
  .shot--real .shot-screen--two .shot-pg:nth-child(2) { display: none; }
}

/* An open laptop in 3D (featured card). The lid leans back from a hinge at its bottom edge, the base is a
   plain slab lying flat from the same hinge towards the viewer, and the whole thing is turned a little and
   seen from slightly above. Lengths are in em so the device scales with .shot's font-size. */
.shot--3d {
  --pitch: -18deg;   /* camera looking down */
  --yaw: -16deg;     /* turned so the right side is nearer */
  --lean: 12deg;     /* lid leaning back past upright */
  --depth: 0.6;      /* base depth as a share of its width */
  perspective: 2600px;
  perspective-origin: 45% 30%;
}
.shot--3d .lap {
  position: relative;
  transform-style: preserve-3d;
  transform: rotateX(var(--pitch)) rotateY(var(--yaw));
}
.shot--3d .lap-lid {
  position: relative;
  transform-origin: 50% 100%;
  transform: rotateX(var(--lean));
}
.shot--3d .shot-frame {
  border-width: 1.1em 1.1em 1.6em;
  border-radius: 1.4em 1.4em 0.4em 0.4em;
  box-shadow: none;
}
.shot--3d .lap-base,
.shot--3d .lap-shadow {
  position: absolute;
  top: 100%;
  left: -2%;
  width: 104%;
  aspect-ratio: 1 / var(--depth);
  transform-origin: 50% 0;
}
.shot--3d .lap-base {
  transform: rotateX(90deg) translateZ(0.5em);
  background: linear-gradient(#3b3b40, #2c2c30);
  border-radius: 0.6em 0.6em 1.6em 1.6em;
}
.shot--3d .lap-base::before {   /* front lip: the slab's thickness */
  content: "";
  position: absolute;
  left: 0.6em;
  right: 0.6em;
  bottom: 0;
  height: 0.9em;
  transform-origin: 50% 100%;
  transform: rotateX(90deg);
  background: #202023;
  border-radius: 0 0 0.3em 0.3em;
}
.shot--3d .lap-keys {
  position: absolute;
  left: 11%;
  right: 11%;
  top: 9%;
  height: 46%;
  border-radius: 0.4em;
  background-color: #404045;
  background-image:
    repeating-linear-gradient(90deg, transparent 0 calc(100% / 15 - 1.5px), #313135 calc(100% / 15 - 1.5px) calc(100% / 15)),
    repeating-linear-gradient(0deg, transparent 0 calc(100% / 5 - 1.5px), #313135 calc(100% / 5 - 1.5px) calc(100% / 5));
}
.shot--3d .lap-pad {
  position: absolute;
  left: 36%;
  right: 36%;
  top: 62%;
  bottom: 9%;
  border-radius: 0.5em;
  background: #38383d;
  box-shadow: inset 0 0 0 1px #4a4a50;
}
.shot--3d .lap-shadow {
  left: -8%;
  width: 118%;
  aspect-ratio: 1 / calc(var(--depth) * 1.2);
  transform: rotateX(90deg) translateZ(-0.3em);
  background: radial-gradient(ellipse at 52% 46%, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0.2) 55%, rgba(0, 0, 0, 0) 74%);
  filter: blur(8px);
}
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .work-card-visual .shot.shot--3d { left: 17%; top: 8%; width: 64%; }
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .work-card-visual .shot.shot--browser.shot--real { left: 7%; top: 11%; width: 86%; font-size: 13px; }

/* ---------- Page head (listing) ---------- */

.page-head {
  padding: clamp(32px, 5vw, 72px) 0 clamp(28px, 3vw, 44px);
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 24px 64px;
  align-items: end;
}

.page-head h1 {
  margin: 0;
  font-size: clamp(40px, 5vw, 80px);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 0.98;
  text-wrap: balance;
}

.page-head .lede { margin: 0; max-width: 40ch; }

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 28px;
}

.filter {
  appearance: none;
  border: 1.5px solid var(--line);
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  padding: 9px 16px;
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}

.filter:hover { border-color: var(--ink); }

.filter[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}

.work-empty {
  padding: 48px 0;
  color: var(--muted);
}

/* ---------- Project page ---------- */

.project-head {
  padding: clamp(32px, 5vw, 72px) 0 clamp(28px, 3vw, 48px);
}

.project-head .eyebrow { margin-bottom: 18px; }

.project-head h1 {
  margin: 0 0 28px;
  max-width: 18ch;
  font-size: clamp(36px, 4.6vw, 72px);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 1.0;
  text-wrap: balance;
}

/* The case-study headline runs big, so its full stop is 25% smaller than the standard h1 dot. */
.project-head h1 .period {
  width: 0.21em;
  height: 0.21em;
}

.project-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 20px 32px;
  margin: 0;
  padding: 24px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.project-meta div { min-width: 0; }

.project-meta dt {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 6px;
}

.project-meta dd {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
}

.project-hero {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  background: var(--card-deep, #eee);
  aspect-ratio: 16 / 8;
  display: grid;
  place-items: center;
}

.project-hero .shot { width: 70%; }
.project-hero .shot--phones { width: 36%; }

/* Phone band under the hero: handsets stepping along one slot at a time, one of them scrolling a page of the
   site. An Embla carousel (assets/phones.js) in loop mode, so the visitor can drag it too. Two layouts share
   the band: `three` shows three phones across the panel, `two` puts the client's testimonial (or a paragraph
   of copy) beside a narrower band that shows the mover and the phone arriving next to it. The set is rendered twice so the loop always
   has more than a band's width of slides. Phone metrics are in em from --pw so a handset scales as one piece. */
/* A single testimonial on a case study, reusing the homepage card's type (.tm-*). On its own it is a panel;
   inside the two-phone band (.project-phones-copy) it is the copy column and the band's panel holds it. */
.project-quote {
  margin: 20px 0 0;
  padding: clamp(36px, 5vw, 72px) clamp(24px, 4vw, 64px);
  border-radius: 22px;
  background: var(--card-bg, #eee);
}
.project-quote .tm-text { margin-bottom: 28px; }
.project-quote .tm-lead { max-width: 22ch; }
.project-quote .tm-lead .period { background: var(--ink); }
.project-quote .tm-rest { max-width: 52ch; color: var(--ink); opacity: 0.72; }
.project-phones-copy.project-quote { margin: 0; padding: 0; border-radius: 0; background: none; position: relative; top: 40px; }   /* sits a little below centre, under the dots */
.project-phones-copy .tm-text { margin-bottom: 24px; }
.project-phones-copy .tm-lead { max-width: 18ch; font-size: clamp(26px, 2.5vw, 40px); }
.project-phones-copy .tm-rest { max-width: 46ch; font-size: clamp(16px, 1.2vw, 18px); }
.project-phones-copy .tm-by { font-size: clamp(16px, 1.2vw, 18px); }

.project-phones {
  margin-top: 20px;
  border-radius: 22px;
  overflow: hidden;
  background: var(--card-bg, #eee);
  padding: clamp(32px, 4.5vw, 64px) 0;
}

.project-phones--two {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  align-items: center;
  gap: clamp(24px, 4vw, 64px);
  padding-left: clamp(28px, 4.5vw, 72px);
}
.project-phones-copy h2 {
  margin: 0 0 14px;
  font-size: clamp(24px, 2.3vw, 36px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1.05;
  text-wrap: balance;
}
.project-phones-copy > p {   /* the plain-copy paragraph only; the testimonial's own type is .tm-* */
  margin: 0;
  max-width: 46ch;
  font-size: 16px;
  line-height: 1.6;
  color: var(--muted);
}
@media (max-width: 999px) {
  /* Stacked: the testimonial sits above the band with clear space between, and no offset. */
  .project-phones--two { grid-template-columns: 1fr; padding-left: 0; gap: 32px; }
  .project-phones .project-phones-copy { padding: 8px clamp(28px, 7vw, 48px) 0; }   /* outranks the quote's own padding reset */
  .project-phones-copy.project-quote { top: 0; }
  .project-phones-copy .tm-lead { max-width: 14ch; }
}

.pm {
  /* Narrow screens: one phone centred with its neighbours peeking in. From 1000px the phone width and gap
     come from the band's own width (cq units resolve where --pw and --gap are used, inside .pm): exactly
     three in view for `three`, two for `two`, the gap a little wider than the edge space so the next phone
     sits just outside. */
  --pw: clamp(170px, 48vw, 240px);
  --gap: calc(var(--pw) * 0.3);
  container-type: inline-size;
  overflow: hidden;
  cursor: grab;
  touch-action: pan-y pinch-zoom;   /* horizontal drags go to the carousel, vertical ones keep scrolling the page */
  user-select: none;
  -webkit-user-select: none;
}
@media (min-width: 1000px) {
  .pm[data-layout="three"] {
    --pw: min(300px, calc(100cqw / 4.68));
    --gap: calc((100cqw - 3 * var(--pw)) / 3.8);
  }
  .pm[data-layout="two"] {
    --pw: min(300px, calc(100cqw / 3.07));
    --gap: calc((100cqw - 2 * var(--pw)) / 2.8);
  }
}
.pm--grabbing { cursor: grabbing; }

.pm-track {
  display: flex;
  gap: var(--gap);
  backface-visibility: hidden;
}
.pm-phone:last-child { margin-right: var(--gap); }   /* Embla reads the loop's wrap-around gap from the last slide's end margin */
.pm-phone img { pointer-events: none; -webkit-user-drag: none; }

.pm-phone {
  flex: 0 0 auto;
  width: var(--pw);
  font-size: calc(var(--pw) / 26);
  aspect-ratio: 9 / 19.2;
  border: 0.55em solid #1c1c1c;
  border-radius: 3em;
  background: #000;
  box-shadow: 0 1.6em 3em -1.6em rgba(0, 0, 0, 0.55), inset 0 0 0 0.12em #3a3a3a;
  overflow: hidden;
}

.pm-screen {
  height: 100%;
  display: flex;
  flex-direction: column;
  border-radius: 2.45em;
  overflow: hidden;
  background: #fff;
}

/* iOS-style status strip above the capture. Its background follows the site's header: the hero tint while
   the page sits at the top, the solid bar colour once it scrolls, on the same timing as the masthead layers. */
.pm-status {
  position: relative;
  flex: 0 0 auto;
  height: 2.3em;
  padding: 0.55em 1.5em 0 1.6em;
  display: flex;
  align-items: center;
  gap: 0.3em;
  background: var(--s-top);
  color: #fff;
  font-size: 1em;
  line-height: 1;
}
.pm-status::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--s-bar);
  opacity: 0;
}
.pm-status > * { position: relative; }
.pm-status b { font-size: 0.78em; font-weight: 700; letter-spacing: -0.01em; margin-right: auto; }
.pm-sig {
  width: 0.95em;
  height: 0.62em;
  background:
    linear-gradient(#fff, #fff) 0 100% / 0.16em 0.28em no-repeat,
    linear-gradient(#fff, #fff) 0.26em 100% / 0.16em 0.4em no-repeat,
    linear-gradient(#fff, #fff) 0.52em 100% / 0.16em 0.52em no-repeat,
    linear-gradient(#fff, #fff) 0.78em 100% / 0.16em 0.62em no-repeat;
}
.pm-wifi {
  width: 0.9em;
  height: 0.9em;
  margin-top: 0.3em;
  border-radius: 50%;
  border: 0.17em solid transparent;
  border-top-color: #fff;
  box-sizing: border-box;
}
.pm-batt {
  width: 1.3em;
  height: 0.62em;
  border: 0.08em solid rgba(255, 255, 255, 0.55);
  border-radius: 0.22em;
  padding: 0.07em;
  background: linear-gradient(#fff, #fff) content-box;
  box-sizing: border-box;
}

/* The capture sits behind the site's fixed masthead in a real scroll container (assets/screens.js), so the
   visitor can scroll any phone with the wheel, a touch or a vertical mouse drag. One phone also scrolls by
   itself, run by assets/phones.js. The script sets .pm-phone--scrolled while a page is away from its top,
   which swaps the header from its top-of-page state to the solid bar. */
.pm-view {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  background: #fff;
}
.pm-scroll {
  position: absolute;
  inset: 0;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-width: none;
}
.pm-scroll::-webkit-scrollbar { display: none; }
.pm-page {
  display: block;
  width: 100%;
  height: auto;
}
.pm-top, .pm-bar {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  z-index: 1;
}
.pm-top, .pm-bar, .pm-status::before { transition: opacity 0.4s linear; }
.pm-bar { opacity: 0; }
.pm-phone--scrolled .pm-top { opacity: 0; }
.pm-phone--scrolled .pm-bar,
.pm-phone--scrolled .pm-status::before { opacity: 1; }

@media (max-width: 720px) {
  .project-phones { margin-top: 14px; border-radius: 16px; }
}


/* Shape polygons for the outcome cards (assets/story-dots.js morphs each dot's circle into one as it
   lands): all have the same point count so clip-path can tween between them. Built as the homepage's service
   shapes are: a triangle tilted a touch, a square turned a little, a half disc sitting low. */
:root {
  --shape-circle: polygon(50.00% 0.00%, 58.68% 0.76%, 67.10% 3.02%, 75.00% 6.70%, 82.14% 11.70%, 88.30% 17.86%, 93.30% 25.00%, 96.98% 32.90%, 99.24% 41.32%, 100.00% 50.00%, 99.24% 58.68%, 96.98% 67.10%, 93.30% 75.00%, 88.30% 82.14%, 82.14% 88.30%, 75.00% 93.30%, 67.10% 96.98%, 58.68% 99.24%, 50.00% 100.00%, 41.32% 99.24%, 32.90% 96.98%, 25.00% 93.30%, 17.86% 88.30%, 11.70% 82.14%, 6.70% 75.00%, 3.02% 67.10%, 0.76% 58.68%, 0.00% 50.00%, 0.76% 41.32%, 3.02% 32.90%, 6.70% 25.00%, 11.70% 17.86%, 17.86% 11.70%, 25.00% 6.70%, 32.90% 3.02%, 41.32% 0.76%);
  --shape-triangle: polygon(41.59% 10.41%, 46.29% 16.31%, 50.99% 22.21%, 55.69% 28.10%, 60.39% 34.00%, 65.09% 39.90%, 69.79% 45.79%, 74.49% 51.69%, 79.20% 57.59%, 83.90% 63.48%, 88.60% 69.38%, 93.30% 75.28%, 98.00% 81.17%, 91.40% 82.57%, 84.80% 83.98%, 78.21% 85.38%, 71.61% 86.78%, 65.01% 88.18%, 58.41% 89.59%, 51.82% 90.99%, 45.22% 92.39%, 38.62% 93.79%, 32.02% 95.20%, 25.43% 96.60%, 18.83% 98.00%, 20.72% 90.70%, 22.62% 83.40%, 24.52% 76.10%, 26.41% 68.80%, 28.31% 61.51%, 30.21% 54.21%, 32.10% 46.91%, 34.00% 39.61%, 35.90% 32.31%, 37.79% 25.01%, 39.69% 17.71%);
  --shape-square: polygon(17.55% 3.66%, 26.31% 5.21%, 35.06% 6.75%, 43.82% 8.29%, 52.57% 9.84%, 61.32% 11.38%, 70.08% 12.92%, 78.83% 14.47%, 87.58% 16.01%, 96.34% 17.55%, 94.79% 26.31%, 93.25% 35.06%, 91.71% 43.82%, 90.16% 52.57%, 88.62% 61.32%, 87.08% 70.08%, 85.53% 78.83%, 83.99% 87.58%, 82.45% 96.34%, 73.69% 94.79%, 64.94% 93.25%, 56.18% 91.71%, 47.43% 90.16%, 38.68% 88.62%, 29.92% 87.08%, 21.17% 85.53%, 12.42% 83.99%, 3.66% 82.45%, 5.21% 73.69%, 6.75% 64.94%, 8.29% 56.18%, 9.84% 47.43%, 11.38% 38.68%, 12.92% 29.92%, 14.47% 21.17%, 16.01% 12.42%);
  --shape-half: polygon(2.00% 70.00%, 2.73% 61.66%, 4.89% 53.58%, 8.43% 46.00%, 13.23% 39.15%, 19.15% 33.23%, 26.00% 28.43%, 33.58% 24.89%, 41.66% 22.73%, 50.00% 22.00%, 58.34% 22.73%, 66.42% 24.89%, 74.00% 28.43%, 80.85% 33.23%, 86.77% 39.15%, 91.57% 46.00%, 95.11% 53.58%, 97.27% 61.66%, 98.00% 70.00%, 92.67% 70.00%, 87.33% 70.00%, 82.00% 70.00%, 76.67% 70.00%, 71.33% 70.00%, 66.00% 70.00%, 60.67% 70.00%, 55.33% 70.00%, 50.00% 70.00%, 44.67% 70.00%, 39.33% 70.00%, 34.00% 70.00%, 28.67% 70.00%, 23.33% 70.00%, 18.00% 70.00%, 12.67% 70.00%, 7.33% 70.00%);
}

/* Three dots travelling down the case study (assets/story-dots.js). The layer is painted above the panels'
   fills but beneath their content: each panel moves its fill into a ::before at a negative z-index and lifts
   its content above the layer, so the dots read as background shapes inside the laptop and phone panels
   and as loose marks beside the heading. */
main.has-dots { position: relative; }
.story-dots {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.story-dots--on { opacity: 1; }
.story-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 80px;
  height: 80px;
  will-change: transform;
}
/* The colour sits on an inner core that drifts along a short track while the dot is parked, as the
   homepage floats do (the glide keyframes above). The outer element carries the position and the clip. */
.story-dot-core {
  display: block;
  width: 100%;
  height: 100%;
  clip-path: var(--shape-circle);   /* the circle too, so a shape's corners are not cut by a border radius */
  transition: clip-path 1.1s cubic-bezier(0.65, 0, 0.35, 1);   /* the shape morphs over a station move */
  animation: glide var(--glide) ease-in-out infinite alternate;
  animation-delay: var(--phase);
}
/* At the outcome cards each dot takes a shape, as the homepage's service icons do, and its icon is drawn
   in ink in that same thin line. */
.story-dots[data-station="stats"] .story-dot--green  .story-dot-core { clip-path: var(--shape-triangle); }
.story-dots[data-station="stats"] .story-dot--yellow .story-dot-core { clip-path: var(--shape-square); }
.story-dots[data-station="stats"] .story-dot--pink   .story-dot-core { clip-path: var(--shape-half); }
.story-dot--green  .story-dot-core { background: var(--green);  --gx: 18px;  --gy: 34px;  --glide: 11s; --phase: -3s; }
.story-dot--yellow .story-dot-core { background: var(--yellow); --gx: -14px; --gy: 26px;  --glide: 9s;  --phase: -6s; }
.story-dot--pink   .story-dot-core { background: var(--pink);   --gx: 12px;  --gy: -22px; --glide: 10s; --phase: -2s; }
/* At the testimonial the green dot is a quote badge: the mark fades in once the dots have arrived, tilted
   a touch, and the green comes to the front so the other two peek out from behind it. */
.story-dot-core { position: relative; }
/* The mark sits on the sphere's surface, as the homepage's does: pushed out to the dot's radius and turned
   round the vertical axis. It waits on the far side, hidden by its backface, and swings round to the front
   during the flight to the testimonial, landing face-on as the dot does, then swings away again on leaving. */
/* Radius and perspective are fixed rather than tied to the live size: a value that changed as the dot grew
   would restart the transform transition every frame and stall the swing. A mark starts out on the far side
   of the sphere but lands flat on the surface (no depth, no magnification) and carries any tilt inside the
   SVG, so at rest it is drawn at its true size and stays crisp. The quote mark shows at the testimonial;
   each dot's card icon shows at the outcome cards. */
.story-dot-core { overflow: hidden; perspective: 182px; }
.story-dot-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  overflow: visible;            /* a tilt can carry a shape past the SVG's box, which clips by default */
  transform: rotateY(180deg) translateZ(38px);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transition: transform 1.1s cubic-bezier(0.65, 0, 0.35, 1);   /* the length of a station move, so the swing runs with the flight */
}
.story-dot-mark--quote {   /* the homepage testimonials' mark (#quote-mark), sized from the dot as .tm-mark is */
  width: 56%;
  aspect-ratio: 42 / 32;
  margin: -21.3% 0 0 -28%;      /* half the height (56% * 32/42) and half the width */
  fill: #ebf8f2;   /* the panel's colour lifted halfway to white, so the mark reads as cut from the dot but sits a touch lighter */
  fill: color-mix(in srgb, var(--card-bg, #d7f1e5) 50%, #fff);
}
.story-dot-mark--card {   /* thin ink line icons, as the homepage service icons */
  width: 46%;
  aspect-ratio: 1;
  margin: -23% 0 0 -23%;
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.story-dot--green .story-dot-mark--card { margin-top: -17%; }   /* a little low in the triangle, where the room is */
.story-dot--pink  .story-dot-mark--card { margin-top: -17%; }   /* and in the half disc, as on the homepage */
.story-dots[data-station="phones"] .story-dot-mark--quote,
.story-dots[data-station="stats"] .story-dot-mark--card { transform: rotateY(0deg) translateZ(0px); }
.story-dots[data-station="phones"] .story-dot--green { z-index: 2; }
/* The badge holds still: the fan reads as one mark, so no drift at this station. The core eases back to
   its resting spot rather than snapping when the drift is switched off. */
.story-dots[data-station="phones"] .story-dot-core,
.story-dots[data-station="stats"] .story-dot-core { animation: none; transition: transform 0.9s ease; transform: translate3d(0, 0, -1px); }
@media (prefers-reduced-motion: reduce) {
  .story-dot-core { animation: none; }
}

/* Everything the dots should pass behind is lifted above the layer. The body copy is lifted piece by piece
   so the outcome cards can stay down with the panels: each card's white fill moves into a ::before beneath
   the layer and its figures sit above it, so a dot can land inside the card behind the number. */
.has-dots .project-head,
.has-dots .gallery,
.has-dots .quote,
.has-dots .project-next,
.has-dots .cta,
.has-dots .prose > section > :not(.stats),
.has-dots .facts > *,
.has-dots .stat > * { position: relative; z-index: 1; }
.has-dots .stat { position: relative; background: transparent; border-color: transparent; }
/* The card's figures start at the left and are budged over as the dot lands, overshooting a touch and
   settling, as the homepage heading's last word does when its dot arrives. The delay lets the dot nearly
   land first; later cards follow the dots' stagger. Leaving the station slides them straight back. */
.has-dots .stat > * { transition: transform 0.5s ease; }
.story-dots[data-station="stats"] ~ article .stat > * {
  transform: translateX(var(--shunt, 72px));
  transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.8s;
}
.story-dots[data-station="stats"] ~ article .stat:nth-child(2) > * { transition-delay: 0.89s; }
.story-dots[data-station="stats"] ~ article .stat:nth-child(3) > * { transition-delay: 0.98s; }
.has-dots .stat span { display: inline-block; white-space: nowrap; }   /* block so it can be moved; no wrapping when budged, or the row would grow */
@media (max-width: 999px) {
  /* The travelling dots are laid out for the desktop stations: off on narrow screens, and the outcome
     cards' figures stay put. */
  .story-dots { display: none; }
  .story-dots[data-station="stats"] ~ article .stat > * { transform: none; transition: none; }
}
.has-dots .stat::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  background: #fff;
  border: 1px solid var(--line);
}
.has-dots .project-hero,
.has-dots .project-phones { position: relative; background: transparent; }
.has-dots .project-hero::before,
.has-dots .project-phones::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
}
.has-dots .project-hero::before { background: var(--card-deep, #eee); }
.has-dots .project-phones::before { background: var(--card-bg, #eee); }
.has-dots .project-hero > *,
.has-dots .project-phones > * { position: relative; z-index: 1; }

.project-body {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
  gap: clamp(32px, 5vw, 96px);
  padding: clamp(40px, 6vw, 88px) 0;
}

.prose h2 {
  margin: 0 0 16px;
  font-size: clamp(26px, 2.4vw, 36px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1.05;
}

.prose h2 + p { font-size: clamp(17px, 1.3vw, 20px); }

.prose p {
  margin: 0 0 20px;
  font-size: 17px;
  line-height: 1.6;
  color: #3f444a;
}

.prose section + section { margin-top: clamp(36px, 4vw, 56px); }

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 18px;
}

.steps li {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 16px;
  align-items: start;
}

.steps .num {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--card-deep, #eee);
  font-weight: 700;
  font-size: 15px;
}

.steps h3 {
  margin: 8px 0 6px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.steps p { margin: 0; font-size: 16px; }

.facts {
  position: sticky;
  top: 24px;
  align-self: start;
  display: grid;
  gap: 28px;
}

.facts h3 {
  margin: 0 0 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}

.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.pill {
  display: inline-block;
  text-decoration: none;
  color: var(--ink);
  padding: 7px 12px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--line);
  font-size: 13px;
  font-weight: 500;
}

.facts ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
}

.facts li {
  padding-left: 18px;
  position: relative;
  font-size: 15px;
  color: #3f444a;
}

.facts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
}

.stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}

.stat {
  padding: 28px 24px;
  border-radius: 16px;
  background: #fff;
  border: 1px solid var(--line);
}

.stat b {
  display: block;
  font-size: clamp(32px, 3vw, 44px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1;
  margin-bottom: 8px;
}

.stat span { font-size: 14px; color: var(--muted); }

.gallery {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  padding-bottom: clamp(40px, 6vw, 88px);
}

.gallery figure {
  margin: 0;
}

.gallery .panel {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  background: var(--card-bg, #eee);
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
}

.gallery .panel .shot { width: 78%; }
.gallery .panel .shot--phone { width: 34%; }
.gallery .panel .shot--phones { width: 46%; }

.gallery figcaption {
  margin-top: 12px;
  font-size: 14px;
  color: var(--muted);
}

.quote {
  margin: 0;
  padding: clamp(32px, 4vw, 56px);
  border-radius: 22px;
  background: var(--ink);
  color: #fff;
}

.quote p {
  margin: 0 0 20px;
  font-size: clamp(22px, 2.2vw, 32px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.3;
  max-width: 30ch;
}

.quote footer {
  padding: 0;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
}

.quote footer b { color: #fff; font-weight: 600; }

.project-next {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  padding: clamp(40px, 6vw, 88px) 0;
}

.next-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 220px;
  padding: 28px;
  border-radius: 18px;
  background: var(--card-bg, #eee);
  color: var(--ink);
  text-decoration: none;
  transition: transform 260ms cubic-bezier(.2,.8,.2,1);
}

.next-card:hover { transform: translateY(-4px); }

.next-card .eyebrow { margin: 0 0 12px; }

.next-card h3 {
  margin: 0;
  font-size: clamp(24px, 2.2vw, 32px);
  font-weight: 800;
  letter-spacing: -0.03em;
}

.next-card .link-arrow { margin-top: 24px; }

.cta {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 24px 48px;
  align-items: center;
  padding: clamp(28px, 3.6vw, 48px);
  border-radius: 22px;
  background: #fff;
  border: 1px solid var(--line);
  margin-bottom: clamp(40px, 6vw, 88px);
}

.cta h2 {
  margin: 0 0 8px;
  font-size: clamp(26px, 2.6vw, 38px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1.05;
}

.cta p { margin: 0; color: var(--muted); font-size: 16px; }

.breadcrumb {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--muted);
  text-decoration: none;
}

.breadcrumb:hover { color: var(--ink); }

/* ---------- Responsive additions ---------- */

@media (max-width: 1080px) {
  .section-head { grid-template-columns: 1fr; }
  .section-head .aside { justify-content: flex-start; }
  .work-grid { grid-template-columns: 1fr; }
  .page-head { grid-template-columns: 1fr; }
  .project-body { grid-template-columns: 1fr; }
  .facts { position: static; }
}

@media (max-width: 720px) {
  .work-card { grid-template-columns: 1fr; min-height: 0; }
  .work-card-visual { min-height: 220px; }
  .work-card-visual .shot { left: 8%; top: 10%; width: 110%; }
  .work-card-visual .shot--phones { left: 20%; width: 64%; }
  .scribble { display: none; }
  .stats { grid-template-columns: 1fr; }
  .gallery { grid-template-columns: 1fr; }
  .project-next { grid-template-columns: 1fr; }
  .cta { grid-template-columns: 1fr; }
  .project-hero { aspect-ratio: 4 / 3; }
  .project-hero .shot { width: 86%; }
  .project-hero .shot--phones { width: 60%; }
}

.work-card[hidden] { display: none; }

/* ---------- Featured work layout (homepage): one project across the top, three beneath ---------- */

.work-grid--feature { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.work-grid--feature .work-card {
  grid-template-columns: minmax(0, 52fr) minmax(0, 48fr);
  min-height: 400px;
}
.work-grid--feature .work-card h3 { font-size: clamp(22px, 1.7vw, 27px); }
.work-grid--feature .work-card p { font-size: 15px; max-width: 24ch; }
.work-grid--feature .work-card-visual .shot { left: 6%; top: 16%; width: 140%; }
.work-grid--feature .work-card-visual .shot--phones { left: 16%; top: 10%; width: 86%; }

/* The /work/ listing uses the featured proportions for every card (.work-grid--wide), one per row. */
.work-grid--wide { grid-template-columns: 1fr; gap: 24px; margin-bottom: clamp(40px, 6vw, 88px); }   /* clear of the CTA below */

/* Cards sit at 80% width and the active one grows to the full width. The card's height is fixed by its
   min-height, so the grow does not move the cards below it. */
.work-grid--wide .work-card {
  width: 80%;
  margin-inline: auto;
  opacity: 0.75;
  filter: saturate(0.85);
  transition: width 600ms cubic-bezier(.2,.8,.2,1), opacity 600ms ease, filter 600ms ease,
              transform 260ms cubic-bezier(.2,.8,.2,1), box-shadow 260ms ease;
}
.work-grid--wide .work-card.is-active { width: 100%; opacity: 1; filter: none; }

:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) {
  grid-column: 1 / -1;
  grid-template-columns: minmax(0, 38fr) minmax(0, 62fr);
  min-height: 520px;
}
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .work-card-copy { padding: clamp(32px, 3.4vw, 56px); }
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .chip { margin-bottom: 26px; }
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) h3 { font-size: clamp(32px, 3.4vw, 50px); margin-bottom: 18px; }
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) p { font-size: clamp(17px, 1.3vw, 20px); max-width: 26ch; }
:is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .work-card-visual .shot { left: 7%; top: 11%; width: 86%; }

/* On the listing only one card is "active" at a time (hovered, else nearest the middle of the viewport;
   work-list.js sets .is-active). Only that card's capture scrolls; the rest hold where they are. */
.work-grid--wide .shot--real .shot-page-img,
.work-grid--wide .shot--real .shot-masthead,
.work-grid--wide .shot--real .shot-masthead-top,
.work-grid--wide .shot--real .shot-screen--two .shot-pg:nth-child(2) { animation-play-state: paused; }
.work-grid--wide .work-card.is-active .shot--real .shot-page-img,
.work-grid--wide .work-card.is-active .shot--real .shot-masthead,
.work-grid--wide .work-card.is-active .shot--real .shot-masthead-top,
.work-grid--wide .work-card.is-active .shot--real .shot-screen--two .shot-pg:nth-child(2) { animation-play-state: running; }

/* Soft dots behind the device, in the card's own tones. Painted as backgrounds so there is no extra
   markup; each is a hard-edged radial gradient, the half-pixel fade is just anti-aliasing. */
.work-card {
  --d1: var(--card-deep);
  --d2: var(--card-deep);
  background-image:
    radial-gradient(circle at var(--d1-at, 84% 10%), var(--d1) var(--d1-r, 42px), transparent calc(var(--d1-r, 42px) + 0.6px)),
    radial-gradient(circle at var(--d2-at, 104% 92%), var(--d2) var(--d2-r, 70px), transparent calc(var(--d2-r, 70px) + 0.6px));
}
.tone-mint     { --d1: #3fbd8a; --d2: #a8e3c7; }
.tone-pink     { --d1: #f6a3c3; --d2: #fbd3e2; }
.tone-yellow   { --d1: #86cba8; --d2: #d4ecd9; --d1-at: 76% 9%; }
.tone-lavender { --d1: #9db5ff; --d2: #c9cfff; --d1-at: 58% 7%; --d1-r: 46px; }

.work-card--feature {
  background-image:
    radial-gradient(circle at 39% 34%, #f6a3c3 52px, transparent 52.6px),
    radial-gradient(circle at 38% 74%, #fbe27a 48px, transparent 48.6px),
    radial-gradient(circle at 88% 11%, #2f9c6e 52px, transparent 52.6px),
    radial-gradient(circle at 97.5% 52%, #7cc9a6 42px, transparent 42.6px),
    radial-gradient(circle at 99% 90%, #b6e4ce 80px, transparent 80.6px);
}

@media (max-width: 1080px) {
  .work-grid--feature { grid-template-columns: 1fr; }
  .work-grid--feature .work-card { grid-template-columns: minmax(0, 46fr) minmax(0, 54fr); min-height: 380px; }
  .work-grid--feature .work-card-visual .shot { left: 4%; top: 14%; width: 118%; }
  :is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) { grid-template-columns: minmax(0, 44fr) minmax(0, 56fr); min-height: 440px; }
  :is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) h3 { font-size: clamp(28px, 4.2vw, 40px); }
  :is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .work-card-visual .shot { left: 6%; top: 12%; width: 110%; }
}
@media (max-width: 720px) {
  .work-grid--feature .work-card,
  :is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) { grid-template-columns: 1fr; min-height: 0; }
  .work-grid--feature .work-card-visual .shot,
  :is(.work-grid--feature .work-card--feature, .work-grid--wide .work-card) .work-card-visual .shot { left: 8%; top: 10%; width: 110%; }
  .work-grid--wide .work-card-visual { min-height: 260px; }
  .work-grid--wide .work-card { width: 100%; opacity: 1; filter: none; }
  .work-grid--feature .work-card-visual .shot--phones { left: 20%; width: 64%; }
  .work-card--feature {
    background-image:
      radial-gradient(circle at 92% 12%, #2f9c6e 40px, transparent 40.6px),
      radial-gradient(circle at 10% 60%, #f6a3c3 36px, transparent 36.6px),
      radial-gradient(circle at 100% 92%, #b6e4ce 70px, transparent 70.6px);
  }
}

/* ---------- Service pages ---------- */

.service-section { padding: clamp(40px, 6vw, 88px) 0 0; }

.section-note {
  margin: 0;
  max-width: 38ch;
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}

/* 01 Hero */

.service-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(32px, 5vw, 96px);
  padding: clamp(32px, 5vw, 72px) 0 clamp(28px, 3vw, 48px);
}

.service-hero .breadcrumb { margin-bottom: 28px; }

.service-hero h1 {
  font-size: clamp(36px, 4.4vw, 68px);
  max-width: 16ch;
  text-wrap: balance;
}

.service-hero-visual {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  background: var(--card-bg, #eee);
  aspect-ratio: 5 / 4;
  display: grid;
  place-items: center;
}

.service-hero-visual .shot { width: 78%; }
.service-hero-visual .shot--phones { width: 44%; }
.service-hero-visual .face { background: transparent; }
.service-hero-visual .mock,
.service-card-visual .mock { font-size: clamp(13px, 1.4vw, 20px); }
.service-hero-visual .mock-dash,
.service-card-visual .mock-dash { left: 18%; right: 18%; top: 50%; bottom: auto; transform: translateY(-50%); border-radius: 12px; padding: 1.4em 1.5em; }
.service-hero-visual .mock-flow,
.service-card-visual .mock-flow { left: 0; right: 34%; top: 50%; bottom: auto; transform: translateY(-50%); padding: 1.6em 1.6em 1.6em 2em; gap: 0; border-radius: 0 14px 14px 0; }
.service-hero-visual .mock-flow .node,
.service-card-visual .mock-flow .node { font-size: 0.75em; }
.service-hero-visual .mock-flow .line,
.service-card-visual .mock-flow .line { height: 1.2em; }

/* Support chat mock (new; same family as .mock-dash / .mock-flow) */

.mock-chat {
  left: 16%;
  right: 16%;
  top: 50%;
  transform: translateY(-50%);
  border-radius: 12px;
  padding: 1.2em 1.1em;
  font-size: clamp(10px, 1.1vw, 14px);
  display: flex;
  flex-direction: column;
  gap: 0.6em;
  text-align: left;
}

.mock-chat .bubble {
  max-width: 82%;
  padding: 0.7em 0.9em;
  border-radius: 1em;
  font-size: 0.7em;
  line-height: 1.35;
  color: #1c2b2a;
  background: #eef0ee;
  border-bottom-left-radius: 0.3em;
}

.mock-chat .bubble.us {
  align-self: flex-end;
  background: var(--green-deep);
  color: #fff;
  border-radius: 1em;
  border-bottom-right-radius: 0.3em;
}

.mock-chat .typing {
  display: flex;
  gap: 0.25em;
  padding: 0.6em 0.8em;
  border-radius: 1em;
  background: #eef0ee;
  align-self: flex-start;
}

.mock-chat .typing i {
  width: 0.45em;
  height: 0.45em;
  border-radius: 50%;
  background: #9aa39f;
  animation: typing 1.2s infinite ease-in-out;
}

.mock-chat .typing i:nth-child(2) { animation-delay: 0.15s; }
.mock-chat .typing i:nth-child(3) { animation-delay: 0.3s; }

@keyframes typing {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-0.25em); opacity: 1; }
}

/* 02 At a glance */

.glance { margin-top: 8px; }

/* 03 Overview */

.service-overview { padding: clamp(40px, 6vw, 88px) 0 0; }

.service-overview-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 24px clamp(32px, 5vw, 96px);
  align-items: start;
}

.service-overview-grid h2 {
  margin: 0;
  font-size: clamp(30px, 3.2vw, 50px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.04;
  text-wrap: balance;
}

.service-overview-grid .prose p:first-child { font-size: clamp(17px, 1.3vw, 20px); }
.service-overview-grid .prose p:last-child { margin-bottom: 0; }

.service-overview .stats { margin-top: clamp(32px, 4vw, 56px); }

/* 04 Features */

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 18px;
  overflow: hidden;
}

.feature {
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.4vw, 36px);
  background: var(--bg);
}

.feature .shape {
  position: relative;
  width: 84px;
  height: 84px;
  margin-bottom: 22px;
  display: grid;
  place-items: center;
  transition: transform 260ms cubic-bezier(.2,.8,.2,1);
}

.feature:hover .shape { transform: translateY(-4px) rotate(-3deg); }

.feature .shape::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}

.feature .shape svg {
  position: relative;
  z-index: 1;
  width: 32px;
  height: 32px;
  color: var(--ink);
}

.feature .shape--half svg { transform: translateY(8px); }

.feature h3 {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.feature p {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--muted);
  max-width: 32ch;
}

/* 05 Process */

.process {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(20px, 2.5vw, 40px);
  counter-reset: step;
}

.process li {
  position: relative;
  padding-top: 28px;
  border-top: 2px solid var(--line);
}

.process li::before {
  content: "";
  position: absolute;
  top: -2px;
  left: 0;
  width: 56px;
  height: 2px;
  background: var(--ink);
}

.process .num {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--card-deep, #eee);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.02em;
  margin-bottom: 18px;
}

.process h3 {
  margin: 0 0 8px;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.03em;
}

.process p {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--muted);
}

/* 06 Fit */

.fit {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 3vw, 48px);
  align-items: start;
}

.fit-intro h2 {
  margin: 0 0 14px;
  font-size: clamp(30px, 3.2vw, 50px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.04;
}

.fit-intro h2 .period,
.service-overview-grid h2 .period,
.deliverables h2 .period,
.faq-intro h2 .period {
  display: inline-block;
  width: 0.26em;
  height: 0.26em;
  border-radius: 50%;
  background: var(--green);
  margin-left: 0.06em;
}

.fit-intro p { margin: 0; color: var(--muted); font-size: 16px; max-width: 34ch; }

.fit-col {
  padding: clamp(24px, 2.4vw, 36px);
  border-radius: 18px;
  background: #fff;
  border: 1px solid var(--line);
}

.fit-col--good { background: var(--card-bg, #fff); border-color: transparent; }

.fit-col h3 {
  margin: 0 0 18px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}

.fit-col ul,
.checklist,
.package ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.fit-col li,
.package li {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 12px;
  align-items: start;
  font-size: 15.5px;
  line-height: 1.5;
  color: #3f444a;
}

.fit-col li svg,
.package li svg {
  width: 22px;
  height: 22px;
  padding: 3px;
  border-radius: 50%;
  margin-top: 1px;
  color: #fff;
  background: var(--green-deep);
}

.fit-col--not li svg { background: #fff; color: var(--coral); border: 1.5px solid var(--line); padding: 4px; }

/* 07 Deliverables */

.deliverables {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
  gap: clamp(32px, 5vw, 96px);
  align-items: start;
}

.deliverables h2 {
  margin: 0 0 28px;
  font-size: clamp(30px, 3.2vw, 50px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.04;
}

.checklist {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 32px;
}

.checklist li {
  position: relative;
  padding-left: 30px;
  font-size: 16px;
  line-height: 1.5;
  color: #3f444a;
}

.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--card-deep, #eee);
}

.checklist li::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 7px;
  width: 7px;
  height: 4px;
  border-left: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
  transform: rotate(-45deg);
}

.pill--lg { font-size: 14px; padding: 9px 14px; }

.facts-note { margin: 0; font-size: 15px; line-height: 1.55; color: #3f444a; }

/* 08 Packages */

.packages {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  align-items: stretch;
}

.package {
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.6vw, 40px);
  border-radius: 22px;
  background: #fff;
  border: 1px solid var(--line);
}

.package h3 {
  margin: 0 0 14px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

.package .price {
  margin: 0 0 6px;
  font-size: clamp(30px, 2.8vw, 42px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1;
}

.package .tag {
  margin: 0 0 24px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--line);
  font-size: 15px;
  color: var(--muted);
}

.package ul { margin-bottom: 28px; }
.package .btn { margin-top: auto; align-self: flex-start; }

.package--featured {
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}

.package--featured h3 { color: rgba(255, 255, 255, 0.65); }
.package--featured .tag { color: rgba(255, 255, 255, 0.7); border-color: rgba(255, 255, 255, 0.16); }
.package--featured li { color: rgba(255, 255, 255, 0.88); }
.package--featured li svg { background: var(--green); color: var(--ink); }
.package--featured .btn--primary { background: var(--green); border-color: var(--green); color: var(--ink); }
.package--featured .btn--primary:hover { background: #26d59a; }

/* 11 FAQ */

.faq {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(32px, 5vw, 96px);
  align-items: start;
}

.faq-intro { position: sticky; top: 24px; }

.faq-intro h2 {
  margin: 0 0 12px;
  font-size: clamp(30px, 3.2vw, 50px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.04;
}

.faq-intro p { margin: 0; color: var(--muted); font-size: 16px; }
.faq-intro a { color: var(--ink); }

.faq-list { border-top: 1px solid var(--line); }

.faq-list details { border-bottom: 1px solid var(--line); }

.faq-list summary {
  position: relative;
  list-style: none;
  cursor: pointer;
  padding: 22px 48px 22px 0;
  font-size: clamp(17px, 1.3vw, 20px);
  font-weight: 600;
  letter-spacing: -0.015em;
}

.faq-list summary::-webkit-details-marker { display: none; }

.faq-toggle {
  position: absolute;
  right: 0;
  top: 50%;
  width: 28px;
  height: 28px;
  margin-top: -14px;
  border-radius: 50%;
  border: 1.5px solid var(--ink);
}

.faq-toggle::before,
.faq-toggle::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 12px;
  height: 1.5px;
  background: var(--ink);
  transform: translate(-50%, -50%);
  transition: transform 200ms ease;
}

.faq-toggle::after { transform: translate(-50%, -50%) rotate(90deg); }
.faq-list details[open] .faq-toggle::after { transform: translate(-50%, -50%) rotate(0); }

.faq-body { padding: 0 48px 24px 0; }
.faq-body p { margin: 0; font-size: 16px; line-height: 1.6; color: #3f444a; max-width: 62ch; }

/* 12 Other services */

.services-more {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
}

.services-more .next-card { min-height: 180px; padding: 24px; }
.services-more .next-card h3 { font-size: clamp(20px, 1.6vw, 24px); }
.services-more .next-card .link-arrow { margin-top: 20px; }

/* Service page CTA spacing */

.service-page .cta { margin-top: clamp(40px, 6vw, 88px); }

/* ---------- Services listing ---------- */

.services-jump {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 28px;
}

.services-jump .filter { text-decoration: none; }

.services-anchor { scroll-margin-top: 24px; }
.services-anchor .work-card { height: 100%; }

.service-card .pills { margin-bottom: 24px; }
.service-card .pill { background: rgba(255, 255, 255, 0.7); border-color: transparent; font-size: 12px; }
.service-card-visual .face { background: transparent; }

/* ---------- Service responsive ---------- */

@media (max-width: 1080px) {
  .service-hero { grid-template-columns: 1fr; }
  .service-hero-visual { aspect-ratio: 16 / 10; }
  .service-overview-grid { grid-template-columns: 1fr; }
  .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .process { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fit { grid-template-columns: 1fr 1fr; }
  .fit-intro { grid-column: 1 / -1; }
  .deliverables { grid-template-columns: 1fr; }
  .packages { grid-template-columns: 1fr; }
  .faq { grid-template-columns: 1fr; }
  .faq-intro { position: static; }
  .services-more { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .service-hero h1 { max-width: none; }
  .service-hero-visual { aspect-ratio: 4 / 3; }
  .service-hero-visual .shot { width: 88%; }
  .service-hero-visual .shot--phones { width: 60%; }
  .feature-grid { grid-template-columns: 1fr; }
  .process { grid-template-columns: 1fr; }
  .fit { grid-template-columns: 1fr; }
  .checklist { grid-template-columns: 1fr; }
  .services-more { grid-template-columns: 1fr; }
  .service-card-visual .mock { font-size: 12px; }
}

/* ---------- Contact page ---------- */

.contact-body {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
  gap: clamp(32px, 5vw, 96px);
  padding: 0 0 clamp(48px, 7vw, 104px);
}

.contact-form {
  display: grid;
  gap: 24px;
  padding: clamp(24px, 3vw, 40px);
  border-radius: 22px;
  background: #fff;
  border: 1px solid var(--line);
}

.field-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

.field {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.field label,
.field legend {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  padding: 0;
}

.field legend { margin-bottom: 8px; }

.field .optional {
  margin-left: 6px;
  font-weight: 500;
  color: var(--faint);
}

.field input,
.field textarea,
.field select {
  appearance: none;
  width: 100%;
  font: inherit;
  font-size: 16px;
  color: var(--ink);
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  transition: border-color 150ms ease, background 150ms ease, box-shadow 150ms ease;
}

.field input::placeholder,
.field textarea::placeholder { color: var(--faint); }

.field textarea {
  resize: vertical;
  min-height: 160px;
  line-height: 1.5;
}

.field input:hover,
.field textarea:hover,
.field select:hover { border-color: #c5c7c3; }

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  background: #fff;
  border-color: var(--ink);
  box-shadow: 0 0 0 4px rgba(30, 194, 139, 0.18);
}

.select { position: relative; }

.select select { padding-right: 44px; cursor: pointer; }

.select::after {
  content: "";
  position: absolute;
  right: 18px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--ink);
  border-bottom: 1.5px solid var(--ink);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

.choices {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.choice {
  position: relative;
  display: inline-block;
  cursor: pointer;
}

.choice input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.choice span {
  display: inline-block;
  border: 1.5px solid var(--line);
  background: #fff;
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  padding: 9px 16px;
  border-radius: 999px;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}

.choice:hover span { border-color: var(--ink); }

.choice input:focus-visible + span {
  border-color: var(--ink);
  box-shadow: 0 0 0 4px rgba(30, 194, 139, 0.18);
}

.choice input:checked + span {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}

.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  margin-top: 8px;
}

.form-foot .btn { cursor: pointer; }
.form-turnstile { margin-left: auto; min-height: 65px; }   /* the Turnstile widget, bottom right of the form */

.form-errors {
  padding: 14px 18px;
  border-radius: 12px;
  background: #fff1ef;
  border: 1px solid #f3c4bd;
  color: #8a2a1c;
  font-size: 15px;
}
.form-errors p { margin: 0; }
.field.is-invalid input,
.field.is-invalid textarea { border-color: #d9534f; }

.form-sent h2 {
  margin: 0 0 8px;
  font-size: clamp(26px, 2.4vw, 36px);
  font-weight: 800;
  letter-spacing: -0.04em;
}
.form-sent h2 .period { display: inline-block; width: 0.22em; height: 0.22em; margin-left: 0.06em; border-radius: 50%; background: var(--green); vertical-align: baseline; }
.form-sent p { margin: 0 0 10px; color: var(--muted); }
.form-sent .form-note a { color: var(--ink); }

.form-note {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--muted);
}

.form-note-icon {
  width: 16px;
  height: 16px;
  flex: none;
}

.contact-facts { top: 32px; }
.contact-facts > div + div { margin-top: 30px; }
.contact-facts .shape--hex { margin-bottom: 15px; }

.contact-line {
  margin: 6px 0 0;
  font-size: clamp(20px, 1.6vw, 26px);
  font-weight: 700;
  letter-spacing: -0.03em;
}

.contact-line a { text-decoration: none; }
.contact-line a:hover { color: var(--green-deep); }

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

@media (max-width: 720px) {
  .field-row { grid-template-columns: 1fr; }
}

/* Question mark with a green dot. The glyph is clipped just above its own dot
   (Inter's sits on the baseline, 0.17em tall, with a gap before the hook) and
   the heading's period is dropped into the space. Offsets are in em so they
   track the clamp()ed heading size. */

.qmark {
  position: relative;
  display: inline-block;
}

.qmark-glyph {
  display: inline-block;
  transform: translateY(-5px);
  clip-path: inset(0 0 0.33em 0);   /* baseline sits 0.13em up; keep everything above 0.2em */
}

.page-head h1 .qmark .period {
  position: absolute;
  width: 0.2em;
  height: 0.2em;
  left: 0.166em;     /* centred under the hook: the glyph dot is centred at 0.266em */
  bottom: 0.12em;    /* bottom edge where the glyph dot sat, 0.01em below the baseline */
  margin: 0;
}

/* ---------- Get in touch, on the contact page (contact-burst.js) ---------- */

/* The button bursts: it goes while its blobs are in flight, then fades back into place. */
header .btn.is-burst { visibility: hidden; }
header .btn.is-back { animation: btn-back 500ms ease-out; }
@keyframes btn-back {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}
.blobs {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2147483645;                                /* above the page, under the cursor */
  pointer-events: none;
}
.blobs > span {
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 50%;
  will-change: transform, opacity;
}
/* The field swallowing a blob: a ring puffs out from its edge and it swells a touch. */
.field input.is-absorbing { animation: absorb 380ms ease-out; }
@keyframes absorb {
  0%   { box-shadow: 0 0 0 0 rgba(17, 17, 17, 0.28); transform: scale(1); }
  35%  { transform: scale(1.012); }
  100% { box-shadow: 0 0 0 16px rgba(17, 17, 17, 0); transform: scale(1); }
}

/* ---------- Ambient drift (contact page) ---------- */

/* Large flat dots in the tile colours, drifting slowly behind the page content on their own short
   tracks (the hero's glide), clipped to the page so they never cause a sideways scroll. */
.has-drift { position: relative; }
.has-drift > .container { position: relative; z-index: 1; }
.drift {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.drift-dot {
  position: absolute;
  border-radius: 50%;
  animation: glide var(--glide) ease-in-out infinite alternate;
  animation-delay: var(--phase);
  will-change: transform;
}
.drift-dot--mint     { width: 380px; height: 380px; background: var(--mint);     left: -150px; top: -120px;   --gx: 40px;  --gy: 70px;  --glide: 16s; --phase: -4s; }
.drift-dot--yellow   { width: 240px; height: 240px; background: var(--yellow);   right: -70px; top: 14%;      --gx: -30px; --gy: 90px;  --glide: 14s; --phase: -9s; }
.drift-dot--pink     { width: 300px; height: 300px; background: var(--pink);     left: 6%;     bottom: -140px; --gx: 60px; --gy: -50px; --glide: 18s; --phase: -2s; }
.drift-dot--lavender { width: 200px; height: 200px; background: var(--lavender); right: 10%;   bottom: 6%;    --gx: -70px; --gy: 40px;  --glide: 15s; --phase: -11s; }
.drift-dot--sky      { width: 150px; height: 150px; background: var(--sky);      left: 42%;    top: -50px;    --gx: 50px;  --gy: 60px;  --glide: 13s; --phase: -6s; }
@media (prefers-reduced-motion: reduce) { .drift-dot { animation: none; } }
@media (max-width: 720px) {
  .drift-dot--mint { width: 240px; height: 240px; left: -110px; top: -80px; }
  .drift-dot--pink { width: 200px; height: 200px; }
  .drift-dot--sky  { display: none; }
}

/* ---------- Hosting page ---------- */

.hosting-page .service-hero h1 { max-width: 14ch; }

/* PageSpeed card in the hero */

.opt-card {
  width: 78%;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 28px);
  padding: clamp(18px, 2vw, 28px);
  border-radius: 18px;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}

.score {
  position: relative;
  width: clamp(104px, 10vw, 132px);
  height: clamp(104px, 10vw, 132px);
  flex: none;
}

.score svg { width: 100%; height: 100%; transform: rotate(-90deg); }

.score-track { fill: none; stroke: var(--line); stroke-width: 7; }

.score-ring {
  fill: none;
  stroke: var(--green);
  stroke-width: 7;
  stroke-linecap: round;
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  animation: score 1.6s cubic-bezier(0.2, 0.7, 0.2, 1) 500ms forwards;
}

@keyframes score { to { stroke-dashoffset: 1; } }

.score-num {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  line-height: 1;
}

.score-num b {
  font-size: clamp(30px, 3vw, 40px);
  font-weight: 800;
  letter-spacing: -0.04em;
  color: var(--ink);
}

.score-num span {
  display: block;
  margin-top: 6px;
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

.bundles { display: grid; gap: 8px; min-width: 0; flex: 1; }

.bundle-head,
.bundle-row {
  display: grid;
  grid-template-columns: 70px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
}

.bundle-head {
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.bundle-name { font-size: 13px; font-weight: 500; color: var(--muted); }

.blocks { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 4px; }

.blocks i {
  display: block;
  height: 14px;
  border-radius: 4px;
  background: var(--bg);
  border: 1px dashed var(--line);
}

.blocks i.on {
  background: var(--green);
  border: 0;
  transform-origin: left;
  animation: blockin 600ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
}

.bundle-row:nth-child(2) .blocks i.on { animation-delay: 700ms; }
.bundle-row:nth-child(3) .blocks i.on { animation-delay: 950ms; }
.bundle-row:nth-child(4) .blocks i.on { animation-delay: 1200ms; }

@keyframes blockin {
  from { transform: scaleX(0.2); opacity: 0; }
  to   { transform: scaleX(1); opacity: 1; }
}

.bundle-foot {
  margin-top: 2px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  font-size: 11.5px;
  color: var(--muted);
}

.bundle-foot span { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.bundle-foot i { display: inline-block; width: 14px; height: 8px; border-radius: 3px; }
.bundle-foot i.on { background: var(--green); }
.bundle-foot i.off { background: var(--bg); border: 1px dashed var(--line); }

/* Edge map */

.map {
  position: relative;
  padding: clamp(16px, 2vw, 28px);
  border-radius: 22px;
  background: #fff;
  border: 1px solid var(--line);
  overflow: hidden;
}

.map::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(55% 70% at 50% 45%, rgba(30, 194, 139, 0.10), transparent 70%);
  pointer-events: none;
}

.map-inner { position: relative; max-width: 1100px; margin: 0 auto; }
.worldmap { display: block; width: 100%; height: auto; }

.map .land { fill: none; stroke: #c9ccc8; stroke-width: 2.5; stroke-linecap: round; }
.map .nd-core { fill: var(--green-deep); }
.map .nd-halo { fill: rgba(30, 194, 139, 0.22); }

.map .ping {
  fill: none;
  stroke: var(--green);
  stroke-width: 1;
  opacity: 0;
  transform-origin: center;
  transform-box: fill-box;
  animation: ping 3.2s ease-out infinite;
}
.map .ping.p1 { animation-delay: 0.5s; }
.map .ping.p2 { animation-delay: 1.1s; }
.map .ping.p3 { animation-delay: 1.7s; }
.map .ping.p4 { animation-delay: 2.2s; }
.map .ping.p5 { animation-delay: 2.8s; }

@keyframes ping {
  0%   { transform: scale(0.5); opacity: 0.8; }
  100% { transform: scale(4); opacity: 0; }
}

/* Beams: each visitor's request runs a 12s cycle, staggered one second apart */
.map .bm,
.map .bm-glow {
  fill: none;
  stroke-linecap: round;
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  animation: beam 12s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.map .bm { stroke: var(--green-deep); stroke-width: 1.4; }
.map .bm-glow { stroke: rgba(30, 194, 139, 0.35); stroke-width: 4; }

.map .vis {
  fill: var(--ink);
  opacity: 0;
  transform-origin: center;
  transform-box: fill-box;
  animation: visitor 12s ease-out infinite;
}

.map .head {
  fill: var(--ink);
  opacity: 0;
  offset-rotate: 0deg;
  animation: head 12s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

.map .hit {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.2;
  opacity: 0;
  transform-origin: center;
  transform-box: fill-box;
  animation: hit 12s ease-out infinite;
}

@keyframes beam {
  0%    { stroke-dashoffset: 100; opacity: 1; }
  10%   { stroke-dashoffset: 0; opacity: 1; }
  26%   { stroke-dashoffset: 0; opacity: 1; }
  34%   { stroke-dashoffset: -100; opacity: 0.6; }
  34.1% { stroke-dashoffset: 100; opacity: 0; }
  100%  { stroke-dashoffset: 100; opacity: 0; }
}

@keyframes head {
  0%   { offset-distance: 0%; opacity: 0; }
  1%   { opacity: 1; }
  9%   { opacity: 1; }
  10%  { offset-distance: 100%; opacity: 0; }
  100% { offset-distance: 100%; opacity: 0; }
}

@keyframes visitor {
  0%   { opacity: 0; transform: scale(0.2); }
  4%   { opacity: 1; transform: scale(1.4); }
  8%   { transform: scale(1); }
  26%  { opacity: 1; }
  34%  { opacity: 0; transform: scale(0.6); }
  100% { opacity: 0; }
}

@keyframes hit {
  0%   { opacity: 0; transform: scale(0.4); }
  9%   { opacity: 0; transform: scale(0.4); }
  10%  { opacity: 0.9; transform: scale(0.6); }
  22%  { opacity: 0; transform: scale(3); }
  100% { opacity: 0; }
}

.map .beam.b0,  .map .beam.b0  > * { animation-delay: 0s; }
.map .beam.b1,  .map .beam.b1  > * { animation-delay: 1s; }
.map .beam.b2,  .map .beam.b2  > * { animation-delay: 2s; }
.map .beam.b3,  .map .beam.b3  > * { animation-delay: 3s; }
.map .beam.b4,  .map .beam.b4  > * { animation-delay: 4s; }
.map .beam.b5,  .map .beam.b5  > * { animation-delay: 5s; }
.map .beam.b6,  .map .beam.b6  > * { animation-delay: 6s; }
.map .beam.b7,  .map .beam.b7  > * { animation-delay: 7s; }
.map .beam.b8,  .map .beam.b8  > * { animation-delay: 8s; }
.map .beam.b9,  .map .beam.b9  > * { animation-delay: 9s; }
.map .beam.b10, .map .beam.b10 > * { animation-delay: 10s; }
.map .beam.b11, .map .beam.b11 > * { animation-delay: 11s; }

.map-legend,
.legend {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--muted);
}

.map-legend i,
.legend i { display: inline-block; vertical-align: middle; margin-right: 8px; }
.k-node { width: 8px; height: 8px; border-radius: 50%; background: var(--green-deep); box-shadow: 0 0 0 3px rgba(30, 194, 139, 0.22); }
.k-vis  { width: 8px; height: 8px; border-radius: 50%; background: var(--ink); }
.k-beam { width: 18px; height: 2px; border-radius: 2px; background: var(--green-deep); }
.k-ok   { width: 8px; height: 8px; border-radius: 50%; background: var(--green); }
.k-bad  { width: 8px; height: 8px; border-radius: 50%; background: var(--coral); }

.points {
  list-style: none;
  margin: clamp(24px, 3vw, 40px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px 32px;
}

.points li {
  position: relative;
  padding-left: 22px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--muted);
}

.points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--green);
}

.points b { color: var(--ink); font-weight: 700; }

/* Edge filter */

.edge {
  position: relative;
  margin-bottom: 20px;
  padding: clamp(18px, 2vw, 28px);
  border-radius: 22px;
  background: #fff;
  border: 1px solid var(--line);
  overflow: hidden;
}

.edge-lanes {
  position: relative;
  height: 150px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 16px;
}

.lane { position: relative; height: 100%; }

.lane::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  border-top: 1px dashed var(--line);
}

.wall {
  position: relative;
  z-index: 2;
  width: 104px;
  padding: 12px 0;
  border-radius: 14px;
  border: 1.5px solid var(--green);
  background: var(--mint);
  display: grid;
  gap: 6px;
  justify-items: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--green-deep);
}

.wall .chip {
  padding: 3px 8px;
  border-radius: 6px;
  background: var(--ink);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
}

.packet {
  position: absolute;
  top: 50%;
  left: 0;
  width: 10px;
  height: 10px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--green);
  animation: pass 4s linear infinite;
  will-change: left, opacity;
}

.packet.bad { background: var(--coral); animation: block 4s linear infinite; }

.lane.in .packet:nth-child(1) { animation-delay: 0s; top: 30%; }
.lane.in .packet:nth-child(2) { animation-delay: 0.9s; top: 70%; }
.lane.in .packet:nth-child(3) { animation-delay: 1.7s; top: 50%; }
.lane.in .packet:nth-child(4) { animation-delay: 2.4s; top: 40%; }
.lane.in .packet:nth-child(5) { animation-delay: 3.2s; top: 60%; }
.lane.out .packet:nth-child(1) { animation-delay: 0s; top: 50%; }
.lane.out .packet:nth-child(2) { animation-delay: 1.7s; top: 50%; }

@keyframes pass {
  0%   { left: -4%; opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { left: 104%; opacity: 0; }
}

@keyframes block {
  0%   { left: -4%; opacity: 0; transform: scale(1); }
  8%   { opacity: 1; }
  88%  { left: 96%; opacity: 1; transform: scale(1); }
  96%  { left: 99%; opacity: 0; transform: scale(2.4); }
  100% { left: 99%; opacity: 0; }
}

.edge-labels {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 16px;
  margin-top: 10px;
  font-size: 13px;
  color: var(--muted);
}

.edge-labels .c { width: 104px; text-align: center; color: var(--green-deep); font-weight: 600; }
.edge-labels .r { text-align: right; }

.feature-grid--3 .feature p { max-width: 36ch; }

/* Ops cards */

.ops {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

.op-card {
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.6vw, 36px);
  border-radius: 22px;
  background: #fff;
  border: 1px solid var(--line);
}

.op-card h3 {
  margin: 0 0 14px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}

.op-card h4 {
  margin: 0 0 10px;
  font-size: clamp(22px, 1.9vw, 28px);
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1.1;
}

.op-card p {
  margin: 0 0 24px;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--muted);
}

.op-card > :last-child { margin-top: auto; }

.op-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  padding: 14px 16px;
  border-radius: 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 13px;
  color: var(--muted);
}

.op-line .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px rgba(30, 194, 139, 0.22); }
.op-line .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; color: var(--ink); }
.op-line .ok { padding: 2px 8px; border-radius: 999px; background: var(--mint); color: var(--green-deep); font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.op-line .dim { color: var(--faint); }

.boxes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  padding: 14px;
  border-radius: 14px;
  background: var(--bg);
  border: 1px solid var(--line);
}

.box {
  aspect-ratio: 1 / 0.8;
  border-radius: 9px;
  border: 1px dashed #c9ccc8;
  display: grid;
  place-items: center;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

.box.you {
  border: 1.5px solid var(--green);
  background: var(--mint);
  color: var(--green-deep);
  font-weight: 700;
}

.timeline {
  padding: 22px 16px 14px;
  border-radius: 14px;
  background: var(--bg);
  border: 1px solid var(--line);
}

.ticks {
  position: relative;
  height: 44px;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
}

.ticks::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
  border-top: 1px solid var(--line);
}

.tick {
  position: relative;
  width: 2px;
  height: 12px;
  margin-bottom: 12px;
  border-radius: 2px;
  background: #c9ccc8;
}

.tick.snap { height: 20px; background: var(--green); }
.tick.now { height: 26px; background: var(--ink); }

.tick.now::after {
  content: "on-demand";
  position: absolute;
  top: -20px;
  right: 0;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

.ticks-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font-size: 12px;
  color: var(--faint);
}

@media (max-width: 1080px) {
  .feature-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .points { grid-template-columns: 1fr; }
  .ops { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .opt-card { width: 86%; flex-direction: column; align-items: stretch; }
  .hosting-page .service-hero-visual { aspect-ratio: auto; padding: 28px 0; }
  .score { margin: 0 auto; }
  .feature-grid--3 { grid-template-columns: 1fr; }
  .edge-labels .c { width: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .score-ring { animation: none; stroke-dashoffset: 1; }
  .blocks i.on { animation: none; }
  .map .ping, .map .bm, .map .bm-glow, .map .vis, .map .head, .map .hit { animation: none; }
  .map .bm, .map .bm-glow { stroke-dashoffset: 0; opacity: 0.6; }
  .packet { animation: none; left: 45%; opacity: 1; }
}

/* ---------- Slim build: "under construction" note beneath the single work card ---------- */

.work-note {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 20px 28px;
  align-items: start;
  padding: clamp(24px, 2.6vw, 40px) clamp(24px, 3vw, 48px);
  border-radius: 18px;
  border: 1.5px dashed #c9cbc7;
  background: rgba(255, 255, 255, 0.6);
}

.work-note-dots {
  display: flex;
  gap: 6px;
  padding-top: 6px;
}
.work-note-dots i {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--mint);
  animation: work-note-pulse 2.4s ease-in-out infinite;
}
.work-note-dots i:nth-child(2) { background: var(--yellow); animation-delay: 0.3s; }
.work-note-dots i:nth-child(3) { background: var(--pink); animation-delay: 0.6s; }

@keyframes work-note-pulse {
  0%, 100% { transform: translateY(0); opacity: 0.6; }
  50% { transform: translateY(-5px); opacity: 1; }
}

.work-note .chip {
  margin-bottom: 14px;
  background: #fff;
  border: 1px solid var(--line);
}
.work-note h3 {
  margin: 0 0 8px;
  font-size: clamp(22px, 2.2vw, 30px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1.05;
}
.work-note h3 .period {
  display: inline-block;
  width: 0.2em;
  height: 0.2em;
  margin-left: 0.08em;
  border-radius: 50%;
  background: var(--green);
  vertical-align: baseline;
}
.work-note p { margin: 0; color: var(--muted); font-size: 16px; max-width: 54ch; }
.work-note a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.work-note a:hover { color: var(--green-deep); }

@media (max-width: 720px) {
  .work-note { grid-template-columns: 1fr; gap: 14px; }
  .work-note-dots { padding-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .work-note-dots i { animation: none; opacity: 1; }
}
