/* =================================================================
   SELF-HOSTED MANROPE

   The font files live in assets/fonts/ and are served from our own
   domain instead of fonts.googleapis.com. Two reasons: privacy (no
   visitor IP is sent to Google just for a typeface — a German court
   ruled that alone a GDPR breach in 2022) and speed (no extra DNS +
   TLS handshake to two Google hosts before text can render).

   These are variable fonts: one file covers every weight from 200 to
   800, so the old five-weight Google URL collapses into one request.
   The family name stays "Manrope" — nothing else in this file or the
   HTML changes. unicode-range keeps the greek file from downloading
   at all unless greek text actually appears on a page.
   ================================================================= */

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('assets/fonts/manrope-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('assets/fonts/manrope-latin-ext-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('assets/fonts/manrope-greek-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF;
}

:root {
  --bg: #07111f;
  --bg-2: #101d30;
  --panel: rgba(14, 25, 39, 0.72);
  --panel-strong: rgba(9, 18, 30, 0.94);
  --card: rgba(17, 28, 46, 0.82);
  --text: #edf5ff;
  --muted: #a8bad2;
  --line: rgba(165, 195, 255, 0.16);
  --primary: #7d8cff;
  --primary-2: #6ce2d1;
  --accent: #ff9e6d;
  --shadow: 0 25px 80px rgba(7, 12, 22, 0.52);

  /* hero intro timeline — the header wordmark reads these too */
  --intro-in: 0.5s;       /* logo arrives                            */
  --intro-fly-at: 0.72s;  /* logo sets off for the header, copy fades in */
  --intro-fly: 1.8s;      /* how long the flight takes               */
  --copy-step: 0.09s;     /* gap between copy lines                  */
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: "Manrope", sans-serif;
  background:
    radial-gradient(circle at top left, rgba(125, 140, 255, 0.2), transparent 28%),
    radial-gradient(circle at bottom right, rgba(108, 226, 209, 0.14), transparent 38%),
    var(--bg);
  color: var(--text);
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

.page-shell {
  position: relative;
  min-height: 100vh;
  /* clip, NOT hidden. Both crop the orbs to the shell, but overflow:
     hidden makes this element a scroll container, and that silently
     kills position: sticky on the header inside it — the bar scrolled
     away with the page instead of staying put. overflow: clip crops
     without creating a scroll container, so the header sticks. */
  overflow: clip;
}

.container {
  width: min(1160px, calc(100% - 40px));
  margin: 0 auto;
}

.section {
  padding: 110px 0;
}

.orb {
  position: absolute;
  filter: blur(70px);
  opacity: 0.45;
  border-radius: 50%;
  pointer-events: none;
}

.orb-1 {
  width: 340px;
  height: 340px;
  background: rgba(125, 140, 255, 0.8);
  top: 90px;
  left: -60px;
}

.orb-2 {
  width: 300px;
  height: 300px;
  background: rgba(108, 226, 209, 0.65);
  right: 10%;
  top: 220px;
}

.orb-3 {
  width: 260px;
  height: 260px;
  background: rgba(255, 158, 109, 0.5);
  right: 18%;
  bottom: 80px;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  backdrop-filter: blur(14px);
  background: rgba(7, 17, 31, 0.4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

.nav-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 0;
}

.brand,
.brand-footer {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-weight: 800;
  letter-spacing: -0.04em;
  text-decoration: none;
}

.brand-logo-img {
  height: 38px;
  width: auto;
  object-fit: contain;
  display: block;
  transition: transform 0.25s ease, filter 0.25s ease;
  filter: drop-shadow(0 4px 12px rgba(29, 78, 216, 0.25));
}

.brand:hover .brand-logo-img {
  transform: translateY(-1px) scale(1.02);
  filter: drop-shadow(0 6px 18px rgba(37, 99, 235, 0.45));
}

.brand-footer-img {
  height: 34px;
  width: auto;
  object-fit: contain;
  display: block;
  transition: transform 0.25s ease, opacity 0.25s ease;
  opacity: 0.9;
}

.brand-footer:hover .brand-footer-img {
  opacity: 1;
  transform: translateY(-1px);
}

.brand-mark {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--primary), var(--primary-2));
  color: #fff;
  box-shadow: 0 10px 25px rgba(109, 117, 255, 0.45);
}

.main-nav {
  display: flex;
  gap: 24px;
  color: var(--muted);
  font-size: 0.95rem;
}

.main-nav a {
  transition: color 0.2s ease;
}

.main-nav a:hover {
  color: var(--text);
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  padding: 0.95rem 1.5rem;
  font-weight: 700;
  border: 1px solid transparent;
  transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
}

.button:hover {
  transform: translateY(-2px);
}

.button-primary {
  background: linear-gradient(135deg, var(--primary), #8b8bff 48%, var(--primary-2));
  box-shadow: 0 18px 34px rgba(109, 117, 255, 0.35);
}

.button-secondary {
  border-color: rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
}

/* =================================================================
   HERO — the logo animates in, holds, dissolves, and the headline
   rises through it.

   Retune the whole sequence from these four variables; everything
   downstream is derived from them, so you never have to hand-edit
   individual delays.
   ================================================================= */

.hero {
  --copy-at: var(--intro-fly-at);   /* copy fades in as the logo leaves */

  --header-h: 77px;

  /* Fill the space under the header exactly, then centre everything in
     it. This is what puts the intro logo at the optical centre of the
     screen instead of low on the page. */
  min-height: calc(100vh - var(--header-h) - 110px);
  min-height: calc(100svh - var(--header-h) - 110px);
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: start;      /* content starts just under the header */
  padding: 44px 0 40px;
}

.star-canvas,
.network-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

.hero > .container {
  position: relative;
  z-index: 1;
  display: grid;
  justify-items: center;
}

.hero-stage {
  position: relative;
  width: 100%;
  display: grid;
  place-items: center;
  text-align: center;
}

/* Everything in the stage shares one grid cell, so the logo sits
   exactly on top of the copy it dissolves into. */
.hero-stage > * {
  grid-area: 1 / 1;
}

/* --- ambient glow behind the whole thing ------------------------- */

.hero-aura {
  /* Out of flow: a 720px-tall glow would otherwise set the height of the
     whole hero and push the copy back down to the middle of the screen.
     inset+margin:auto centres it without touching transform, which the
     breathe animation needs. */
  position: absolute;
  inset: 0;
  margin: auto;
  width: min(720px, 92vw);
  height: min(720px, 92vw);
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgba(125, 140, 255, 0.26),
    rgba(108, 226, 209, 0.1) 45%,
    transparent 70%
  );
  filter: blur(24px);
  opacity: 0;
  animation:
    aura-in 1.5s ease forwards,
    aura-breathe 8s ease-in-out 1.5s infinite;
}

@keyframes aura-in {
  from { opacity: 0; transform: scale(0.72); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes aura-breathe {
  0%, 100% { opacity: 0.95; transform: scale(1); }
  50%      { opacity: 0.68; transform: scale(1.07); }
}

/* --- the intro logo ---------------------------------------------- */

.intro-logo {
  position: relative;
  z-index: 2;
  width: min(440px, 66vw);
  padding: 26px;
  /* No overflow:hidden here. The sheen is masked to the logo artwork, so
     clipping is unnecessary — and it used to cut the image's blue
     drop-shadow off at the box edge, which read as a rectangle. */
  pointer-events: none;
  opacity: 0;
  will-change: transform, opacity, filter;
  animation:
    logo-in var(--intro-in) cubic-bezier(0.16, 1, 0.3, 1) forwards,
    logo-fly var(--intro-fly) cubic-bezier(0.55, 0, 0.25, 1) var(--intro-fly-at) forwards;
}

.intro-logo img {
  width: 100%;
  filter: drop-shadow(0 18px 44px rgba(29, 78, 216, 0.35));
}

@keyframes logo-in {
  from {
    opacity: 0;
    transform: translateY(28px) scale(0.9);
    filter: blur(18px);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

/* Travels to where the header wordmark sits. script.js measures both
   elements and writes --fly-x / --fly-y / --fly-s; the values below are
   only a rough fallback for the split second before that runs. */
@keyframes logo-fly {
  from {
    opacity: 1;
    transform: none;
  }
  60% {
    opacity: 0.55;
  }
  to {
    opacity: 0;
    transform:
      translate(var(--fly-x, -34vw), var(--fly-y, -32vh))
      scale(var(--fly-s, 0.34));
  }
}


.intro-sheen {
  position: absolute;
  inset: 26px;              /* matches .intro-logo padding = the image box */
  /* A brand-tinted light sweep. Pure white is invisible here because
     the logo is already near-white, so the band carries the brand
     gradient instead. */
  background: linear-gradient(
    100deg,
    transparent 38%,
    rgba(125, 140, 255, 0.8) 47%,
    rgba(120, 235, 215, 0.9) 53%,
    transparent 62%
  );
  background-size: 300% 100%;
  background-position: 0% 0;
  /* Clip the light to the logo artwork itself. */
  -webkit-mask-image: url("assets/logo.png");
  mask-image: url("assets/logo.png");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  animation: sheen 0.55s cubic-bezier(0.35, 0, 0.25, 1) calc(var(--intro-in) - 0.22s) forwards;
}

@keyframes sheen {
  from { background-position: 0% 0; }
  to   { background-position: 100% 0; }
}

/* --- the copy that rises out of the dissolve --------------------- */

.hero-copy {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1040px;
}

.step {
  opacity: 0;
  animation: step-in 1.05s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: calc(var(--copy-at) + var(--i) * var(--copy-step));
}

@keyframes step-in {
  from { opacity: 0; transform: translateY(18px); filter: blur(9px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

/* Headline lines are masked so they slide up from behind their own
   baseline rather than just fading. */
.line-mask {
  display: block;
  overflow: hidden;
  padding-bottom: 0.16em;   /* room for descenders inside the mask */
}

.hero-copy h1 .line {
  display: block;
  text-wrap: balance;
  animation-name: line-rise;
  animation-duration: 1.15s;
}

@keyframes line-rise {
  from { opacity: 0; transform: translateY(120%); }
  to   { opacity: 1; transform: none; }
}

/* --- shared type styles (also used by the sections below) -------- */

.eyebrow {
  margin: 0 0 16px;
  color: var(--primary-2);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-size: 0.75rem;
  font-weight: 800;
}

/* Hero variant: no full stops, lighter weight, wider tracking, and a
   small raised dot between each word. */
.eyebrow-hero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* four widely-tracked words do not fit one phone line, so let it wrap
     rather than run off the edge */
  flex-wrap: wrap;
  row-gap: 10px;
  gap: 18px;
  margin-bottom: 24px;
  font-size: 0.7rem;
  font-weight: 600;
}

/* "The People Behind ErgonSite" is four widely-tracked words; at phone
   widths the tracking has to come in for it to stay on one line, and a
   dot stranded at the end of a wrapped line reads as a typo. */
@media (max-width: 560px) {
  .eyebrow-hero {
    gap: 9px;
    row-gap: 8px;
    font-size: 0.62rem;
  }

  .eyebrow-hero > span {
    letter-spacing: 0.16em;
    margin-right: -0.16em;
  }

  .eyebrow-hero .eyebrow-dot {
    width: 5px;
    height: 5px;
  }
}

.eyebrow-hero > span {
  letter-spacing: 0.38em;
  /* cancel the trailing letter-space so the dots sit optically centred */
  margin-right: -0.38em;
}

.eyebrow-hero .eyebrow-dot {
  width: 6px;
  height: 6px;
  margin-right: 0;
  flex: 0 0 auto;
  transform: rotate(45deg);
  border-radius: 1px;
  background: linear-gradient(135deg, var(--primary), var(--primary-2));
  box-shadow: 0 0 10px rgba(108, 226, 209, 0.55);
}

.hero-copy h1,
.section-heading h2 {
  margin: 0;
  letter-spacing: -0.055em;
  line-height: 1.05;
}

.hero-copy h1 {
  font-size: clamp(2.3rem, 5vw, 3.9rem);
}

.subtitle {
  font-size: 1.1rem;
  text-wrap: pretty;
  color: var(--muted);
  line-height: 1.7;
  max-width: 620px;
  margin: 24px auto 0;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
  margin-top: 32px;
}

.proof-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 40px;
  margin-top: 38px;
}

.proof-row div {
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--muted);
}

.proof-row strong {
  color: var(--text);
  font-size: 1.5rem;
  letter-spacing: -0.05em;
}

/* --- social links in the header ---------------------------------- */

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

.social-link {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
  color: var(--muted);
  transition:
    transform 0.25s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.25s ease,
    border-color 0.25s ease,
    background 0.25s ease,
    box-shadow 0.25s ease;
}

.social-link svg {
  width: 17px;
  height: 17px;
  fill: currentColor;
}

.social-link:hover {
  transform: translateY(-3px);
  color: #fff;
  border-color: rgba(125, 140, 255, 0.55);
  background: linear-gradient(135deg, rgba(125, 140, 255, 0.24), rgba(108, 226, 209, 0.14));
  box-shadow: 0 12px 26px rgba(109, 117, 255, 0.3);
}

.social-link:focus-visible {
  outline: 2px solid var(--primary-2);
  outline-offset: 3px;
}

/* --- respect the user's motion preference ------------------------ */

@media (prefers-reduced-motion: reduce) {
  .intro-logo,
  .hero-aura {
    display: none;
  }

  .step,
  .hero-copy h1 .line {
    opacity: 1 !important;
    animation: none !important;
    transform: none !important;
    filter: none !important;
  }

  .pulse-dot {
    animation: none;
  }

  .marquee-track,
  .member-ring {
    animation: none;
  }

  .member-photo {
    opacity: 1;
    transform: none;
  }

  .marquee {
    overflow-x: auto;
  }

  html {
    scroll-behavior: auto;
  }
}

/* =================================================================
   CLIENT MARQUEE — logos scroll continuously and dissolve at both
   edges. The track is cloned once by script.js, so the -50% shift
   lands exactly one set along and the loop is seamless.
   ================================================================= */

.client-strip {
  padding: 34px 0 0;
}

/* Centred eyebrow above the moving logos. Same treatment as the
   closing client band so the two read as one system. */
.client-strip-label {
  margin: 0 0 18px;
  text-align: center;
  color: rgba(168, 186, 210, 0.6);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.marquee {
  position: relative;
  width: 100%;
  overflow: hidden;
  /* logos fade out on one side and fade in on the other */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  animation: marquee 41s linear infinite;
}

.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track {
  animation-play-state: paused;
}

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

/* Fixed-size chips are what actually makes every logo read as the same
   size — the image is capped on BOTH axes and centred, so a wide
   wordmark and a square mark occupy identical space. */
.marquee-item {
  flex: 0 0 auto;
  width: 220px;
  height: 94px;
  /* margin, not flex gap: gap would add a trailing space to only one
     copy of the list and the loop would visibly jump */
  margin-right: 18px;
  display: grid;
  place-items: center;
  padding: 14px 15px;
  border-radius: 18px;
  /* Each client keeps their own background colour — set per item via
     --tile in index.html. Sample it from the logo's own artwork. */
  background: var(--tile, rgba(255, 255, 255, 0.04));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.07),
    0 14px 30px rgba(3, 8, 16, 0.42);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.marquee-item:hover {
  transform: translateY(-4px);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.14),
    0 20px 40px rgba(3, 8, 16, 0.55);
}

.marquee-item a {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
}

.marquee-item a:focus-visible {
  outline: 2px solid var(--primary-2);
  outline-offset: 4px;
  border-radius: 12px;
}

.marquee-item img {
  max-width: 100%;
  max-height: 42px;
  width: auto;
  object-fit: contain;
}

/* =================================================================
   CLOSING CLIENT BAND — the same logos as the marquee, small and
   static, sitting above the footer on every page. At the foot of a
   page this is reassurance rather than a headline, so it does not
   move and it does not compete with the CTA above it.
   ================================================================= */

.client-band {
  padding: 2px 0 22px;
}

.client-band .container {
  display: grid;
  justify-items: center;
  gap: 11px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.client-band-label {
  margin: 0;
  color: rgba(168, 186, 210, 0.6);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.client-band-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.client-band-list li {
  width: 104px;
  height: 42px;
  display: grid;
  place-items: center;
  padding: 7px 9px;
  border-radius: 10px;
  background: var(--tile, rgba(255, 255, 255, 0.04));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.client-band-list li:hover,
.client-band-list li:focus-within {
  transform: translateY(-2px);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.14),
    0 10px 20px rgba(3, 8, 16, 0.45);
}

.client-band-list a {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
}

.client-band-list a:focus-visible {
  outline: 2px solid var(--primary-2);
  outline-offset: 4px;
  border-radius: 10px;
}

.client-band-list img {
  max-width: 100%;
  max-height: 21px;
  width: auto;
  object-fit: contain;
}

@media (max-width: 420px) {
  .client-band-list li {
    width: 94px;
    height: 38px;
  }
}

/* the footer already carries its own top rule; two lines stacked reads
   as a mistake, so the band absorbs it */
.client-band + .site-footer .footer-wrap {
  border-top: 0;
  padding-top: 8px;
}

.section-heading {
  margin-bottom: 38px;
}

.section-heading h2 {
  font-size: clamp(2.3rem, 3vw, 3.4rem);
  max-width: 680px;
}

/* =================================================================
   SERVICES PAGE — shares the team page's drifting-network backdrop
   ================================================================= */

.services-page {
  position: relative;
  overflow: hidden;
  padding-top: 104px;
}

.services-page > .container {
  position: relative;
  z-index: 1;
}

.services-page .page-intro {
  margin-bottom: 54px;
}

/* =================================================================
   TEAM PAGE
   ================================================================= */

.team-page {
  position: relative;
  overflow: hidden;
  padding-top: 112px;
}

.team-page > .container {
  position: relative;
  z-index: 1;
}

.team-intro,
.page-intro {
  text-align: center;
  max-width: 780px;
  margin: 0 auto 66px;
}

.team-intro h1,
.page-intro h1 {
  margin: 0;
  font-size: clamp(2.1rem, 4vw, 3.4rem);
  letter-spacing: -0.055em;
  line-height: 1.06;
  text-wrap: balance;
}

.team-lede,
.page-lede {
  margin: 22px auto 0;
  max-width: 620px;
  color: var(--muted);
  font-size: 1.08rem;
  line-height: 1.75;
  text-wrap: pretty;
}

/* Core team and Advisory network are different kinds of relationship,
   so they get a label each rather than one undifferentiated 2x2. The
   heading parts are the same .group-head / .group-title the services
   page uses — one idea, reused, not a second one invented. */
.team-group {
  max-width: 880px;
  margin: 0 auto;
}

.team-group + .team-group {
  margin-top: 54px;
}

.team-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 26px;
  max-width: 880px;
  margin: 0 auto;
}

.member {
  text-align: center;
  padding: 36px 24px 30px;
  border-radius: 26px;
  border: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.012));
  transition:
    transform 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.35s ease,
    box-shadow 0.35s ease;
}

.member:hover {
  transform: translateY(-8px);
  border-color: rgba(125, 140, 255, 0.45);
  box-shadow: 0 26px 60px rgba(3, 8, 16, 0.5);
}

.member-photo {
  position: relative;
  width: 186px;
  height: 186px;
  margin: 0 auto 24px;
  border-radius: 50%;
  /* the photo scales in slightly after the card, so the two motions
     read as one gesture rather than a single flat fade */
  opacity: 0;
  transform: scale(0.86);
  transition:
    opacity 0.8s ease calc(0.18s + var(--i, 0) * 0.12s),
    transform 0.8s cubic-bezier(0.16, 1, 0.3, 1) calc(0.18s + var(--i, 0) * 0.12s);
}

.member.visible .member-photo {
  opacity: 1;
  transform: none;
}

/* conic gradient behind the photo, turning slowly */
.member-ring {
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    var(--primary),
    var(--primary-2),
    var(--accent),
    var(--primary)
  );
  opacity: 0.75;
  animation: ring-spin 11s linear infinite;
  transition: opacity 0.35s ease, filter 0.35s ease;
}

.member:hover .member-ring {
  opacity: 1;
  filter: drop-shadow(0 0 16px rgba(125, 140, 255, 0.5));
}

@keyframes ring-spin {
  to { transform: rotate(1turn); }
}

.member-photo img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  /* A centred square crop of a full-length portrait lands on the torso,
     not the face. --photo-y, set per member in the HTML, slides the
     crop up so the face sits in the middle of the circle. Portraits
     shot head-and-shoulders need nothing; full-length ones want
     something in the 15-25% range. */
  object-position: 50% var(--photo-y, 50%);
  /* the inset border is what turns the gradient behind into a ring */
  border: 5px solid #0b1626;
  filter: saturate(0.92);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1), filter 0.45s ease;
}

.member:hover .member-photo img {
  transform: scale(1.04);
  filter: saturate(1.06);
}

.member h3 {
  margin: 0 0 7px;
  font-size: 1.42rem;
  letter-spacing: -0.04em;
}

.member-role {
  margin: 0;
  color: var(--primary-2);
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* one line, and only one: what this person actually covers */
.member-expertise {
  margin: 12px auto 0;
  max-width: 28ch;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.55;
  text-wrap: pretty;
}

.card-grid,
.work-grid {
  display: grid;
  gap: 24px;
}

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

/* =================================================================
   SERVICES

   Seven cards on a 12-column grid: two rows of three, then a wide
   seventh that spans the full width and closes the block. Its own
   class rather than .card-grid, so the single-column responsive rule
   for the other grids does not reach it.
   ================================================================= */

/* Three groups, each with its own column count: 3 across for Brand &
   Web, 4 for Software & Systems, 2 for Growth & Support. The differing
   rhythm is what makes the groups read as groups without heavy rules
   between them. */

.service-group + .service-group {
  margin-top: 54px;
}

.group-head {
  margin: 0 0 22px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}

.group-title {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--primary-2);
}

.group-title::before {
  content: "";
  width: 26px;
  height: 2px;
  border-radius: 2px;
  flex: 0 0 auto;
  background: linear-gradient(90deg, var(--primary-2), var(--primary));
}

/* Where the groups are an ordered argument rather than a list, the rule
   is replaced by the step number — the order is the message. */
.group-title.has-num::before {
  display: none;
}

.group-num {
  flex: 0 0 auto;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: rgba(125, 140, 255, 0.95);
}

.group-num::after {
  content: "\2014";
  margin: 0 2px 0 9px;
  color: rgba(165, 195, 255, 0.35);
}

.group-title.has-num + .group-lede {
  margin-left: 0;
}

.group-lede {
  margin: 10px 0 0 38px;
  color: var(--muted);
  font-size: 0.98rem;
  line-height: 1.6;
}

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

.services-grid .service-card {
  grid-column: span 4;
}

.services-grid-four .service-card {
  grid-column: span 3;
}

.services-grid-two .service-card {
  grid-column: span 6;
}

/* A single card closing a group reads better across the full width than
   stranded in a half-empty row. Both classes so it outranks the
   responsive .services-grid .service-card rules further down. */
.services-grid.services-grid-one .service-card {
  grid-column: span 12;
}

.service-card {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: rgba(14, 25, 39, 0.68);
  border: 1px solid var(--line);
  border-radius: 24px;
  padding: 30px 26px 34px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02);
  transition:
    transform 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.3s ease,
    background 0.3s ease,
    box-shadow 0.35s ease;
}

/* Cursor-tracked spotlight. script.js writes --mx / --my; with no
   pointer (touch, keyboard) the fallback centres it, so the card still
   lights up on :focus-within rather than staying flat. */
.service-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  background: radial-gradient(
    260px circle at var(--mx, 50%) var(--my, 40%),
    rgba(108, 226, 209, 0.16),
    rgba(125, 140, 255, 0.08) 45%,
    transparent 70%
  );
  transition: opacity 0.4s ease;
}

.service-card:hover,
.service-card:focus-within {
  transform: translateY(-8px);
  border-color: rgba(108, 226, 209, 0.45);
  background: rgba(17, 30, 48, 0.82);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 22px 50px rgba(4, 10, 20, 0.5);
}

.service-card:hover::before,
.service-card:focus-within::before {
  opacity: 1;
}

/* the oversized glyph bleeding off the bottom-right corner */
.sc-watermark {
  position: absolute;
  right: -34px;
  bottom: -38px;
  z-index: -1;
  width: 178px;
  height: 178px;
  color: var(--primary);
  opacity: 0.07;
  pointer-events: none;
  transition: opacity 0.45s ease, transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  animation: sc-drift 9s ease-in-out infinite;
}

.sc-watermark svg {
  width: 100%;
  height: 100%;
}

.service-card:hover .sc-watermark,
.service-card:focus-within .sc-watermark {
  opacity: 0.16;
  transform: translate(-8px, -10px) rotate(-4deg) scale(1.06);
  color: var(--primary-2);
}

@keyframes sc-drift {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -9px; }
}

.icon-wrap {
  position: relative;
  width: 56px;
  height: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 16px;
  font-size: 1.4rem;
  color: var(--primary-2);
  background: linear-gradient(135deg, rgba(125, 140, 255, 0.18), rgba(108, 226, 209, 0.12));
  margin-bottom: 22px;
  transition:
    transform 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    background 0.35s ease,
    box-shadow 0.35s ease;
}

.icon-wrap svg {
  width: 27px;
  height: 27px;
  overflow: visible;
}

/* the ring that expands out of the tile on hover */
.icon-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(108, 226, 209, 0.55);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.service-card:hover .icon-wrap,
.service-card:focus-within .icon-wrap {
  transform: translateY(-3px) scale(1.06);
  background: linear-gradient(135deg, rgba(125, 140, 255, 0.3), rgba(108, 226, 209, 0.24));
  box-shadow: 0 12px 26px rgba(4, 10, 20, 0.45);
}

.service-card:hover .icon-wrap::after,
.service-card:focus-within .icon-wrap::after {
  opacity: 1;
  animation: sc-ring 1.6s ease-out infinite;
}

@keyframes sc-ring {
  0%   { transform: scale(1);    opacity: 0.7; }
  70%  { transform: scale(1.32); opacity: 0; }
  100% { transform: scale(1.32); opacity: 0; }
}

.service-card h3,
.service-card h4 {
  margin: 0 0 12px;
  font-size: 1.35rem;
  letter-spacing: -0.04em;
  line-height: 1.25;
}

.service-card p {
  margin: 0;
  color: var(--muted);
  line-height: 1.7;
}

/* accent rule along the bottom edge, drawn in from the left on hover */
.sc-underline {
  position: absolute;
  left: 26px;
  right: 26px;
  bottom: 0;
  height: 3px;
  border-radius: 3px 3px 0 0;
  background: linear-gradient(90deg, var(--primary-2), var(--primary));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}

.service-card:hover .sc-underline,
.service-card:focus-within .sc-underline {
  transform: scaleX(1);
}

/* the seventh card runs wide, so the icon sits beside the copy */
.service-card-wide {
  display: flex;
  align-items: center;
  gap: 26px;
  padding: 26px 26px 30px;
}

.service-card-wide .icon-wrap {
  margin-bottom: 0;
  flex: 0 0 auto;
}

.service-card-wide .sc-text {
  min-width: 0;
}

/* side by side needs width; on a phone the icon goes back on top */
@media (max-width: 760px) {
  .service-card-wide {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    padding: 26px 22px 28px;
  }
}

.services-more {
  margin: 30px 0 0;
  text-align: center;
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  color: var(--primary-2);
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color 0.25s ease, border-color 0.25s ease, gap 0.25s ease;
}

.link-arrow svg {
  width: 17px;
  height: 17px;
}

.link-arrow:hover {
  gap: 14px;
  color: var(--text);
  border-color: rgba(108, 226, 209, 0.6);
}

/* the list of what each service actually covers — services page only;
   the home page keeps the one-line version */
.sc-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
}

.sc-list li {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 12px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  transition: border-color 0.3s ease, color 0.3s ease, background 0.3s ease;
}

.service-card:hover .sc-list li,
.service-card:focus-within .sc-list li {
  border-color: rgba(108, 226, 209, 0.32);
  background: rgba(108, 226, 209, 0.07);
  color: var(--text);
}

/* four across gets tight before three across does, so it folds first */
@media (max-width: 1180px) {
  .services-grid-four .service-card {
    grid-column: span 6;
  }
}

@media (max-width: 1040px) {
  .services-grid .service-card {
    grid-column: span 6;
  }
}

@media (max-width: 760px) {
  .services-grid .service-card {
    grid-column: span 12;
  }

  .service-group + .service-group {
    margin-top: 40px;
  }

  .group-lede {
    margin-left: 0;
  }
}

/* the drifting glyph and the pulsing ring are decoration; drop them
   for anyone who has asked the OS to calm animation down */
@media (prefers-reduced-motion: reduce) {
  .sc-watermark {
    animation: none;
  }

  .service-card:hover .icon-wrap::after,
  .service-card:focus-within .icon-wrap::after {
    animation: none;
  }
}

/* =================================================================
   PROCESS RAIL

   One system, two densities. Vertical is the base: the track runs down
   the left edge and each step stacks against it, which is what the
   services page uses at every width because its steps carry a
   paragraph and a list. The home page adds .process-rail-row, which
   lays the same rail out horizontally above 900px for the short
   one-line version.

   Vertical-first is deliberate. It is the layout with no ceiling on
   copy length, so it is the one that should not need overriding.

   The connector is a single element whose fill scales from 0 to 1 on
   reveal; every dot is timed off its own --i so it lights up exactly
   as the fill sweeps past. Add or remove a step and the choreography
   still lands — nothing hardcodes "five" but the row column count.
   ================================================================= */

.process-rail {
  position: relative;
  padding-top: 4px;
}

.process-steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 30px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.process-node {
  position: relative;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  column-gap: 20px;
}

.rail-track {
  position: absolute;
  top: 17px;
  /* runs the full height of the list rather than stopping at the last
     dot: the arrowhead then lands at the very bottom of the last step,
     which is the one that is supposed to keep going */
  bottom: 0;
  left: 12px;
  width: 2px;
  border-radius: 2px;
  background: rgba(165, 195, 255, 0.14);
}

.rail-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, var(--primary), var(--primary-2));
  transform: scaleY(0);
  transform-origin: center top;
  transition: transform 1.7s cubic-bezier(0.16, 1, 0.3, 1) 0.25s;
}

.process-rail.visible .rail-fill {
  transform: scaleY(1);
}

/* one arrowhead at the finish. A chevron between every step is noise;
   a single one at the end reads as direction */
.rail-tip {
  position: absolute;
  left: 50%;
  bottom: -3px;
  width: 11px;
  height: 11px;
  background: var(--primary-2);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  transform: translateX(-50%) rotate(90deg) scale(0);
  transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) 1.85s;
}

.process-rail.visible .rail-tip {
  transform: translateX(-50%) rotate(90deg) scale(1);
}

.node-dot {
  position: relative;
  z-index: 1;
  align-self: start;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid rgba(165, 195, 255, 0.28);
  background: #0a1424;
  /* the fill takes 1.7s from 0.25s, so step i is reached at
     0.25s + 1.7s x (i / 4) */
  transition:
    border-color 0.4s ease,
    box-shadow 0.4s ease,
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(0.25s + var(--i, 0) * 0.42s);
}

.node-dot::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(165, 195, 255, 0.3);
  transition: background 0.4s ease;
  transition-delay: inherit;
}

.process-rail.visible .node-dot {
  border-color: var(--primary-2);
  box-shadow: 0 0 0 5px rgba(108, 226, 209, 0.09);
  transform: scale(1.08);
}

.process-rail.visible .node-dot::after {
  background: var(--primary-2);
}

/* the last step is a retainer, not a milestone — its ring keeps going
   after the line has arrived */
.is-ongoing .node-dot::before {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 1px solid rgba(108, 226, 209, 0.5);
  opacity: 0;
}

.process-rail.visible .is-ongoing .node-dot::before {
  animation: node-pulse 2.8s ease-out 2.1s infinite;
}

@keyframes node-pulse {
  0%   { transform: scale(0.72); opacity: 0.75; }
  70%  { transform: scale(1.35); opacity: 0; }
  100% { transform: scale(1.35); opacity: 0; }
}

.node-num,
.process-node h3,
.process-node p,
.node-list {
  grid-column: 2;
}

.node-num {
  display: block;
  margin-bottom: 6px;
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: rgba(168, 186, 210, 0.55);
  transition: color 0.4s ease;
  transition-delay: calc(0.25s + var(--i, 0) * 0.42s);
}

.process-rail.visible .node-num {
  color: var(--primary-2);
}

.process-node h3 {
  margin: 0 0 10px;
  font-size: 1.25rem;
  letter-spacing: -0.03em;
}

.process-node p {
  margin: 0;
  max-width: 62ch;
  color: var(--muted);
  font-size: 0.96rem;
  line-height: 1.7;
}

/* what actually happens inside a step — services page only */
.node-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 26px;
  max-width: 62ch;
  margin: 16px 0 0;
  padding: 0;
  list-style: none;
}

.node-list li {
  position: relative;
  padding-left: 20px;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.6;
}

.node-list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.62em;
  width: 8px;
  height: 2px;
  border-radius: 2px;
  background: var(--primary-2);
  opacity: 0.75;
}

@media (max-width: 600px) {
  .node-list {
    grid-template-columns: 1fr;
  }
}

/* --- the home page variant: same rail, laid out across ------------- */

@media (min-width: 901px) {
  .process-rail-row .process-steps {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 22px;
  }

  .process-rail-row .process-node {
    display: block;
    text-align: center;
    padding: 0 6px;
  }

  .process-rail-row .node-dot {
    margin: 0 auto 20px;
  }

  .process-rail-row .process-node p {
    max-width: none;
    font-size: 0.92rem;
  }

  /* the last dot centre sits at 90% of the row; the track runs 32px
     past it so the arrowhead clears the dot instead of hiding behind it */
  .process-rail-row .rail-track {
    top: 17px;
    bottom: auto;
    left: 10%;
    right: calc(10% - 32px);
    width: auto;
    height: 2px;
  }

  .process-rail-row .rail-fill {
    background: linear-gradient(90deg, var(--primary), var(--primary-2));
    transform: scaleX(0);
    transform-origin: left center;
  }

  .process-rail-row.visible .rail-fill {
    transform: scaleX(1);
  }

  .process-rail-row .rail-tip {
    top: 50%;
    bottom: auto;
    left: auto;
    right: -3px;
    transform: translateY(-50%) scale(0);
    transform-origin: left center;
  }

  .process-rail-row.visible .rail-tip {
    transform: translateY(-50%) scale(1);
  }
}

/* the sweep is the whole point of the graphic, so reduced motion gets
   the finished state at once rather than a still frame of nothing */
@media (prefers-reduced-motion: reduce) {
  .rail-fill,
  .rail-tip,
  .node-dot,
  .node-num {
    transition-duration: 0.01ms;
    transition-delay: 0ms;
  }

  .process-rail.visible .is-ongoing .node-dot::before {
    animation: none;
  }
}

.section-lede {
  margin: 16px 0 0;
  max-width: 620px;
  color: var(--muted);
  font-size: 1.02rem;
  line-height: 1.7;
}

.work-grid {
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* =================================================================
   WORK CARDS

   The preview is the client's own logo on the client's own brand
   colour, set per card with --tile in index.html. Same treatment as
   the marquee chip, at poster size.

   To use a real screenshot instead, replace the <img> inside
   .project-preview with the screenshot and drop the inline --tile:
   .project-preview img switches to object-fit: cover automatically
   for anything wider than a logo.
   ================================================================= */

.project-card {
  background: rgba(10, 18, 29, 0.62);
  border: 1px solid var(--line);
  border-radius: 22px;
  overflow: hidden;
  transition:
    transform 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.3s ease,
    box-shadow 0.35s ease;
}

.project-card:hover,
.project-card:focus-within {
  transform: translateY(-8px);
  border-color: rgba(108, 226, 209, 0.45);
  box-shadow: 0 24px 52px rgba(4, 10, 20, 0.5);
}

/* the whole card is the link, so the click target is the card and not
   a small line of text inside it */
.project-card > a {
  display: block;
  height: 100%;
}

.project-card > a:focus-visible {
  outline: 2px solid var(--primary-2);
  outline-offset: -4px;
  border-radius: 20px;
}

.project-preview {
  display: grid;
  place-items: center;
  height: 210px;
  padding: 26px 30px;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
  background: var(--tile, rgba(255, 255, 255, 0.04));
}

.project-preview img {
  max-width: 100%;
  max-height: 72px;
  width: auto;
  object-fit: contain;
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

/* A stacked lockup (mark above wordmark) is close to square. Capped at
   the same 72px as the wide, one-line logos it reads as shrunken rather
   than smaller, so it gets its own ceiling. */
.project-preview.is-stacked img {
  max-height: 124px;
}

.project-card:hover .project-preview img,
.project-card:focus-within .project-preview img {
  transform: scale(1.05);
}

.project-info {
  display: grid;
  gap: 8px;
  padding: 20px 20px 22px;
}

.project-name {
  font-size: 1.3rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.25;
  color: var(--text);
}

/* a project can involve more than one service, so the tag is a list */
.project-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 2px 0 0;
  padding: 0;
  list-style: none;
}

.project-tags li {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 600;
  transition: border-color 0.3s ease, background 0.3s ease, color 0.3s ease;
}

.project-card:hover .project-tags li,
.project-card:focus-within .project-tags li {
  border-color: rgba(108, 226, 209, 0.3);
  background: rgba(108, 226, 209, 0.07);
  color: var(--text);
}

.project-visit {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 4px;
  color: var(--primary-2);
  font-size: 0.86rem;
  font-weight: 700;
  transition: gap 0.25s ease;
}

.project-visit svg {
  width: 15px;
  height: 15px;
}

.project-card:hover .project-visit,
.project-card:focus-within .project-visit {
  gap: 11px;
}

.contact-wrap {
  display: grid;
  gap: 18px;
}

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

.contact-form {
  display: grid;
  gap: 18px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: 24px;
  background: rgba(12, 20, 32, 0.8);
  box-shadow: var(--shadow);
}

.contact-form h3 {
  margin: 0 0 8px;
  font-size: 1.6rem;
  letter-spacing: -0.05em;
}

.contact-form label {
  display: grid;
  gap: 8px;
  color: var(--muted);
  font-size: 0.92rem;
}

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

.contact-form input,
.contact-form textarea {
  width: 100%;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 14px;
  background: rgba(255,255,255,0.02);
  color: var(--text);
  padding: 14px 16px;
  font-family: inherit;
  font-size: 1rem;
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.contact-form input:focus,
.contact-form textarea:focus {
  border-color: rgba(125, 140, 255, 0.7);
  box-shadow: 0 0 0 4px rgba(125, 140, 255, 0.12);
}

.contact-form textarea {
  resize: vertical;
  min-height: 120px;
}

.full-width {
  width: 100%;
}

.form-message {
  min-height: 24px;
  margin: 0;
  color: var(--primary-2);
  font-weight: 700;
}

.form-message.error {
  color: #ff9aa5;
}

.admin-page {
  padding-top: 90px;
}

.admin-panel {
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: 28px;
  background: rgba(11, 19, 29, 0.78);
  box-shadow: var(--shadow);
}

.admin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
}

.admin-header h1 {
  margin: 0;
  font-size: clamp(2.2rem, 3vw, 3rem);
  letter-spacing: -0.06em;
}

.admin-toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 18px;
}

.search-field {
  display: grid;
  gap: 8px;
  min-width: min(100%, 320px);
  color: var(--muted);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.search-field input {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  font: inherit;
  color: var(--text);
  background: rgba(255,255,255,0.02);
}

.admin-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 18px;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  min-width: 760px;
}

.admin-table th,
.admin-table td {
  text-align: left;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

.admin-table th {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  background: rgba(255,255,255,0.02);
}

.admin-table td {
  color: var(--text);
  line-height: 1.7;
}

.delete-button {
  border: 1px solid rgba(255, 154, 165, 0.35);
  background: rgba(255, 154, 165, 0.08);
  color: #ffd7dc;
  border-radius: 999px;
  padding: 8px 12px;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.delete-button:hover {
  transform: translateY(-1px);
  opacity: 0.9;
}

.empty-state {
  text-align: center;
  color: var(--muted);
  padding: 28px 18px;
}

/* =================================================================
   CLOSING BANNER — the dot field is a generated PNG; the gradient on
   top of it keeps the headline readable wherever the wave happens to
   be bright.
   ================================================================= */

.cta-banner {
  padding: 10px 0 40px;
}

.wave-plate {
  position: relative;
  overflow: hidden;
  border-radius: 30px;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  background-color: #071120;
  /* static fallback; wave.js swaps this out once the canvas is drawing */
  background-image: url("assets/wave-dots.png");
  background-size: cover;
  background-position: center;
}

.wave-plate.wave-live {
  background-image: none;
}

.wave-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* sits above the wave so the headline never fights the bright ribbons */
.wave-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    rgba(6, 14, 26, 0.9) 0%,
    rgba(6, 14, 26, 0.62) 34%,
    rgba(6, 14, 26, 0.06) 62%,
    rgba(6, 14, 26, 0.24) 100%
  );
}

.wave-content {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 30px;
  padding: 54px 52px;
  min-height: 258px;
}

.wave-content h2 {
  margin: 0;
  max-width: 17ch;
  font-size: clamp(1.9rem, 3.1vw, 3rem);
  letter-spacing: -0.05em;
  line-height: 1.06;
  text-wrap: balance;
}

.wave-lede {
  margin: 16px 0 0;
  max-width: 48ch;
  color: var(--muted);
  font-size: 0.98rem;
  line-height: 1.6;
}

/* The closing banner is a short, wide plate, so the wave stays bright
   right under the copy. Darken the scrim further across before leaning
   on text shadows. Wide layouts only: below 940px the copy stacks and
   the top-weighted scrim further down already covers it, and applying
   this there flattens the wave to black. */
@media (min-width: 941px) {
  .cta-banner .wave-scrim {
    background: linear-gradient(
      100deg,
      rgba(6, 14, 26, 0.95) 0%,
      rgba(6, 14, 26, 0.84) 38%,
      rgba(6, 14, 26, 0.34) 64%,
      rgba(6, 14, 26, 0.26) 100%
    );
  }
}

.cta-banner .wave-lede {
  margin-top: 12px;
  color: rgba(214, 228, 247, 0.92);
}

.button-light {
  gap: 10px;
  flex: 0 0 auto;
  background: #ffffff;
  color: #0a1524;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 1.05rem 1.7rem;
  box-shadow: 0 14px 30px rgba(3, 8, 16, 0.4);
}

.button-light svg {
  width: 18px;
  height: 18px;
  transition: transform 0.25s ease;
}

.button-light:hover svg {
  transform: translateX(4px);
}

/* Email and phone share one treatment — matched glass pills. */
.button-ghost {
  gap: 11px;
  color: var(--text);
  font-weight: 700;
  letter-spacing: 0.01em;
  padding: 0.95rem 1.5rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.07);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
  transition:
    transform 0.22s ease,
    background 0.22s ease,
    border-color 0.22s ease,
    box-shadow 0.22s ease;
}

.button-ghost svg {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  color: var(--primary-2);
  transition: color 0.22s ease;
}

.button-ghost:hover {
  transform: translateY(-2px);
  background: rgba(255, 255, 255, 0.13);
  border-color: rgba(108, 226, 209, 0.55);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 14px 30px rgba(3, 8, 16, 0.4);
}

.button-ghost:focus-visible {
  outline: 2px solid var(--primary-2);
  outline-offset: 3px;
}

/* =================================================================
   REVEAL BUTTONS — label collapses, value expands, on hover.

   The width morph uses grid-template-columns 1fr -> 0fr, which is the
   only reliable way to animate to a content-sized width. Both spans
   stay in the DOM, so the link text is always available to assistive
   tech and to search engines.
   ================================================================= */

.reveal-btn {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  /* One collapsed width for the set; the expanded width is measured
     per button in script.js so each opens only as far as its own text
     needs. The left edge stays put — the button grows to the right. */
  --rb-min: 186px;
  width: var(--rb-min);
  padding: 0.9rem 1.45rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.07);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
  color: var(--text);
  font-weight: 700;
  transition:
    width 0.48s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.22s ease,
    background 0.22s ease,
    border-color 0.22s ease,
    box-shadow 0.22s ease;
}

.reveal-btn:hover,
.reveal-btn:focus-visible {
  width: var(--rb-open, 282px);
  transform: translateY(-2px);
  background: rgba(255, 255, 255, 0.13);
  border-color: rgba(108, 226, 209, 0.55);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 14px 30px rgba(3, 8, 16, 0.4);
}

.reveal-btn:focus-visible {
  outline: 2px solid var(--primary-2);
  outline-offset: 3px;
}

.rb-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  color: var(--primary-2);
}

.rb-icon svg {
  width: 18px;
  height: 18px;
}

.rb-swap {
  position: relative;
  display: grid;
  width: 100%;
  align-items: center;
  /* clipped so the longer detail never spills while collapsed */
  overflow: hidden;
}

.rb-swap > span {
  grid-area: 1 / 1;
  white-space: nowrap;
  text-align: left;
}

.rb-label,
.rb-value {
  transition:
    opacity 0.34s ease,
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.rb-value {
  opacity: 0;
  transform: translateY(9px);
  transition-delay: 0s;
}

.rb-label {
  opacity: 1;
  transform: none;
  transition-delay: 0.1s;
}

/* on the way in, the outgoing label leads and the value follows */
.reveal-btn:hover .rb-label,
.reveal-btn:focus-visible .rb-label {
  opacity: 0;
  transform: translateY(-9px);
  transition-delay: 0s;
}

.reveal-btn:hover .rb-value,
.reveal-btn:focus-visible .rb-value {
  opacity: 1;
  transform: none;
  transition-delay: 0.1s;
}

/* No hover on touch: show the detail outright rather than hiding it
   behind a gesture that will never happen. */
@media (hover: none) {
  .reveal-btn { width: var(--rb-open, 282px); }
  .rb-label { opacity: 0; }
  .rb-value { opacity: 1; transform: none; }
}

/* Stacked, and the column is pinned to the expanded width. Without a
   fixed width the column resizes as a button grows, which drags the
   whole set sideways instead of letting one button extend. */
.wave-content .cta-actions {
  flex: 0 0 auto;
  width: 282px;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}

/* =================================================================
   COUNTRY DIAL-CODE PICKER
   ================================================================= */

.contact-form select.is-enhanced {
  /* kept in the DOM as the form value, driven by phone-field.js */
  display: none;
}

.dial-picker {
  position: relative;
  min-width: 0;
}

.dial-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.02);
  color: var(--text);
  padding: 14px 16px;
  font: inherit;
  font-size: 1rem;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.dial-trigger::after {
  content: "";
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-left: auto;
  border-right: 2px solid rgba(168, 186, 210, 0.9);
  border-bottom: 2px solid rgba(168, 186, 210, 0.9);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.25s ease;
}

.dial-trigger[aria-expanded="true"] {
  border-color: rgba(125, 140, 255, 0.7);
  box-shadow: 0 0 0 4px rgba(125, 140, 255, 0.12);
}

.dial-trigger[aria-expanded="true"]::after {
  transform: translateY(2px) rotate(225deg);
}

.dial-current {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dial-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 8px);
  left: 0;
  width: max(100%, 280px);
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(10, 19, 32, 0.98);
  backdrop-filter: blur(14px);
  box-shadow: 0 24px 50px rgba(3, 8, 16, 0.6);
}

.dial-search {
  width: 100%;
  margin-bottom: 8px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
  padding: 10px 12px;
  font: inherit;
  font-size: 0.94rem;
  outline: none;
}

.dial-search:focus {
  border-color: rgba(125, 140, 255, 0.7);
}

.dial-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 246px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.dial-option {
  padding: 9px 12px;
  border-radius: 10px;
  color: var(--text);
  font-size: 0.94rem;
  cursor: pointer;
}

.dial-option.is-active {
  background: rgba(125, 140, 255, 0.18);
}

.dial-option[aria-selected="true"] {
  color: var(--primary-2);
  font-weight: 700;
}

.dial-empty {
  padding: 12px;
  color: var(--muted);
  font-size: 0.9rem;
  text-align: center;
}

.dial-other {
  width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.02);
  color: var(--text);
  padding: 14px 16px;
  font-family: inherit;
  font-size: 1rem;
  outline: none;
}

.dial-other:focus {
  border-color: rgba(125, 140, 255, 0.7);
  box-shadow: 0 0 0 4px rgba(125, 140, 255, 0.12);
}

/* when "Other" is picked the row makes room for the manual code box */
.phone-row:has(.dial-other:not([hidden])) {
  grid-template-columns: minmax(0, 190px) minmax(0, 110px) minmax(0, 1fr);
}

/* =================================================================
   FORM FIELDS
   ================================================================= */

.contact-form .field {
  display: grid;
  gap: 8px;
  color: var(--muted);
  font-size: 0.92rem;
}

.field-label {
  display: flex;
  align-items: center;
  gap: 8px;
}

.field-optional {
  font-style: italic;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(168, 186, 210, 0.7);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 8px;
}

.phone-row {
  display: grid;
  grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
  gap: 12px;
}

.contact-form select {
  width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.02);
  color: var(--text);
  padding: 14px 16px;
  font-family: inherit;
  font-size: 1rem;
  outline: none;
  appearance: none;
  /* chevron drawn in the background so the control matches the inputs
     rather than inheriting the OS select styling */
  background-image: linear-gradient(45deg, transparent 50%, rgba(168, 186, 210, 0.9) 50%),
    linear-gradient(135deg, rgba(168, 186, 210, 0.9) 50%, transparent 50%);
  background-position: calc(100% - 20px) 22px, calc(100% - 14px) 22px;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.contact-form select:focus {
  border-color: rgba(125, 140, 255, 0.7);
  box-shadow: 0 0 0 4px rgba(125, 140, 255, 0.12);
}

.contact-form select option {
  background: #0d1a2b;
  color: var(--text);
}

.field-msg {
  display: none;
  color: #ff9aa5;
  font-size: 0.82rem;
  line-height: 1.45;
}

/* =================================================================
   MULTI-SELECT ("What do you need?")

   Deliberately built from the same parts as .dial-picker above so the
   two custom controls in this form look like one idea, not two. The
   panel is a plain checkbox list, which is what makes picking several
   things obvious — a native <select multiple> hides that behind
   ctrl-click, and renders as an unlabelled scroll box on phones.
   ================================================================= */

.multiselect {
  position: relative;
  min-width: 0;
}

/* an explicit display beats the browser's own [hidden] rule, so the
   two states have to be spelled out */
.multiselect-trigger[hidden],
.multiselect-panel[hidden] {
  display: none;
}

.multiselect-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.02);
  color: var(--text);
  padding: 14px 16px;
  font: inherit;
  font-size: 1rem;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.multiselect-trigger::after {
  content: "";
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-left: auto;
  border-right: 2px solid rgba(168, 186, 210, 0.9);
  border-bottom: 2px solid rgba(168, 186, 210, 0.9);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.25s ease;
}

.multiselect-trigger[aria-expanded="true"] {
  border-color: rgba(125, 140, 255, 0.7);
  box-shadow: 0 0 0 4px rgba(125, 140, 255, 0.12);
}

.multiselect-trigger[aria-expanded="true"]::after {
  transform: translateY(2px) rotate(225deg);
}

.multiselect-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.multiselect-value.is-placeholder {
  color: rgba(168, 186, 210, 0.75);
}

.multiselect-panel {
  width: 100%;
  margin-top: 2px;
  max-height: 290px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(10, 19, 32, 0.98);
  backdrop-filter: blur(14px);
  box-shadow: 0 24px 50px rgba(3, 8, 16, 0.6);
}

/* .contact-form label is a grid; these rows are not */
.contact-form .multiselect-option {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: 10px;
  color: var(--text);
  font-size: 0.94rem;
  cursor: pointer;
  transition: background 0.15s ease;
}

.contact-form .multiselect-option:hover {
  background: rgba(125, 140, 255, 0.14);
}

/* The form's input rule sizes text boxes, and the browser's own checkbox
   is a white square that shouts on a dark panel. Drawn from scratch: a
   quiet outline until it is ticked, then solid brand teal. */
.contact-form .multiselect-option input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  margin: 0;
  padding: 0;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.26);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.contact-form .multiselect-option input[type="checkbox"]::after {
  content: "";
  width: 10px;
  height: 5px;
  border-left: 2px solid #07111f;
  border-bottom: 2px solid #07111f;
  transform: translateY(-1px) rotate(-45deg) scale(0.4);
  opacity: 0;
  transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.14s ease;
}

.contact-form .multiselect-option input[type="checkbox"]:checked {
  background: var(--primary-2);
  border-color: var(--primary-2);
}

.contact-form .multiselect-option input[type="checkbox"]:checked::after {
  transform: translateY(-1px) rotate(-45deg) scale(1);
  opacity: 1;
}

.contact-form .multiselect-option:has(input:checked) {
  color: var(--primary-2);
  font-weight: 700;
}

.contact-form .multiselect-option input[type="checkbox"]:focus-visible {
  outline: 2px solid rgba(125, 140, 255, 0.9);
  outline-offset: 2px;
}

.field.has-error .multiselect-trigger,
.field.has-error .multiselect-panel {
  border-color: rgba(255, 154, 165, 0.75);
}

/* Only once needs-field.js has taken over does the panel float above the
   page. Before that it sits in the flow as an ordinary checkbox list, so
   the field still works with scripting off. */
.multiselect.is-enhanced .multiselect-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 8px);
  left: 0;
  margin-top: 0;
}

.field.has-error .field-msg {
  display: block;
}

.field.has-error input,
.field.has-error textarea,
.field.has-error select {
  border-color: rgba(255, 154, 165, 0.75);
}

.field.has-error input:focus,
.field.has-error textarea:focus,
.field.has-error select:focus {
  box-shadow: 0 0 0 4px rgba(255, 154, 165, 0.14);
}

.button[disabled] {
  opacity: 0.6;
  cursor: progress;
}

/* =================================================================
   FOOTER
   ================================================================= */

.site-footer {
  padding: 20px 0 42px;
}

.footer-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 18px 26px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
  color: var(--muted);
}

.footer-side {
  display: flex;
  align-items: center;
  gap: 18px;
}

.footer-email {
  color: var(--text);
  font-weight: 700;
  border-bottom: 1px solid rgba(108, 226, 209, 0.35);
  padding-bottom: 2px;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.footer-email:hover {
  color: var(--primary-2);
  border-color: var(--primary-2);
}

.social-nav-sm .social-link {
  width: 34px;
  height: 34px;
}

.social-nav-sm .social-link svg {
  width: 15px;
  height: 15px;
}

.footer-copy {
  margin: 0;
  font-size: 0.92rem;
}

/* current page in the nav */
.main-nav a.is-current {
  color: var(--text);
}

/* =================================================================
   CONTACT PAGE

   Stacked, the banner pushed the enquiry form off the bottom of the
   screen, so the page read as "call or email us" and the form was
   never found. On anything desktop-shaped the two now sit side by
   side and the whole form is visible without scrolling; below that
   the banner goes back on top, where a phone has to scroll anyway.
   ================================================================= */

.contact-page {
  padding-top: 74px;
  padding-bottom: 78px;
}

.contact-page .wave-content {
  min-height: 0;
  padding: 34px 46px;
}

.contact-page .wave-content h2 {
  font-size: clamp(1.7rem, 2.4vw, 2.45rem);
}

@media (min-width: 1000px) {
  .contact-page {
    padding-top: 54px;
    padding-bottom: 40px;
  }

  .contact-wrap {
    /* the form carries two fields per row, so it takes the wider half */
    grid-template-columns: minmax(0, 0.84fr) minmax(0, 1.16fr);
    align-items: stretch;
    gap: 20px;
  }

  /* the banner is a tall column here, not a wide strip */
  .contact-page .wave-content {
    height: 100%;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 30px;
    padding: 44px 42px;
  }

  /* the horizontal scrim was lit for a wide plate; a tall one needs the
     dark end at the top, where the headline sits */
  .contact-page .wave-scrim {
    background: linear-gradient(
      170deg,
      rgba(6, 14, 26, 0.9) 0%,
      rgba(6, 14, 26, 0.62) 42%,
      rgba(6, 14, 26, 0.2) 100%
    );
  }

  /* tightened so the send button clears the fold on a 720px-tall
     laptop viewport — every value here is bought from vertical space */
  .contact-form {
    gap: 11px;
    padding: 22px 26px 22px;
    align-content: start;
  }

  .contact-form h3 {
    margin-bottom: 2px;
    font-size: 1.45rem;
  }

  .contact-page .field-row,
  .contact-page .phone-row {
    gap: 13px;
  }

  .contact-page .contact-form .field {
    gap: 6px;
  }

  .contact-form input,
  .contact-form select,
  .contact-form textarea {
    padding: 11px 15px;
  }

  .contact-form select {
    background-position: calc(100% - 20px) 19px, calc(100% - 14px) 19px;
  }

  .contact-form textarea {
    min-height: 84px;
  }

  .contact-page .form-message {
    min-height: 0;
  }
}

/* more specific than the responsive .wave-content rules further down,
   so the contact banner keeps its own padding at every width */
@media (max-width: 940px) {
  .contact-page .wave-content {
    padding: 32px 30px;
  }
}

@media (max-width: 640px) {
  .contact-page .wave-content {
    padding: 26px 20px;
  }
}

.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 0.7s ease calc(var(--i, 0) * 0.12s),
    transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) calc(var(--i, 0) * 0.12s);
}

.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}

@media (max-width: 940px) {
  .hero {
    --header-h: 165px;
  }

  .card-grid,
  .work-grid {
    grid-template-columns: 1fr;
    display: grid;
  }

  .team-grid {
    grid-template-columns: 1fr;
  }

  .team-group + .team-group {
    margin-top: 40px;
  }

  .team-intro {
    margin-bottom: 44px;
  }

  .nav-wrap {
    flex-wrap: wrap;
    gap: 14px;
  }

  .main-nav {
    order: 3;
    width: 100%;
    justify-content: center;
    flex-wrap: wrap;
  }

  /* the horizontal scrim does not protect narrow layouts, so switch it
     to a top-weighted one where the copy actually sits */
  .wave-scrim {
    background: linear-gradient(
      180deg,
      rgba(6, 14, 26, 0.9) 0%,
      rgba(6, 14, 26, 0.66) 48%,
      rgba(6, 14, 26, 0.3) 100%
    );
  }

  .wave-content {
    flex-direction: column;
    align-items: flex-start;
    gap: 26px;
    padding: 44px 34px;
  }

  .wave-content h2 {
    max-width: 100%;
  }

}

@media (max-width: 640px) {
  .section {
    padding: 85px 0;
  }

  .marquee-item {
    width: 148px;
    height: 76px;
    margin-right: 12px;
    padding: 12px 16px;
  }

  .marquee-item img {
    max-height: 36px;
  }

  .main-nav {
    gap: 10px 14px;
    font-size: 0.78rem;
  }

  /* the aura reads as a wash at phone size — pull it back */
  .hero-aura {
    width: 70vw;
    opacity: 0.4;
  }

  .intro-logo {
    width: 78vw;
  }

  .button {
    width: 100%;
  }

  .cta-row,
  .proof-row,
  .cta-actions {
    display: grid;
    width: 100%;
  }

  .field-row,
  .phone-row {
    grid-template-columns: 1fr;
  }

  .proof-row {
    gap: 16px;
  }

  .proof-row {
    justify-items: center;
  }

  .footer-wrap {
    flex-direction: column;
    text-align: center;
  }

  .footer-side {
    flex-direction: column;
    gap: 14px;
  }

  .wave-content {
    padding: 34px 22px;
  }

  /* let the pills use the full column on a phone rather than a fixed
     282px, which overflows a 320px screen */
  .wave-content .cta-actions,
  .reveal-btn {
    width: 100%;
  }

  .button-light {
    width: auto;
  }

  .cta-actions {
    width: 100%;
  }
}

.hidden {
  display: none !important;
}

.admin-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Login Modal Styles */
.login-modal-overlay {
  padding-top: 100px;
  padding-bottom: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 200px);
}

.login-modal-card {
  width: min(100%, 420px);
  padding: 36px 32px;
  border: 1px solid var(--line);
  border-radius: 28px;
  background: rgba(11, 19, 29, 0.85);
  box-shadow: var(--shadow);
  backdrop-filter: blur(16px);
  animation: fadeIn 0.4s ease;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.login-modal-header {
  text-align: center;
  margin-bottom: 26px;
}

.login-modal-mark-img {
  width: 52px;
  height: 52px;
  object-fit: contain;
  margin: 0 auto 16px;
  display: block;
  filter: drop-shadow(0 8px 22px rgba(29, 78, 216, 0.4));
}

.login-modal-header .brand-mark {
  width: 44px;
  height: 44px;
  margin: 0 auto 16px;
  font-size: 1.2rem;
}

.login-modal-header h2 {
  margin: 0 0 8px;
  font-size: 1.8rem;
  letter-spacing: -0.04em;
}

.login-modal-header p {
  margin: 0;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.5;
}

.login-form {
  display: grid;
  gap: 16px;
}

.login-form label {
  display: grid;
  gap: 6px;
  color: var(--muted);
  font-size: 0.85rem;
}

.login-form input {
  width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
  padding: 13px 16px;
  font-family: inherit;
  font-size: 0.98rem;
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.login-form input:focus {
  border-color: rgba(125, 140, 255, 0.7);
  box-shadow: 0 0 0 4px rgba(125, 140, 255, 0.12);
}

.login-hint {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  text-align: center;
  color: var(--muted);
}

.login-hint code {
  color: var(--primary-2);
  background: rgba(108, 226, 209, 0.1);
  padding: 2px 6px;
  border-radius: 6px;
  font-family: inherit;
}

/* =================================================================
   LEGAL PAGES (privacy policy)
   ================================================================= */

.legal-page {
  padding-top: 104px;
}

.legal-page .page-intro {
  margin-bottom: 44px;
}

.legal-body {
  max-width: 820px;
  margin: 0 auto;
}

.legal-body h2 {
  margin: 38px 0 12px;
  font-size: 1.25rem;
  letter-spacing: -0.03em;
}

.legal-body h2:first-child {
  margin-top: 0;
}

.legal-body p {
  margin: 0 0 14px;
  color: var(--muted);
  line-height: 1.8;
}

.legal-body a {
  color: var(--primary-2);
  font-weight: 600;
}

.form-consent {
  margin: 0;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.6;
}

.form-consent a {
  color: var(--primary-2);
  font-weight: 600;
}

.footer-legal {
  color: var(--muted);
  font-size: 0.85rem;
  transition: color 0.2s ease;
}

.footer-legal:hover {
  color: var(--text);
}

/* =================================================================
   COOKIE CONSENT — banner + preferences dialog, injected by
   consent.js. All three banner actions carry near-equal visual
   weight: a consent where refusing is harder than agreeing is not a
   valid consent. Accept is allowed the primary tint, nothing more.
   ================================================================= */

.consent-banner {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 26px;
  justify-content: space-between;
  width: min(1160px, calc(100% - 32px));
  padding: 16px 20px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: rgba(9, 18, 30, 0.96);
  backdrop-filter: blur(12px);
  box-shadow: 0 20px 60px rgba(3, 8, 16, 0.65);
}

.consent-copy strong {
  display: block;
  margin-bottom: 3px;
  font-size: 0.92rem;
}

.consent-copy p,
.consent-banner p {
  margin: 0;
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.55;
}

.consent-banner a,
.consent-dialog a {
  color: var(--primary-2);
  font-weight: 600;
}

.consent-actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
  flex-wrap: wrap;
}

.consent-btn {
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  padding: 0.6rem 1.05rem;
  border-radius: 999px;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.consent-btn:hover {
  transform: translateY(-1px);
  border-color: rgba(108, 226, 209, 0.5);
  background: rgba(108, 226, 209, 0.1);
}

.consent-btn-primary {
  border-color: transparent;
  background: linear-gradient(135deg, var(--primary), var(--primary-2));
  color: #06101d;
}

.consent-btn-primary:hover {
  background: linear-gradient(135deg, var(--primary), var(--primary-2));
  filter: brightness(1.08);
}

/* ---- the preferences dialog ---- */

.consent-overlay {
  position: fixed;
  inset: 0;
  z-index: 110;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(4, 9, 17, 0.72);
  backdrop-filter: blur(7px);
  /* the overlay scrolls on short screens so the dialog itself never
     needs overflow, which would clip its corner nodes */
  overflow-y: auto;
}

/* THE CONSTELLATION FRAME
   Not a card: the boundary is drawn in the site's network language.
   A glowing node sits on each corner (spans injected by consent.js);
   between them runs a hairline conic-gradient border with one brighter
   segment that slowly travels around the frame. The interior is glass,
   so the dimmed page shows through instead of a solid slab. Browsers
   without @property support simply get the border without the moving
   glow, which still reads correctly. */

@property --cf-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.consent-dialog {
  position: relative;
  width: min(560px, 100%);
  margin: auto 0;
  padding: 30px 30px 26px;
  border: 1px solid transparent;
  border-radius: 14px;
  /* layer 1 fills the inside, layer 2 paints the 1px border */
  background:
    linear-gradient(rgba(10, 19, 32, 0.82), rgba(10, 19, 32, 0.82)) padding-box,
    conic-gradient(
      from var(--cf-angle, 0deg),
      rgba(165, 195, 255, 0.22),
      rgba(108, 226, 209, 0.95) 8%,
      rgba(165, 195, 255, 0.22) 16%,
      rgba(165, 195, 255, 0.22) 55%,
      rgba(125, 140, 255, 0.7) 62%,
      rgba(165, 195, 255, 0.22) 70%
    ) border-box;
  backdrop-filter: blur(18px);
  box-shadow: 0 30px 90px rgba(3, 8, 16, 0.7);
  animation: cf-travel 9s linear infinite;
}

@keyframes cf-travel {
  to { --cf-angle: 360deg; }
}

/* the corner nodes */
.cf-node {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--primary-2);
  box-shadow: 0 0 9px rgba(108, 226, 209, 0.9), 0 0 0 3px rgba(108, 226, 209, 0.12);
  animation: cf-breathe 3.4s ease-in-out infinite;
}

.cf-tl { top: -4px; left: -4px; }
.cf-tr { top: -4px; right: -4px; animation-delay: 0.85s; }
.cf-br { bottom: -4px; right: -4px; animation-delay: 1.7s; }
.cf-bl { bottom: -4px; left: -4px; animation-delay: 2.55s; }

@keyframes cf-breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(0.72); opacity: 0.65; }
}

@media (prefers-reduced-motion: reduce) {
  .consent-dialog { animation: none; }
  .cf-node { animation: none; }
}

.consent-dialog h2 {
  margin: 0 0 8px;
  font-size: 1.35rem;
  letter-spacing: -0.03em;
}

.consent-intro {
  margin: 0 0 20px;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
}

/* Categories on the site's rail: a soft track down the left with one
   node per category, instead of boxes. The essential node is always
   lit; the analytics node follows its toggle. */
.consent-flow {
  position: relative;
  padding-left: 36px;
  margin-bottom: 6px;
}

.consent-rail {
  position: absolute;
  left: 11px;
  top: 10px;
  bottom: 12px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg, rgba(108, 226, 209, 0.55), rgba(125, 140, 255, 0.25));
}

.consent-item {
  position: relative;
  padding: 2px 0 22px;
}

.consent-item:last-child {
  padding-bottom: 4px;
}

.cat-node {
  position: absolute;
  left: -32px;
  top: 4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #0a1424;
  border: 2px solid rgba(165, 195, 255, 0.35);
  transition: border-color 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}

/* lit state: essential always, analytics when its toggle is on */
.consent-item.is-on .cat-node,
.consent-item-optional:has(input:checked) .cat-node {
  border-color: var(--primary-2);
  background: var(--primary-2);
  box-shadow: 0 0 10px rgba(108, 226, 209, 0.8), 0 0 0 4px rgba(108, 226, 209, 0.12);
}

.consent-cat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

.consent-cat-head h3 {
  margin: 0;
  font-size: 1rem;
  letter-spacing: -0.02em;
}

.consent-item p {
  margin: 0;
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.6;
}

.consent-lock {
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--primary-2);
  border: 1px solid rgba(108, 226, 209, 0.4);
  border-radius: 999px;
  padding: 4px 10px;
}

/* toggle switch */
.consent-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}

.consent-toggle input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  cursor: pointer;
}

/* The knob is a node, not a plain disc: a bright core with a soft aura,
   the same visual language as the process-rail dots and the star field.
   Off, it sits dim at the left of a dark track; on, it slides right,
   lights up teal, and its aura breathes — echoing the "ongoing" pulse
   used elsewhere on the site. */
.consent-slider {
  width: 46px;
  height: 24px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  transition: background 0.3s ease, border-color 0.3s ease;
  position: relative;
}

/* the node core */
.consent-slider::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 4px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(237, 245, 255, 0.75);
  transform: translateY(-50%);
  transition:
    left 0.32s cubic-bezier(0.16, 1, 0.3, 1),
    background 0.3s ease,
    box-shadow 0.3s ease;
}

/* the node aura — invisible until the toggle is on */
.consent-slider::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 11px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0.6);
  background: radial-gradient(circle, rgba(108, 226, 209, 0.55), transparent 70%);
  opacity: 0;
  transition: left 0.32s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease;
  pointer-events: none;
}

.consent-toggle input:checked + .consent-slider {
  background: linear-gradient(90deg, rgba(125, 140, 255, 0.3), rgba(108, 226, 209, 0.4));
  border-color: rgba(108, 226, 209, 0.75);
}

.consent-toggle input:checked + .consent-slider::after {
  left: 28px;
  background: var(--primary-2);
  box-shadow: 0 0 10px rgba(108, 226, 209, 0.9);
}

.consent-toggle input:checked + .consent-slider::before {
  left: 35px;
  opacity: 1;
  animation: consent-node-pulse 2.4s ease-in-out infinite;
}

@keyframes consent-node-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(0.7); opacity: 0.35; }
  50%      { transform: translate(-50%, -50%) scale(1.5); opacity: 0.9; }
}

.consent-toggle input:focus-visible + .consent-slider {
  outline: 2px solid var(--primary-2);
  outline-offset: 3px;
}

/* the breathing aura is decoration; hold it still for reduced-motion */
@media (prefers-reduced-motion: reduce) {
  .consent-toggle input:checked + .consent-slider::before {
    animation: none;
  }
}

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

.consent-more {
  margin: 4px 0 18px;
  font-size: 0.84rem;
}

.consent-dialog-actions {
  justify-content: flex-end;
}

@media (max-width: 640px) {
  .consent-banner {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
  }

  .consent-actions {
    justify-content: center;
  }
}

.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin: 6px 0 18px;
  font-size: 0.88rem;
}

.legal-table th,
.legal-table td {
  text-align: left;
  vertical-align: top;
  padding: 10px 14px;
  border: 1px solid var(--line);
  color: var(--muted);
  line-height: 1.6;
}

.legal-table th {
  color: var(--text);
  background: rgba(255, 255, 255, 0.03);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.legal-table code {
  color: var(--primary-2);
}

/* cookie names stay on one line; the description column absorbs the width */
.legal-table td:first-child {
  white-space: nowrap;
}

@media (max-width: 560px) {
  .legal-table td:first-child {
    white-space: normal;
  }
}

.legal-settings-btn {
  font-size: 0.88rem;
  padding: 0.7rem 1.3rem;
}

@media (max-width: 560px) {
  .legal-table,
  .legal-table thead,
  .legal-table tbody,
  .legal-table tr,
  .legal-table th,
  .legal-table td {
    display: block;
  }

  .legal-table thead {
    display: none;
  }

  .legal-table td {
    border-top: 0;
  }

  .legal-table tr td:first-child {
    border-top: 1px solid var(--line);
    background: rgba(255, 255, 255, 0.03);
  }
}

.legal-list {
  margin: 0 0 14px;
  padding-left: 20px;
  color: var(--muted);
  line-height: 1.8;
}

.legal-list li {
  margin-bottom: 10px;
}

.legal-list strong {
  color: var(--text);
}

/* location line in the footer + contact plate */
.footer-copy {
  line-height: 1.75;
}

.footer-copy a[href^='tel:'] {
  white-space: nowrap;
}

.contact-location {
  margin: 14px 0 0;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.7;
}

/* =================================================================
   IN DEVELOPMENT — clients whose site is not live yet.

   The chip still shows, because the work is real and the logo earns
   its place in "Trusted by"; what changes is that it stops being a
   link to a page that would disappoint, and says so plainly instead.
   ================================================================= */

/* --- home marquee chip --- */
/* The note sits BELOW the chip, not on top of the logo — a badge inside
   the tile competes with the wordmark it is describing. The track gets
   padding so the marquee's overflow:hidden doesn't clip it. */
.marquee-track {
  padding-bottom: 24px;
}

.marquee-item {
  position: relative;
}

.marquee-item.is-wip {
  cursor: default;
}

.marquee-item.is-wip::after {
  content: "In development";
  position: absolute;
  top: calc(100% + 7px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.5rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(168, 186, 210, 0.7);
}

/* a chip that goes nowhere should not lift on hover */
.marquee-item.is-wip:hover {
  transform: none;
}

/* --- footer client band --- */
/* the caption hangs below a 42px chip, so the row aligns from the top
   and the list reserves the space rather than clipping it */
.client-band-list {
  align-items: flex-start;
  padding-bottom: 26px;
}

.client-band-list li.is-wip {
  position: relative;
  overflow: visible;
  cursor: default;
}

.client-band-list li.is-wip::after {
  content: "In development";
  position: absolute;
  top: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%);
  width: 104px;
  text-align: center;
  line-height: 1.25;
  font-size: 0.47rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(168, 186, 210, 0.72);
}

/* a chip that goes nowhere should not lift on hover */
.client-band-list li.is-wip:hover,
.client-band-list li.is-wip:focus-within {
  transform: none;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}

/* --- work page card --- */
/* mirrors .project-card > a so a non-linking card keeps the same shape */
.project-card > .project-static {
  display: block;
  height: 100%;
}

.project-card.is-wip:hover,
.project-card.is-wip:focus-within {
  transform: translateY(-4px);
  border-color: rgba(255, 158, 109, 0.38);
}

.project-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  white-space: nowrap;
  color: var(--accent);
  font-size: 0.86rem;
  font-weight: 700;
}


/* =================================================================
   THE DIFFERENCE — the system stack

   The whole repositioning argument in one picture: the website is one
   layer, and the layers under it are where the business runs. Built
   from an ordered list with a rail drawn behind it rather than an
   illustration, so it stays crisp at any size, needs no image, and
   still reads as a plain list if the CSS never arrives.
   ================================================================= */

.difference-wrap {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr);
  gap: 60px;
  align-items: start;
}

.difference-copy .section-lede {
  margin-top: 20px;
  max-width: 46ch;
}

.difference-note {
  margin: 22px 0 0;
  padding-left: 16px;
  border-left: 2px solid rgba(108, 226, 209, 0.45);
  max-width: 42ch;
  color: rgba(205, 222, 245, 0.9);
  font-size: 1rem;
  line-height: 1.6;
}

.stack {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* the line every layer hangs off */
.stack-rail {
  position: absolute;
  left: 11px;
  top: 14px;
  bottom: 14px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(
    180deg,
    rgba(125, 140, 255, 0.15),
    rgba(125, 140, 255, 0.75) 18%,
    rgba(108, 226, 209, 0.75) 82%,
    rgba(108, 226, 209, 0.15)
  );
}

.stack-layer {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 18px;
  padding: 13px 18px 13px 0;
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity 0.6s ease calc(var(--i, 0) * 0.07s),
    transform 0.6s cubic-bezier(0.16, 1, 0.3, 1) calc(var(--i, 0) * 0.07s);
}

.stack.visible .stack-layer {
  opacity: 1;
  transform: none;
}

.stack-marker {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin-top: 1px;
  border-radius: 50%;
  border: 2px solid rgba(108, 226, 209, 0.55);
  background: #0a1524;
  display: grid;
  place-items: center;
}

.stack-marker::after {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary-2);
  box-shadow: 0 0 10px 2px rgba(108, 226, 209, 0.5);
}

/* the customer is the one layer we do not build — outlined, not filled */
.stack-layer.is-entry .stack-marker {
  border-color: rgba(165, 195, 255, 0.4);
}

.stack-layer.is-entry .stack-marker::after {
  background: rgba(168, 186, 210, 0.7);
  box-shadow: none;
}

.stack-layer.is-entry .stack-name {
  color: var(--muted);
}

.stack-body {
  min-width: 0;
}

.stack-name {
  display: block;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
}

.stack-note {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.5;
  text-wrap: pretty;
}

/* .services-more centres itself; here it belongs with the copy */
.difference-more {
  margin-top: 26px;
  text-align: left;
}

@media (max-width: 980px) {
  .difference-wrap {
    grid-template-columns: 1fr;
    gap: 38px;
  }

  .difference-copy .section-lede,
  .difference-note {
    max-width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .stack-layer {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* =================================================================
   WORK PAGE CASE CARDS

   The portfolio has to argue that we build systems, not pages, so each
   card reads sector -> problem -> what was built -> state, rather than
   a bag of feature chips. Scoped to .work-grid-cases so the homepage's
   Selected work grid keeps its existing shape.
   ================================================================= */

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

.work-grid-cases .project-info {
  gap: 0;
  padding: 22px 22px 24px;
  /* the foot is pinned to the bottom so Live / In development sits on
     one line across the row, however tall the copy above it runs */
  display: flex;
  flex-direction: column;
  height: auto;
}

.work-grid-cases .project-card > a,
.work-grid-cases .project-card > .project-static {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.work-grid-cases .project-preview {
  flex: 0 0 auto;
}

.work-grid-cases .project-info {
  flex: 1 1 auto;
}

.project-sector {
  display: block;
  margin-bottom: 7px;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(168, 186, 210, 0.78);
}

.project-block {
  display: block;
  margin-top: 16px;
}

.project-block-label {
  display: block;
  margin-bottom: 6px;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(125, 140, 255, 0.85);
}

.project-block-text {
  display: block;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.55;
  text-wrap: pretty;
}

/* the dot-separated build list: one line of evidence, not twelve chips */
.project-stack {
  color: rgba(205, 222, 245, 0.88);
}

.project-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  /* push it to the bottom of the card */
  margin-top: auto;
  padding-top: 20px;
}

.work-grid-cases .project-status {
  margin-top: 0;
}

/* live and in-development are both deliberate states, so they are told
   apart by colour rather than one of them looking like a warning */
.project-status.is-live {
  color: var(--primary-2);
}

.project-status.is-live::before {
  background: var(--primary-2);
  box-shadow: 0 0 0 4px rgba(108, 226, 209, 0.16);
}

@media (max-width: 1080px) {
  .work-grid-cases {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

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


.project-status::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(255, 158, 109, 0.16);
}

/* =================================================================
   DIGITAL SYSTEMS LANDING PAGE

   Four small blocks, all built from the palette and spacing already in
   use elsewhere. Nothing here is a new visual language — the cards,
   process rail, chips and closing plate on that page are the site's
   existing components.
   ================================================================= */

/* --- the problem: a system diagram that fails -----------------------
   Eight tools laid out as a network on a faint blueprint grid. Every
   neighbour is linked, and every link is broken in the middle — the
   picture says "these should talk to each other and don't" before the
   copy underneath has to. Links are pseudo-elements on each node, so
   they follow the grid at every breakpoint without a single image. */
.tool-map {
  position: relative;
  margin: 44px 0 0;
  padding: 56px 48px;
  border: 1px solid var(--line);
  border-radius: 28px;
  overflow: hidden;
  background:
    radial-gradient(ellipse 60% 70% at 50% 50%, rgba(125, 140, 255, 0.10), transparent 70%),
    rgba(8, 15, 26, 0.72);
}

/* the blueprint: a dot grid that fades out toward the edges */
.tool-map::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(rgba(165, 195, 255, 0.22) 1px, transparent 1.4px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(ellipse 75% 80% at 50% 50%, #000 30%, transparent 85%);
  mask-image: radial-gradient(ellipse 75% 80% at 50% 50%, #000 30%, transparent 85%);
}

.tool-grid {
  --col-gap: 72px;
  --row-gap: 64px;
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--row-gap) var(--col-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.tool-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 22px 14px 20px;
  border: 1px dashed rgba(165, 195, 255, 0.3);
  border-radius: 18px;
  background: rgba(10, 18, 30, 0.92);
  text-align: center;
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity 0.55s ease calc(var(--i, 0) * 0.06s),
    transform 0.55s cubic-bezier(0.16, 1, 0.3, 1) calc(var(--i, 0) * 0.06s);
}

.tool-map.visible .tool-node {
  opacity: 1;
  transform: none;
}

.tool-icon {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  border: 1px solid rgba(165, 195, 255, 0.16);
  background: rgba(125, 140, 255, 0.08);
  color: rgba(168, 186, 210, 0.9);
}

.tool-icon svg {
  width: 20px;
  height: 20px;
}

.tool-name {
  color: rgba(214, 228, 247, 0.92);
  font-size: 0.92rem;
  font-weight: 700;
  line-height: 1.3;
}

/* Links. A solid segment either side and a clear gap in the middle:
   drawn, but not connected. ::after runs right, ::before runs down. */
.tool-node::after,
.tool-node::before {
  content: "";
  position: absolute;
  pointer-events: none;
}

.tool-node::after {
  top: 50%;
  left: 100%;
  width: var(--col-gap);
  height: 2px;
  margin-top: -1px;
  background: linear-gradient(90deg,
    rgba(125, 140, 255, 0.55) 0 34%,
    transparent 34% 66%,
    rgba(108, 226, 209, 0.55) 66% 100%);
}

.tool-node::before {
  top: 100%;
  left: 50%;
  width: 2px;
  height: var(--row-gap);
  margin-left: -1px;
  background: linear-gradient(180deg,
    rgba(125, 140, 255, 0.55) 0 34%,
    transparent 34% 66%,
    rgba(108, 226, 209, 0.55) 66% 100%);
}

/* four across: no link off the right edge, none below the last row */
.tool-node:nth-child(4n)::after,
.tool-node:nth-child(n + 5)::before {
  display: none;
}

@media (max-width: 1040px) {
  .tool-grid {
    --col-gap: 44px;
    --row-gap: 48px;
  }

  .tool-map {
    padding: 44px 32px;
  }
}

/* two across on narrow screens: the links are re-laid for the new grid */
@media (max-width: 760px) {
  .tool-grid {
    --col-gap: 36px;
    --row-gap: 40px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .tool-map {
    padding: 32px 22px;
    border-radius: 22px;
  }

  .tool-node:nth-child(4n)::after,
  .tool-node:nth-child(n + 5)::before {
    display: block;
  }

  .tool-node:nth-child(2n)::after,
  .tool-node:nth-child(n + 7)::before {
    display: none;
  }

  .tool-node {
    padding: 18px 10px 16px;
  }

  .tool-name {
    font-size: 0.86rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tool-node {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

.problem-punch {
  margin: 40px auto 0;
  max-width: 54ch;
  text-align: center;
  font-size: 1.14rem;
  font-weight: 600;
  line-height: 1.6;
  color: var(--text);
  text-wrap: pretty;
}

/* --- examples: three vertical chains ------------------------------- */
.flow-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}

.flow-card {
  padding: 26px 24px 28px;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: rgba(10, 18, 29, 0.62);
}

.flow-label {
  margin: 0 0 20px;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--primary-2);
}

.flow-chain {
  position: relative;
  margin: 0;
  padding: 0 0 0 26px;
  list-style: none;
}

/* one line down the whole chain, rather than a border per step */
.flow-chain::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 10px;
  bottom: 10px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(
    180deg,
    rgba(125, 140, 255, 0.7),
    rgba(108, 226, 209, 0.7)
  );
}

.flow-step {
  position: relative;
  padding: 7px 0;
  color: var(--muted);
  font-size: 0.93rem;
  line-height: 1.45;
}

.flow-step::before {
  content: "";
  position: absolute;
  left: -26px;
  top: 14px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid rgba(108, 226, 209, 0.6);
  background: #0a1524;
}

/* the first step is where the customer arrives, the last is the payoff */
.flow-step:first-child {
  color: var(--text);
  font-weight: 700;
}

.flow-step:last-child::before {
  background: var(--primary-2);
  box-shadow: 0 0 10px 2px rgba(108, 226, 209, 0.45);
}

/* --- the checklist -------------------------------------------------- */
.check-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 40px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.check-list li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1.55;
}

.check-list .check {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin-top: 1px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  border: 1px solid rgba(108, 226, 209, 0.32);
  background: rgba(108, 226, 209, 0.1);
  color: var(--primary-2);
}

.check-list .check svg {
  width: 13px;
  height: 13px;
}

.signs-note {
  margin: 38px 0 0;
  padding-left: 18px;
  border-left: 2px solid rgba(108, 226, 209, 0.45);
  max-width: 62ch;
  color: rgba(205, 222, 245, 0.9);
  font-size: 1rem;
  line-height: 1.65;
}

/* --- why us --------------------------------------------------------- */
.why-wrap {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1fr);
  gap: 56px;
  align-items: start;
}

.why-note {
  margin: 22px 0 0;
  max-width: 44ch;
  color: rgba(205, 222, 245, 0.88);
  font-size: 1rem;
  line-height: 1.6;
}

.why-caps {
  margin-top: 26px;
}

.why-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.why-list li {
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
}

.why-list li:first-child {
  padding-top: 0;
}

.why-list li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.why-name {
  display: block;
  font-size: 1.02rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
}

.why-desc {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.55;
}

/* secondary link inside the closing plate */
.wave-links {
  margin: 16px 0 0;
}

.wave-links a {
  color: var(--primary-2);
  font-weight: 700;
  font-size: 0.95rem;
  border-bottom: 1px solid rgba(108, 226, 209, 0.4);
  padding-bottom: 2px;
  transition: color 0.25s ease, border-color 0.25s ease;
}

.wave-links a:hover {
  color: var(--text);
  border-color: rgba(108, 226, 209, 0.8);
}

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

  .why-wrap {
    grid-template-columns: 1fr;
    gap: 36px;
  }

  .why-note,
  .check-list li {
    max-width: 100%;
  }
}

@media (max-width: 720px) {
  .check-list {
    grid-template-columns: 1fr;
    gap: 14px;
  }

  .problem-punch {
    font-size: 1.05rem;
    text-align: left;
  }
}
