/*
Theme Name: Web Oz
Theme URI: https://web-oz.com
Author: Yair Oz
Author URI: https://web-oz.com
Description: Hand-built custom theme for Web Oz — Yair Oz, webmaster & webcreator. Light warm canvas, electric cobalt accent, Space Grotesk + JetBrains Mono.
Version: 1.0.0
Requires at least: 6.4
Tested up to: 7.0
Requires PHP: 8.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: weboz
*/

/* ============================================================
   1. TOKENS
   ============================================================ */

:root {
  /* colour */
  --canvas: #e9e7e2;
  --paper: #f6f5f2;
  --ink: #161513;
  --ink-90: rgba(22, 21, 19, 0.9);
  --ink-65: rgba(22, 21, 19, 0.65);
  /* 0.62 is the lowest alpha that still clears 4.5:1 on the canvas. */
  --ink-muted: rgba(22, 21, 19, 0.62);
  --ink-20: rgba(22, 21, 19, 0.2);
  --ink-12: rgba(22, 21, 19, 0.12);
  --ink-06: rgba(22, 21, 19, 0.06);
  --accent: #1e3bff;
  --accent-deep: #1430cc;
  --on-accent: #ffffff;

  /* type */
  --font-sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* layout */
  --max: 1440px;
  --gutter: clamp(1.25rem, 4vw, 4.5rem);
  --section-y: clamp(3.75rem, 11vw, 10rem);

  /* motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 0.6s;
}

/* ============================================================
   2. RESET / BASE
   ============================================================ */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

body {
  margin: 0;
  background-color: var(--canvas);
  background-image: radial-gradient(rgba(22, 21, 19, 0.1) 1px, transparent 1.3px);
  background-size: 24px 24px;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

body.is-locked { overflow: hidden; }

img,
svg,
video { display: block; max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 0.95;
  text-wrap: balance;
}

p { margin: 0; }

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

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

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

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

.skip-link {
  position: absolute;
  top: -100px; left: 1rem;
  z-index: 999;
  padding: 0.75rem 1.25rem;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  transition: top 0.2s;
}
.skip-link:focus { top: 1rem; }

/* ============================================================
   3. LAYOUT PRIMITIVES
   ============================================================ */

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

.section { padding-block: var(--section-y); }

.rule {
  height: 1px;
  background: var(--ink-12);
  border: 0;
  margin: 0;
}

/* Eyebrow label — mono, tracked, with a cobalt tick */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.eyebrow::before {
  content: "";
  width: 6px; height: 6px;
  background: var(--accent);
  border-radius: 50%;
  flex: none;
}

/* Section labels can sit quietly at 45%, but the hero's names the job.
   It carries "webmaster & digital asset manager", which is the whole
   repositioning, so it gets weight and contrast the others do not. */
.hero .eyebrow {
  color: var(--ink-65);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
}

.hero .eyebrow::before {
  width: 7px;
  height: 7px;
}

.section-head {  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  margin-bottom: clamp(3rem, 6vw, 5rem);
}

.section-title {
  font-size: clamp(2.25rem, 6vw, 4.75rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  text-transform: lowercase;
  max-width: 18ch;
}

.section-lede {
  max-width: 46ch;
  color: var(--ink-65);
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.1875rem);
  line-height: 1.55;
}

/* ============================================================
   4. BUTTONS
   ============================================================ */

.btn {
  --btn-bg: var(--ink);
  --btn-fg: var(--paper);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.95rem 1.6rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border-radius: 100px;
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.35s var(--ease), color 0.3s var(--ease);
}

.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--accent);
  transform: translateY(101%);
  transition: transform 0.45s var(--ease);
}

.btn:hover { transform: translateY(-2px); color: var(--on-accent); }
.btn:hover::after { transform: translateY(0); }

.btn .btn-arrow {
  transition: transform 0.4s var(--ease);
}
.btn:hover .btn-arrow { transform: translate(3px, -3px); }

.btn--accent { --btn-bg: var(--accent); --btn-fg: var(--on-accent); }
.btn--accent::after { background: var(--ink); }
.btn--accent:hover { color: var(--paper); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink-20);
}
.btn--ghost:hover { color: var(--on-accent); box-shadow: inset 0 0 0 1px transparent; }

.btn--sm { padding: 0.6rem 1.15rem; font-size: 0.8125rem; }

/* Text link with animated underline */
.tlink {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 500;
}
.tlink::after {
  content: "";
  position: absolute;
  left: 0; bottom: -3px;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.45s var(--ease);
}
.tlink:hover::after { transform: scaleX(1); transform-origin: left; }

/* ============================================================
   5. HEADER
   ============================================================ */

.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  transition: transform 0.5s var(--ease), background-color 0.4s, box-shadow 0.4s;
}

.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--canvas) 78%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--ink-12);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
}

.site-header.is-stuck::before { opacity: 1; }
.site-header.is-hidden { transform: translateY(-100%); }

.header-inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  height: clamp(4.5rem, 6vw, 5.5rem);
}

/* Wordmark */
.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.1em;
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.045em;
  text-transform: lowercase;
}

.brand-dot {
  width: 0.32em;
  height: 0.32em;
  border-radius: 50%;
  background: var(--accent);
  transform: translateY(-0.06em);
  transition: transform 0.4s var(--ease);
}

.brand:hover .brand-dot { transform: translateY(-0.06em) scale(1.5); }

/* Desktop nav */
.nav {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 2.5vw, 2.25rem);
}

.nav-link {
  position: relative;
  font-size: 1rem;
  font-weight: 500;
  color: var(--ink-65);
  text-transform: lowercase;
  transition: color 0.3s;
}

.nav-link::after {
  content: "";
  position: absolute;
  left: 0; bottom: -6px;
  width: 100%; height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.4s var(--ease);
}

.nav-link:hover { color: var(--ink); }
.nav-link:hover::after { transform: scaleX(1); transform-origin: left; }

.header-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* Availability pill */
.status {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.85rem;
  border-radius: 100px;
  box-shadow: inset 0 0 0 1px var(--ink-12);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-65);
}

.status-dot {
  position: relative;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #16a34a;
  flex: none;
}

.status-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #16a34a;
  animation: ping 2.4s var(--ease) infinite;
}

@keyframes ping {
  0% { transform: scale(1); opacity: 0.7; }
  70%, 100% { transform: scale(3); opacity: 0; }
}

/* Burger */
.burger {
  display: none;
  position: relative;
  width: 44px; height: 44px;
  margin-right: -10px;
  border-radius: 50%;
}

.burger span {
  position: absolute;
  left: 12px;
  width: 20px; height: 1.5px;
  background: var(--ink);
  transition: transform 0.4s var(--ease), opacity 0.2s;
}
.burger span:nth-child(1) { top: 18px; }
.burger span:nth-child(2) { top: 25px; }

.burger.is-open span:nth-child(1) { transform: translateY(3.5px) rotate(45deg); }
.burger.is-open span:nth-child(2) { transform: translateY(-3.5px) rotate(-45deg); }

/* Mobile drawer */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--canvas);
  padding: clamp(7rem, 18vw, 9rem) var(--gutter) 3rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.7s var(--ease-out);
  visibility: hidden;
}

.drawer.is-open {
  clip-path: inset(0 0 0 0);
  visibility: visible;
}

.drawer-nav {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.drawer-link {
  font-size: clamp(2.25rem, 11vw, 3.5rem);
  font-weight: 500;
  letter-spacing: -0.04em;
  line-height: 1.15;
  text-transform: lowercase;
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}

.drawer.is-open .drawer-link {
  opacity: 1;
  transform: none;
  transition-delay: calc(0.12s + var(--i) * 0.06s);
}

.drawer-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--ink-65);
}

/* ============================================================
   6. HERO
   ============================================================ */

.hero {
  position: relative;
  padding-top: clamp(6.5rem, 11vw, 9rem);
  /* Trimmed so the client strip sits inside the first fold rather than
     just below it. */
  padding-bottom: clamp(1.75rem, 3.5vw, 3rem);
  overflow: hidden;
}

.hero-grid {
  display: grid;
  gap: clamp(1.75rem, 3vw, 2.75rem);
}

.hero-title {
  font-size: clamp(2.3rem, 5.7vw, 5.4rem);
  line-height: 0.88;
  letter-spacing: -0.045em;
  text-transform: lowercase;
  /* Line one now carries the showcase card between words, so it needs room
     for the text plus the card. Shrink either and the line wraps, which
     pushes the CTAs below the fold. */
  max-width: 28ch;
}

/* rotating word */
.rotator {
  display: inline-grid;
  vertical-align: top;
  color: var(--accent);
}

.rotator-word {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(0.35em) rotate(2deg);
  transition: opacity 0.42s var(--ease) 0.1s, transform 0.6s var(--ease);
  white-space: nowrap;
}

.rotator-word.is-active {
  opacity: 1;
  transform: none;
}

/* The outgoing word drops out faster than the incoming one arrives.
   Both sit in the same grid cell, so an even crossfade leaves two
   readable words stacked on each other for a third of a second. */
.rotator-word.is-leaving {
  opacity: 0;
  transform: translateY(-0.35em) rotate(-2deg);
  transition: opacity 0.22s var(--ease), transform 0.5s var(--ease);
}

.hero-foot {
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
  align-items: end;
  grid-template-columns: 1fr;
}

.hero-lede {
  max-width: 44ch;
  font-size: clamp(1.0625rem, 1rem + 0.4vw, 1.3125rem);
  line-height: 1.5;
  color: var(--ink-65);
}

.hero-lede strong { color: var(--ink); font-weight: 500; }

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}



/* ============================================================
   7. CLIENT LOOPS — grayscale client-logo marquee
   ============================================================ */

.loops {
  padding-block: clamp(2rem, 4.5vw, 3.5rem);
  overflow: hidden;
  user-select: none;
}

.loop {
  overflow: hidden;
  white-space: nowrap;
}

.loop-track {
  display: inline-flex;
  align-items: center;
  will-change: transform;
}

.loop-item {
  display: inline-flex;
  align-items: center;
  padding-inline: clamp(1.75rem, 3.5vw, 3.25rem);
}

/* Real client logos — grey at rest, true colours on hover (Zinka-style) */
.loop-logo {
  height: clamp(30px, 3.4vw, 44px);
  width: auto;
  max-width: 190px;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.55;
  transition: filter 0.35s var(--ease), opacity 0.35s var(--ease);
}

.loop-item:hover .loop-logo {
  filter: none;
  opacity: 1;
}

.loop-domain {
  font-family: var(--font-mono);
  font-size: 1rem;
  color: var(--ink-muted);
}
/* ============================================================
   7b. PRELOADER
   ============================================================ */

.loader {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: grid;
  place-items: center;
  background: #0f1017;
  color: var(--paper);
  overflow: hidden;
}

.loader.is-done { pointer-events: none; }

.loader-inner {
  display: grid;
  gap: 0.75rem;
  justify-items: center;
  transition: opacity 0.35s var(--ease), transform 0.45s var(--ease);
}

.loader.is-done .loader-inner {
  opacity: 0;
  transform: translateY(-14px);
}

.loader-brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.12em;
  font-size: clamp(2rem, 6vw, 3.5rem);
  font-weight: 700;
  letter-spacing: -0.045em;
  text-transform: lowercase;
}

.loader-word {
  display: inline-block;
  animation: loader-rise 0.7s var(--ease-out) both;
}

.loader-word:last-child { animation-delay: 0.12s; }

@keyframes loader-rise {
  from { opacity: 0; transform: translateY(0.6em); }
  to   { opacity: 1; transform: none; }
}

.loader-dot {
  width: 0.28em;
  height: 0.28em;
  border-radius: 50%;
  background: var(--accent);
  transform: translateY(-0.06em);
  animation: loader-pop 0.5s 0.3s var(--ease-out) both;
}

@keyframes loader-pop {
  from { transform: translateY(-0.06em) scale(0); }
  60%  { transform: translateY(-0.06em) scale(1.5); }
  to   { transform: translateY(-0.06em) scale(1); }
}

.loader-count {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  color: rgba(246, 245, 242, 0.5);
  font-variant-numeric: tabular-nums;
}

/* The curtain wipes upward to reveal the page */
.loader-curtain {
  position: absolute;
  inset: 0;
  background: transparent;
  z-index: -1;
}

.loader.is-done .loader-curtain,
.loader.is-done {
  animation: loader-wipe 0.75s var(--ease-out) 0.1s both;
}

@keyframes loader-wipe {
  to { clip-path: inset(0 0 100% 0); visibility: hidden; }
}

/* While loading, hold the page still */
body.is-loading { overflow: hidden; }

/* No-JS or reduced motion: never show a blocking overlay */
html:not(.js) .loader { display: none; }

@media (prefers-reduced-motion: reduce) {
  .loader { display: none; }
}

/* ============================================================
   8. SERVICES
   ============================================================ */

.svc-list { border-top: 1px solid var(--ink-12); }

.svc {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: clamp(1rem, 4vw, 3rem);
  align-items: start;
  padding-block: clamp(2rem, 4vw, 3.25rem);
  border-bottom: 1px solid var(--ink-12);
  isolation: isolate;
}

.svc::before {
  content: "";
  position: absolute;
  inset: 0 calc(var(--gutter) * -0.5);
  z-index: -1;
  background: var(--paper);
  opacity: 0;
  transform: scaleY(0.85);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
  border-radius: 6px;
}

.svc:hover::before { opacity: 1; transform: scaleY(1); }

.svc-num {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--ink-muted);
  padding-top: 0.75rem;
  transition: color 0.35s;
}

.svc:hover .svc-num { color: var(--accent); }

.svc-body {
  display: grid;
  gap: clamp(0.75rem, 2vw, 1.25rem);
  grid-template-columns: 1fr;
}

.svc-title {
  font-size: clamp(1.75rem, 4.5vw, 3.25rem);
  line-height: 1;
  letter-spacing: -0.035em;
  text-transform: lowercase;
  transition: transform 0.45s var(--ease);
}

.svc:hover .svc-title { transform: translateX(0.6rem); }

.svc-desc {
  max-width: 52ch;
  color: var(--ink-65);
  line-height: 1.55;
}

.svc-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-top: 0.25rem;
}

.tag {
  padding: 0.3rem 0.7rem;
  border-radius: 100px;
  box-shadow: inset 0 0 0 1px var(--ink-12);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--ink-65);
}

/* ============================================================
   8b. UNDER MANAGEMENT
   ============================================================ */

.stack {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--ink-12);
}

.stack-item {
  display: grid;
  gap: 0.5rem;
  align-content: start;
  padding: clamp(1.25rem, 2.5vw, 1.75rem) 0;
  padding-right: clamp(1rem, 3vw, 2.5rem);
  border-bottom: 1px solid var(--ink-12);
  position: relative;
}

/* Accent tick that grows on hover — cheap, and it makes the list feel alive */
.stack-item::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 0;
  height: 1px;
  background: var(--accent);
  transition: width 0.5s var(--ease);
}

.stack-item:hover::before { width: 100%; }

.stack-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}

.stack-desc {
  max-width: 40ch;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-65);
}

/* ============================================================
   9. WORK
   ============================================================ */

.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(1.5rem, 2.5vw, 2rem);
  min-height: clamp(8.5rem, 10vw, 10rem);
  padding: clamp(1.5rem, 2.5vw, 2rem);
  background: var(--paper);
  border-radius: 10px;
  overflow: hidden;
  isolation: isolate;
  box-shadow: inset 0 0 0 1px transparent;
  transition: transform 0.8s var(--ease), color 0.7s var(--ease), box-shadow 0.7s var(--ease);
}

.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--ink);
  clip-path: circle(0% at 50% 100%);
  transition: clip-path 1.25s var(--ease);
}

.card:hover,
.card.is-live {
  transform: translateY(-3px);
  color: var(--paper);
  box-shadow: inset 0 0 0 1px rgba(246, 245, 242, 0.4);
}

.card:hover::before,
.card.is-live::before { clip-path: circle(150% at 50% 100%); }

.card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.card-sector {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  transition: color 0.7s;
}

.card:hover .card-sector,
.card.is-live .card-sector { color: var(--accent); }

.card-arrow {
  flex: none;
  color: var(--ink-muted);
  transition: transform 0.5s var(--ease), color 0.4s;
}

.card:hover .card-arrow,
.card.is-live .card-arrow {
  transform: translate(4px, -4px);
  color: var(--paper);
}

.card-body { display: grid; gap: 0.6rem; }

.card-title {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
}

.card-domain {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--ink-muted);
  transition: color 0.7s;
  word-break: break-all;
}

.card:hover .card-domain,
.card.is-live .card-domain { color: rgba(246, 245, 242, 0.6); }

.card-note {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-65);
  transition: color 0.7s;
}

.card:hover .card-note,
.card.is-live .card-note { color: rgba(246, 245, 242, 0.75); }

/* wide feature card */
.card--wide { grid-column: span 2; }

@media (max-width: 720px) {
  .card--wide { grid-column: span 1; }
}

.work-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: clamp(2rem, 4vw, 3rem);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

/* ============================================================
   10. APPROACH
   ============================================================ */

.approach {
  background: var(--ink);
  color: var(--paper);
  border-radius: clamp(12px, 2vw, 20px);
}

.approach .section-lede { color: rgba(246, 245, 242, 0.6); }
.approach .eyebrow { color: rgba(246, 245, 242, 0.5); }
.approach .rule { background: rgba(246, 245, 242, 0.14); }

.approach-wrap {
  padding-block: var(--section-y);
  padding-inline: clamp(1.25rem, 4vw, 4.5rem);
}

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid rgba(246, 245, 242, 0.14);
  padding-top: clamp(2.5rem, 5vw, 3.5rem);
}

.step { display: grid; gap: 0.9rem; align-content: start; }

.step-num {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  color: var(--accent);
}

.step-title {
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  letter-spacing: -0.03em;
  text-transform: lowercase;
}

.step-desc {
  color: rgba(246, 245, 242, 0.6);
  font-size: 0.9375rem;
  line-height: 1.6;
}

/* ============================================================
   11. CTA
   ============================================================ */

.cta { text-align: left; }

.cta-title {
  font-size: clamp(2.5rem, 8.5vw, 7rem);
  line-height: 0.9;
  letter-spacing: -0.045em;
  text-transform: lowercase;
  max-width: 16ch;
}

.cta-title a { position: relative; color: var(--accent); }

.cta-title a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0.06em;
  width: 100%; height: 0.055em;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.6s var(--ease);
}

.cta-title a:hover::after { transform: scaleX(1); transform-origin: left; }

.cta-rows {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
  margin-top: clamp(3rem, 6vw, 4.5rem);
  padding-top: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid var(--ink-12);
}

.cta-row { display: grid; gap: 0.5rem; align-content: start; }

.cta-row-label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.cta-row-value {
  font-size: clamp(1.0625rem, 1.6vw, 1.25rem);
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* ============================================================
   12. FOOTER
   ============================================================ */

.site-footer {
  padding-bottom: clamp(1.5rem, 3vw, 2.5rem);
}

.footer-top {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.5rem, 5vw, 4rem);
  padding-block: clamp(3rem, 6vw, 4.5rem);
  border-top: 1px solid var(--ink-12);
}

.footer-brandcol { display: grid; gap: 1rem; align-content: start; max-width: 34ch; }

.footer-tagline {
  color: var(--ink-65);
  font-size: 0.9375rem;
  line-height: 1.55;
}

.footer-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 2rem;
}

.footer-col { display: grid; gap: 0.75rem; align-content: start; }

.footer-col-head {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.footer-link {
  font-size: 0.9375rem;
  color: var(--ink-65);
  transition: color 0.3s;
  width: fit-content;
}

.footer-link:hover { color: var(--accent); }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: clamp(1.5rem, 3vw, 2rem);
  border-top: 1px solid var(--ink-12);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.footer-bottom a:hover { color: var(--accent); }

/* Oversized footer wordmark */
.footer-mark {
  display: block;
  width: 100%;
  margin-top: clamp(2rem, 5vw, 3.5rem);
  font-size: clamp(4rem, 21vw, 20rem);
  font-weight: 700;
  line-height: 0.8;
  letter-spacing: -0.055em;
  text-transform: lowercase;
  color: var(--ink);
  opacity: 0.07;
  text-align: center;
  user-select: none;
  pointer-events: none;
}

/* ============================================================
   12b. PROSE (inner pages / posts)
   ============================================================ */

.prose {
  max-width: 68ch;
  font-size: 1.0625rem;
  line-height: 1.7;
  color: var(--ink-90);
}

.prose > * + * { margin-top: 1.4em; }

.prose h2 {
  font-size: clamp(1.6rem, 3vw, 2.25rem);
  letter-spacing: -0.03em;
  margin-top: 2.2em;
}

.prose h3 {
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  letter-spacing: -0.025em;
  margin-top: 1.8em;
}

.prose a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

.prose a:hover { text-decoration-thickness: 2px; }

.prose ul,
.prose ol { padding-left: 1.25em; }

.prose li + li { margin-top: 0.5em; }

.prose blockquote {
  margin-left: 0;
  padding-left: 1.5rem;
  border-left: 2px solid var(--accent);
  color: var(--ink-65);
  font-size: 1.125em;
}

.prose img,
.prose figure { border-radius: 8px; }

.prose code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  padding: 0.15em 0.4em;
  background: var(--ink-06);
  border-radius: 4px;
}

.prose pre {
  padding: 1.25rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: 8px;
  overflow-x: auto;
}

.prose pre code { background: none; padding: 0; }

.prose hr {
  height: 1px;
  border: 0;
  background: var(--ink-12);
  margin-block: 2.5em;
}

/* Pagination inherits the work-foot rail */
.work-foot .nav-links {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.work-foot .page-numbers {
  color: var(--ink-muted);
  transition: color 0.3s;
}

.work-foot .page-numbers:hover,
.work-foot .page-numbers.current { color: var(--accent); }

/* ============================================================
   13. SCROLL REVEAL
   ============================================================ */

/* The hidden state is gated behind .js so that if the script never loads,
   the content is simply visible rather than permanently transparent. */
.reveal {
  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
  transition-delay: var(--d, 0s);
}

.js .reveal {
  opacity: 0;
  transform: translateY(24px);
}

.js .reveal.is-in { opacity: 1; transform: none; }

/* ============================================================
   14. RESPONSIVE
   ============================================================ */

@media (min-width: 900px) {
  .hero-foot {
    grid-template-columns: minmax(0, 1.15fr) auto;
    gap: 3rem;
  }
  .section-head--split {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    align-items: end;
  }
  .svc-body {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
    gap: clamp(1.5rem, 4vw, 4rem);
  }
  .footer-top { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
  .cta-title { max-width: 14ch; }
}

@media (max-width: 899px) {
  .nav, .header-actions .status { display: none; }
  .burger { display: block; }
  .header-actions .btn { display: none; }
}

@media (max-width: 560px) {
  .hero-meta { grid-template-columns: 1fr 1fr; }
  .btn { padding: 0.85rem 1.35rem; }
}

/* ============================================================
   15. MOTION PREFERENCES
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .rotator-word { position: static; }
  .rotator-word:not(.is-active) { display: none; }
}

:root { --font-serif: "Instrument Serif", Georgia, serif; }

/* ============================================================
   16. PREMIUM PASS — texture, typography, cursor, physics
   ============================================================ */

/* ---- Serif italic accent (Instrument Serif) ---- */

.hero-title em,
.section-title em,
.cta-title em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: 1.06em;
  letter-spacing: -0.015em;
}

/* Rotator words join the serif voice — the moving word becomes the flourish */
.rotator-word {
  font-family: var(--font-serif);
  font-style: italic;
  letter-spacing: -0.01em;
}

/* ---- Masked word reveals ---- */

.split .w {
  display: inline-block;
  clip-path: inset(-0.35em -0.15em -0.22em -0.15em);
}

.split .wi {
  display: inline-block;
  transform: translateY(125%) rotate(4deg);
  transform-origin: 0 100%;
  transition: transform 0.9s var(--ease-out);
  transition-delay: calc(var(--wd, 0) * 55ms);
  will-change: transform;
}

.is-in .wi,
.split.is-in .wi { transform: none; }

/* ---- Section index on eyebrows ---- */

.eyebrow[data-idx]::after {
  content: "/ " attr(data-idx);
  color: var(--ink-20);
  margin-left: 0.35rem;
  letter-spacing: 0.2em;
}

.approach .eyebrow[data-idx]::after { color: rgba(246, 245, 242, 0.25); }

/* ---- Rolling nav links ---- */

.nav-link .roll {
  display: inline-grid;
  overflow: hidden;
}

.nav-link .r1,
.nav-link .r2 {
  grid-area: 1 / 1;
  display: block;
  transition: transform 0.5s var(--ease);
}

.nav-link .r2 { transform: translateY(130%); }
.nav-link:hover .r1 { transform: translateY(-130%); }
.nav-link:hover .r2 { transform: translateY(0); }

/* ---- Custom cursor ---- */

html.cur,
html.cur a,
html.cur button { cursor: none; }

.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0;
  left: 0;
  border-radius: 50%;
  pointer-events: none;
  z-index: 1001;
}

.cursor-dot {
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  background: var(--accent);
  transition: opacity 0.3s;
}

.cursor-ring {
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid var(--ink-muted);
  z-index: 1000;
  transition: opacity 0.3s, border-color 0.3s;
}

html.cur-hover .cursor-ring { border-color: var(--accent); }
html.cur-out .cursor-dot,
html.cur-out .cursor-ring { opacity: 0; }

/* ---- Film grain ---- */

body::after {
  content: "";
  position: fixed;
  inset: -100%;
  z-index: 140;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.035;
  animation: grain 9s steps(8) infinite;
}

@keyframes grain {
  0%, 100% { transform: translate(0, 0); }
  12.5% { transform: translate(-3%, 2%); }
  25% { transform: translate(2%, -3%); }
  37.5% { transform: translate(-2%, -2%); }
  50% { transform: translate(3%, 2%); }
  62.5% { transform: translate(-3%, -1%); }
  75% { transform: translate(1%, 3%); }
  87.5% { transform: translate(2%, 1%); }
}

/* ---- Scroll physics targets ---- */

.hero-grid,
.approach,
.footer-mark { will-change: transform; }

.approach { transform-origin: 50% 20%; }

/* ---- Motion preferences ---- */

@media (prefers-reduced-motion: reduce) {
  body::after { animation: none; }
  .cursor-dot,
  .cursor-ring { display: none; }
  .split .wi { transform: none; transition: none; }
}

/* ============================================================
   17. PRELOADER AURORA
   Drifting gradient blobs behind the counter — cobalt, lilac
   and blush on deep navy-ink, wiped away with the curtain.
   ============================================================ */

.loader::before,
.loader::after {
  content: "";
  position: absolute;
  width: 65vmax;
  height: 65vmax;
  border-radius: 50%;
  filter: blur(70px);
  opacity: 0.55;
  pointer-events: none;
}

.loader::before {
  top: -18vmax;
  left: -12vmax;
  background: radial-gradient(circle at 35% 35%, rgba(30, 59, 255, 0.8), transparent 62%);
  animation: aurora-a 7s var(--ease) infinite alternate;
}

.loader::after {
  bottom: -22vmax;
  right: -14vmax;
  background:
    radial-gradient(circle at 65% 65%, rgba(151, 125, 189, 0.75), transparent 60%),
    radial-gradient(circle at 30% 85%, rgba(243, 196, 201, 0.45), transparent 55%);
  animation: aurora-b 9s var(--ease) infinite alternate;
}

@keyframes aurora-a {
  to { transform: translate(9vmax, 7vmax) scale(1.18); }
}

@keyframes aurora-b {
  to { transform: translate(-7vmax, -9vmax) scale(1.12); }
}

.loader-inner {
  position: relative;
  z-index: 1;
}

/* ============================================================
   18. ABOUT — dark panel, portrait ring, personal stats
   ============================================================ */

.about-panel {
  background: var(--ink);
  color: var(--paper);
  border-radius: clamp(12px, 2vw, 20px);
  padding: clamp(2rem, 5vw, 4.5rem);
  display: grid;
  gap: clamp(2.5rem, 5vw, 3.5rem);
  align-items: center;
}

@media (min-width: 900px) {
  .about-card--intro { grid-template-columns: 1.25fr 0.75fr; }
  .about-card--numbers { grid-template-columns: 0.8fr 1.2fr; align-items: center; }
  .about-card--numbers .about-stats { grid-template-columns: repeat(3, 1fr); }
}

.about-panel .eyebrow { color: rgba(246, 245, 242, 0.5); }
.about-panel .eyebrow[data-idx]::after { color: rgba(246, 245, 242, 0.25); }

.about-body { margin-top: clamp(1.25rem, 2.5vw, 1.75rem); }

.about-body p {
  max-width: 50ch;
  color: rgba(246, 245, 242, 0.88);
  font-size: clamp(1.0625rem, 0.72rem + 0.5vw, 1.1875rem);
  line-height: 1.7;
}

.about-body p + p { margin-top: 1em; }

.about-figure {
  position: relative;
  width: min(300px, 62vw);
  margin-inline: auto;
}

.about-photo {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 50%;
  display: block;
  background: var(--accent);
}

.about-ring {
  position: absolute;
  inset: -16%;
  width: 132%;
  height: 132%;
  animation: ringspin 55s linear infinite;
  transform-origin: 50% 50%;
}

.about-ring text {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  fill: rgba(246, 245, 242, 0.45);
}

@keyframes ringspin {
  to { transform: rotate(360deg); }
}

.about-stats {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-content: center;
}

.about-stat { display: grid; gap: 0.4rem; }

.about-stat-num {
  font-size: clamp(3rem, 5.5vw, 4.75rem);
  font-weight: 600;
  letter-spacing: -0.05em;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
}

.about-stat-label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(246, 245, 242, 0.5);
  max-width: 22ch;
}

/* ============================================================
   19. ZINKA-STYLE ENTRANCE + CLEANER CURSOR + WHATSAPP
   ============================================================ */

/* Hero entrance springs with a slight overshoot */
.js .hero .reveal {
  transform: translateY(38px);
  transition-duration: 0.95s;
  transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.js .hero .reveal.is-in { transform: none; }

.hero-title .wi {
  transition-timing-function: cubic-bezier(0.34, 1.4, 0.64, 1);
}

/* Single-dot cursor — the ring is gone */
.cursor-ring { display: none; }

.cursor-dot {
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  z-index: 1001;
}

html.cur-hover .cursor-dot { opacity: 0.4; }

/* WhatsApp bubble */
.wa-bubble {
  position: fixed;
  right: clamp(1rem, 2.5vw, 1.75rem);
  bottom: clamp(1rem, 2.5vw, 1.75rem);
  z-index: 95;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  /* A light ring keeps it separated from the dark bands too */
  box-shadow:
    0 8px 24px rgba(22, 21, 19, 0.28),
    0 0 0 3px rgba(246, 245, 242, 0.18);
  transition: transform 0.35s var(--ease), background-color 0.3s, box-shadow 0.3s;
}

.wa-bubble:hover {
  transform: translateY(-3px) scale(1.06);
  background: var(--ink);
  color: var(--paper);
  box-shadow:
    0 12px 30px rgba(22, 21, 19, 0.35),
    0 0 0 3px rgba(30, 59, 255, 0.35);
}

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

/* Work cards are also .reveal, whose transition shorthand (declared later)
   was clobbering the hover easing — restore both sets, with the entry
   stagger delay applied only to the entry properties. */
.card.reveal {
  transition:
    opacity 0.8s var(--ease-out),
    transform 0.8s var(--ease),
    color 0.7s var(--ease),
    box-shadow 0.7s var(--ease);
  transition-delay: var(--d, 0s), var(--d, 0s), 0s, 0s;
}

/* ============================================================
   20. COLOUR PASS — cobalt becomes a voice, not a garnish
   Zinka's pattern: headline words carry the brand colour, and
   dark surfaces get a brighter variant so it still pops.
   ============================================================ */

:root { --accent-bright: #6b82ff; }

/* Serif italic accent words go cobalt everywhere */
.hero-title em,
.section-title em,
.cta-title em { color: var(--accent); }

/* On dark surfaces the standard cobalt is too deep — brighten it */
.approach .section-title em,
.about-panel .section-title em { color: var(--accent-bright); }

/* The about title is fully coloured, like the reference's orange headline */
.about-panel .section-title { color: var(--accent-bright); }

/* Service numbers wear the colour permanently, not only on hover */
.svc-num { color: var(--accent); }

/* The giant footer wordmark becomes a cobalt wash */
.footer-mark {
  color: var(--accent);
  opacity: 0.09;
}

/* Managed-stack labels warm up on hover along with their accent line */
.stack-item:hover .stack-label { color: var(--accent); }
.stack-label { transition: color 0.35s var(--ease); }

/* ============================================================
   21. COLOUR PASS II — the cobalt closing band + cobalt wipes
   ============================================================ */

/* Contact becomes a full cobalt band — the page ends on a brand statement */
.cta {
  background: var(--accent);
  color: var(--on-accent);
}

.cta .eyebrow { color: rgba(255, 255, 255, 0.7); }
.cta .eyebrow::before { background: #fff; }
.cta .eyebrow[data-idx]::after { color: rgba(255, 255, 255, 0.4); }

/* Headline: white, with the italic words flipped to ink — black-on-brand */
.cta .cta-title { color: var(--on-accent); }
.cta .cta-title em { color: var(--ink); }
.cta .cta-title a { color: var(--ink); }

.cta .cta-rows { border-top-color: rgba(255, 255, 255, 0.3); }
.cta .cta-row-label { color: rgba(255, 255, 255, 0.65); }

/* Work cards flood cobalt on hover instead of ink */
.card::before { background: var(--accent); }
.card:hover .card-sector,
.card.is-live .card-sector { color: rgba(246, 245, 242, 0.8); }
.card:hover .card-domain,
.card.is-live .card-domain { color: rgba(246, 245, 242, 0.7); }
.card:hover .card-note,
.card.is-live .card-note { color: rgba(246, 245, 242, 0.85); }

/* Approach step numbers use the bright variant on the dark panel */
.approach .step-num { color: var(--accent-bright); }

/* Service tags warm up when their row is hovered */
.tag { transition: box-shadow 0.3s var(--ease), color 0.3s var(--ease); }
.svc:hover .tag {
  box-shadow: inset 0 0 0 1px rgba(30, 59, 255, 0.35);
  color: var(--accent-deep);
}

/* Cobalt scrollbar — small, but it brands every pixel of the visit */
html { scrollbar-color: var(--accent) var(--canvas); }
::-webkit-scrollbar { width: 12px; }
::-webkit-scrollbar-track { background: var(--canvas); }
::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 6px;
  border: 3px solid var(--canvas);
}

/* ============================================================
   22. STICKY "LET'S TALK" CHIP
   Fixed top-left on every page. Deliberately outside <header>
   so the header's hide-on-scroll never takes it with it.
   ============================================================ */

.talk-chip {
  position: fixed;
  top: clamp(0.85rem, 1.4vw, 1.35rem);
  left: clamp(1.25rem, 4vw, 2rem);
  z-index: 101;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.3rem 0.9rem 0.3rem 0.3rem;
  border-radius: 100px;
  background: color-mix(in srgb, var(--canvas) 82%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: inset 0 0 0 1px var(--ink-12);
  transition: box-shadow 0.4s var(--ease), transform 0.4s var(--ease);
}

.talk-chip:hover {
  transform: translateY(-1px);
  box-shadow: inset 0 0 0 1px rgba(30, 59, 255, 0.45);
}

.talk-chip-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  overflow: hidden;
  flex: none;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--ink-12);
}

.talk-chip-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease);
}

.talk-chip:hover .talk-chip-avatar img { transform: scale(1.12); }

.talk-chip-text {
  position: relative;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--accent);
  white-space: nowrap;
}

.talk-chip-text::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.45s var(--ease);
}

.talk-chip:hover .talk-chip-text::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* Clear room so the wordmark never sits under the chip */
@media (min-width: 1100px) {
  .header-inner { padding-left: calc(var(--gutter) + 150px); }
}

@media (max-width: 1099px) {
  .talk-chip-text { display: none; }
  .talk-chip { padding: 0.3rem; }
  .header-inner { padding-left: calc(var(--gutter) + 46px); }
}

/* On the cobalt contact band the chip needs the light treatment */
.wp-admin-bar-showing .talk-chip { top: calc(32px + 1rem); }

/* ============================================================
   23. SCROLL CUE — bent rail with a travelling light
   Ported from the HLZ law-office build, retuned for this
   palette and for LTR reading direction.
   ============================================================ */

.scroll-cue {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  right: clamp(10px, 1.4vw, 20px);
  top: 50%;
  transform: translateY(-50%);
  --cue-text: var(--ink-muted);
  --cue-track: var(--ink-20);
  --cue-light: var(--accent);
  transition: opacity 0.6s var(--ease);
}

/* Once you have started scrolling it has done its job */
body.is-scrolled .scroll-cue { opacity: 0; }

.scroll-cue__svg {
  display: block;
  height: clamp(240px, 36vh, 408px);
  width: auto;
  /* Mirrored on the right so the bend still opens toward the page
     rather than off the edge of the screen. */
  transform: scaleX(-1);
}

.scroll-cue__track,
.scroll-cue__run {
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke; /* hairline-crisp at any height */
}

.scroll-cue__track { stroke: var(--cue-track); }

/* A short cobalt segment glides from top to bottom, following the bend.
   dasharray is one segment plus one gap summing to the whole path
   (pathLength=1), so shifting the offset by exactly one period loops with
   no restart flicker. linear keeps the glide even — an eased curve would
   visibly hitch at the loop seam. */
.scroll-cue__run {
  stroke: var(--cue-light);
  opacity: 0.9;
  stroke-dasharray: 0.14 0.86;
  animation: scrollCueRun 5s linear infinite;
}

@keyframes scrollCueRun {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

/* The label nests inside the bend, between the rail's two vertical runs */
.scroll-cue__text {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  writing-mode: vertical-rl; /* reads downward — LTR, unlike the RTL original */
  font-family: var(--font-mono);
  font-size: clamp(9px, 0.7vw, 10.5px);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--cue-text);
}

/* Below this the gutter is too narrow to hold the rail clear of the text */
@media (max-width: 1023px) {
  .scroll-cue { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-cue__run { animation: none; stroke-dashoffset: 0.55; }
}

/* Reserve a lane for the scroll cue so the hero content never crowds it */
@media (min-width: 1024px) {
  .hero .hero-grid { padding-right: calc(var(--gutter) + 74px); }
}

/* ============================================================
   24. HERO SHOWCASE CARD
   A tilted screenshot sitting inline in the headline, cycling
   through the client work, growing and straightening on hover.
   Sized in em so it scales with the headline at every breakpoint.
   ============================================================ */

.hero-shot {
  display: inline-block;
  vertical-align: middle;
  position: relative;
  z-index: 3;
  width: 3.036em; /* em of the h1 — tracks the headline size */
  height: 1.898em; /* 16:10 */
  margin-inline: 0.28em;
  /* Pull the oversized line box back so the card does not blow the
     headline's leading apart. */
  margin-block: -0.28em;
}

.hero-shot-frame {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  overflow: hidden;
  background: var(--ink);
  box-shadow:
    0 18px 40px -18px rgba(22, 21, 19, 0.45),
    inset 0 0 0 1px rgba(22, 21, 19, 0.12);
  transform: rotate(-5.5deg);
  transform-origin: 50% 50%;
  transition: transform 0.75s var(--ease), box-shadow 0.75s var(--ease);
  will-change: transform;
}

/* Straightens and grows — it lifts over the headline, like the reference */
.hero-shot:hover .hero-shot-frame {
  transform: rotate(0deg) scale(1.24);
  box-shadow:
    0 40px 80px -24px rgba(22, 21, 19, 0.5),
    inset 0 0 0 1px rgba(30, 59, 255, 0.4);
}

.hero-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity 1.3s var(--ease), transform 2.4s var(--ease);
}

.hero-slide.is-active {
  opacity: 1;
  transform: scale(1);
}

.hero-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0; /* top-anchored: show the client's hero */
}

/* Domain tag, only once the card is hovered */
.hero-slide-tag {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 1.1em 0.9em 0.7em;
  background: linear-gradient(to top, rgba(22, 21, 19, 0.92), transparent);
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.145em; /* em of the h1, so it stays small */
  letter-spacing: 0.12em;
  text-transform: lowercase;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}

.hero-shot:hover .hero-slide.is-active .hero-slide-tag { opacity: 1; }

/* On narrow screens it cannot sit inline, so it becomes a full-width
   block that breaks the headline across it — same stacking as the
   reference, rather than being hidden. */
@media (max-width: 767px) {
  .hero-shot {
    display: block;
    width: 80%;
    height: auto;
    aspect-ratio: 16 / 10;
    margin: 0.5em auto 0.42em;
  }

  .hero-shot-frame {
    transform: rotate(-3deg);
    border-radius: 10px;
  }

  /* No hover on touch — keep it settled and readable */
  .hero-shot:hover .hero-shot-frame { transform: rotate(-3deg); }

  .hero-slide-tag { font-size: 0.3em; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-shot-frame { transition: none; }
  .hero-slide { transition: none; }
}

/* ============================================================
   25. APPROACH → CONTACT OVERLAP
   The two boldest blocks on the page used to meet at a flush
   seam, which read as an accident. The dark panel now sits
   deliberately on top of the cobalt band, continuing the
   floating-card language rather than queueing two bands.
   ============================================================ */

#approach {
  position: relative;
  z-index: 1;
}

/* The wipe sits beneath, so the next section can climb over it */
.wipe { z-index: 1; }

/* Sells the layering — without a shadow the overlap reads flat */
.approach {
  box-shadow: 0 50px 90px -50px rgba(22, 21, 19, 0.55);
}

.cta {
  position: relative;
  z-index: 2;
  /* Overlaps the pinned panel, so the cover begins with no seam. */
  margin-top: calc(-1 * clamp(3rem, 6vw, 6rem));
  padding-top: calc(var(--section-y) + clamp(3rem, 6vw, 6rem));
}

/* ============================================================
   26. PINNED FOLD — the contact band scrolls over the approach
   panel instead of pushing it up. Only this pair qualifies:
   the panel fits a viewport, and the cobalt band is the one
   fully opaque section on the page, so nothing shows through.
   ============================================================ */

@media (min-width: 900px) and (min-height: 640px) {
  #approach {
    position: sticky;
    top: 0;
    /* Panel is 834px at 1440 — this keeps it centred when there is
       spare height, and simply pins to the top when there is not. */
    padding-block: max(0px, calc((100vh - 900px) / 2));
  }
}

@media (prefers-reduced-motion: reduce) {
  #approach { position: relative; }
}

/* ============================================================
   27. ABOUT — TWO STACKING CARDS
   Split in two so the pair can pile up on scroll, the way the
   Aurora reference stacks its offer cards. The second card
   sticks lower than the first, so the first stays visible as
   a lip behind it rather than being covered completely.
   ============================================================ */

.about-stack {
  display: grid;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}

.about-card {
  /* Cards are opaque, which is what lets one cleanly cover the other */
  box-shadow: 0 40px 80px -50px rgba(22, 21, 19, 0.6);
}

/* The lift on the second card reads as a separate sheet of paper */
.about-card--numbers { background: #22211e; }

.about-numbers-head { align-self: center; }

@media (min-width: 900px) and (min-height: 640px) {
  .about-card {
    position: sticky;
    /* Cleared by the fixed header */
    top: clamp(5rem, 9vh, 7rem);
  }

  /* Sticks lower, so it slides up over the intro and leaves its edge showing */
  .about-card--numbers { top: clamp(7.5rem, 14vh, 11rem); }
}

@media (prefers-reduced-motion: reduce) {
  .about-card { position: relative; }
}
/* The numbers card was less than half the intro's height, so it read as a
   small panel floating on a big one rather than a stacked deck. Give it
   real presence: taller, with the figures carrying the space. */
@media (min-width: 900px) and (min-height: 640px) {
  .about-card--numbers {
    min-height: clamp(24rem, 68vh, 42rem);
    align-content: center;
  }

  .about-card--numbers .about-stat-num {
    font-size: clamp(3.5rem, 7vw, 6.5rem);
  }

  .about-card--numbers .about-stat-label {
    font-size: 0.75rem;
  }
}

/* ============================================================
   28. ABOUT CARDS — wider, stronger tonal step, arc pattern
   ============================================================ */

/* Wider than the standard shell so the pair reads as near-full-bleed
   panels, the way the reference cards do. */
.about-stack {
  max-width: min(var(--max), 100vw);
  padding-inline: clamp(0.75rem, 1.6vw, 1.75rem);
}

/* A real tonal step between the two. The covering card is the lighter
   of the pair, so the one on top reads as nearer. */
.about-card--intro { background: #141310; }
.about-card--numbers { background: #2e2b25; }

.about-card {
  position: relative;
  /* Keeps the arcs inside the rounded corners. Safe on a sticky element —
     it is an *ancestor's* overflow that breaks sticky, not its own. */
  overflow: hidden;
}

/* Concentric arcs, a shade lighter than the card they sit on */
.about-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  --arc: rgba(246, 245, 242, 0.05);
  background:
    radial-gradient(circle at var(--arc-x) var(--arc-y), transparent 0 34%, var(--arc) 34% 50%, transparent 50.2%),
    radial-gradient(circle at var(--arc-x) var(--arc-y), transparent 0 66%, var(--arc) 66% 82%, transparent 82.2%),
    radial-gradient(circle at var(--arc-x) var(--arc-y), transparent 0 98%, var(--arc) 98% 114%, transparent 114.2%);
}

/* Different origin per card so the pair does not look duplicated */
.about-card--intro::before { --arc-x: 78%; --arc-y: 12%; }
.about-card--numbers::before { --arc-x: 22%; --arc-y: 118%; --arc: rgba(246, 245, 242, 0.055); }

/* Content sits above the pattern */
.about-card > * {
  position: relative;
  z-index: 1;
}

/* Re-assert the pinning: the `position: relative` needed above for the arc
   pattern comes later in the file than the sticky rule and was silently
   cancelling it. Base stays relative (mobile, reduced-motion); desktop pins. */
@media (min-width: 900px) and (min-height: 640px) {
  .about-card {
    position: sticky;
    top: clamp(5rem, 9vh, 7rem);
  }

  .about-card--numbers { top: clamp(7.5rem, 14vh, 11rem); }
}

@media (prefers-reduced-motion: reduce) {
  .about-card { position: relative; }
}

/* ============================================================
   29. SCROLL WORD FILL
   Paragraphs sit dim and brighten a word at a time as they
   travel up the viewport. Driven by the shared rAF ticker.
   ============================================================ */

.fill-on-scroll .fw {
  /* Only the about panel uses this today, and it overrides to 0.8.
     This floor exists so the effect can never render text below AA
     if it is ever pointed at something on the light canvas. */
  opacity: 0.62;
  transition: opacity 0.5s var(--ease);
}

.fill-on-scroll .fw.is-lit { opacity: 1; }

/* On the dark cards the dim state needs to be a touch brighter to stay legible */
.about-card .fill-on-scroll .fw { opacity: 0.8; }
.about-card .fill-on-scroll .fw.is-lit { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .fill-on-scroll .fw { opacity: 1; transition: none; }
}

/* ============================================================
   30. AVAILABILITY BADGE — filled, with a slow shine
   ============================================================ */

.status {
  position: relative;
  overflow: hidden;
  /* Tinted, not filled: this is a state label and nothing here is
     clickable, so it must not carry a button's affordance. */
  background: rgba(30, 59, 255, 0.08);
  color: var(--accent-deep);
  box-shadow: inset 0 0 0 1px rgba(30, 59, 255, 0.22);
}

/* Bright green still reads as "available" against the cobalt */
.status-dot,
.status-dot::after { background: #16a34a; }

/* No sweep and no hover lift — both would imply it can be pressed. The
   only motion is the dot's existing pulse, which reads as "live status". */

/* The dot and label must sit above the glint */
.status > * { position: relative; z-index: 1; }



/* ============================================================
   31. BRAND WIPE — hero into the client strip
   A tall band whose stage pins while the Web Oz mark opens as
   a window onto client work, then floods the screen. Same idea
   as the reference, using our own mark instead of a borrowed one.
   ============================================================ */

.wipe {
  position: relative;
  height: 340vh;
  background: var(--ink);
}

.wipe-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: grid;
  align-content: end;
  justify-items: center;
  padding-bottom: clamp(3rem, 9vh, 7rem);
}

.wipe-art {
  position: absolute;
  inset: 0;
  background-color: var(--canvas);
  background-image: radial-gradient(rgba(22, 21, 19, 0.1) 1px, transparent 1.3px);
  background-size: 24px 24px;
  opacity: var(--fade, 1);
  /* The mark itself, as an alpha window. Size is driven by scroll. */
  --shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2048 2048'%3E%3Ccircle cx='790' cy='1024' r='539' fill='%23fff'/%3E%3Ccircle cx='1590' cy='1024' r='200' fill='%23fff'/%3E%3C/svg%3E");
  -webkit-mask: var(--shape) no-repeat 50% 42% / var(--m, 34%);
  mask: var(--shape) no-repeat 50% 42% / var(--m, 34%);
  will-change: mask-size;
}

.wipe-copy {
  position: relative;
  z-index: 1;
  transition: opacity 0.25s linear;
  padding-inline: var(--gutter);
  text-align: center;
  pointer-events: none;
}

.wipe-title {
  font-size: clamp(2rem, 5.5vw, 5rem);
  line-height: 0.95;
  letter-spacing: -0.04em;
  text-transform: lowercase;
  color: var(--paper);
  max-width: 16ch;
  margin-inline: auto;
  text-shadow: 0 2px 30px rgba(22, 21, 19, 0.55);
  transition: color 0.6s var(--ease), text-shadow 0.6s var(--ease);
}

/* Once the doorway has flooded the frame with canvas the words are sitting
   on light, so ink with a light halo is the only readable pairing. */
.wipe-copy.on-light .wipe-title {
  color: var(--ink);
  text-shadow:
    0 1px 18px rgba(233, 231, 226, 0.95),
    0 0 42px rgba(233, 231, 226, 0.85);
}

.wipe-copy.on-light .wipe-title em { color: var(--accent); }

.wipe-title em {
  color: var(--accent-bright);
  transition: color 0.6s var(--ease);
}

@media (max-width: 767px) {
  /* 130vh left only 30vh of travel once the sticky stage is subtracted,
     so the entire reveal happened inside a couple of flicks. 280vh gives
     it 180vh to play out, which is the desktop pacing scaled for a phone. */
  .wipe { height: 280vh; }
  .wipe-art { background-position: 50% 12%; }
}

@media (prefers-reduced-motion: reduce) {
  .wipe { height: auto; }
  .wipe-stage { position: relative; height: 60vh; }
  .wipe-art { --m: 200%; opacity: 1; }
}

.wipe-logo {
  display: grid;
  place-items: center;
  width: 100%;
  height: clamp(40px, 5vw, 64px);
}

.wipe-logo img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  object-fit: contain;
  /* Logos are dark-on-transparent; flip them to bone for the ink stage */
  filter: brightness(0) invert(1);
  opacity: 0.9;
}

/* ---- The work, seen through the doorway ---------------------------------
   Cards render around 250px wide from 768px sources, so they stay crisp —
   the opposite of the earlier single screenshot stretched to full bleed. */
.wipe-gallery {
  position: absolute;
  /* Deep vertical overscan: the columns travel, and this keeps their ends
     off screen the whole way. */
  inset: -18% 0;
  display: grid;
  grid-template-columns: 0.92fr 1.3fr 0.86fr 1.02fr;
  align-items: start;
  gap: clamp(0.5rem, 0.9vw, 0.85rem);
  padding-inline: clamp(0.5rem, 0.9vw, 0.85rem);
  opacity: 0;
  will-change: transform, opacity;
}

.wipe-col {
  display: grid;
  gap: clamp(0.5rem, 0.9vw, 0.85rem);
  align-content: start;
  will-change: transform;
}

.wipe-gallery img {
  display: block;
  width: 100%;
  /* Matches the screenshots exactly (1200x750), so cover crops nothing */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .wipe-gallery { opacity: 0.9; transform: none; }
}

/* ============================================================
   34. WIPE EXIT — services climbs over the pinned stage
   ============================================================ */

#services {
  position: relative;
  z-index: 2;
  /* Opaque, or the pinned gallery would show straight through it */
  background-color: var(--canvas);
  background-image: radial-gradient(rgba(22, 21, 19, 0.1) 1px, transparent 1.3px);
  background-size: 24px 24px;
}

@media (min-width: 900px) and (min-height: 640px) {
  #services {
    /* Rises over the last stretch of the band while the stage is still pinned */
    margin-top: -100vh;
    padding-top: calc(var(--section-y) + 4vh);
    border-radius: clamp(14px, 2.2vw, 26px) clamp(14px, 2.2vw, 26px) 0 0;
    box-shadow: 0 -34px 70px -40px rgba(22, 21, 19, 0.55);
  }
}
/* Abraham leads the widest column — a slightly stronger presence so the eye
   lands there first, without shouting. */
.wipe-gallery img.is-featured {
  box-shadow:
    0 0 0 2px rgba(30, 59, 255, 0.55),
    0 22px 44px -22px rgba(22, 21, 19, 0.6);
}

@media (max-width: 1023px) {
  .wipe-gallery { grid-template-columns: 1fr 1.22fr; }
  .wipe-col:nth-child(n + 3) { display: none; }
}
/* ============================================================
   35. CONTACT WAVE
   Line-ribbon across the base of the cobalt band. Each line
   draws itself in on entrance, then breathes slowly.
   ============================================================ */

.cta { overflow: hidden; }

.wave-wrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  pointer-events: none;
  z-index: 0;
}

.wave {
  display: block;
  /* Overhangs its box on both sides so the scroll drift never
     drags a cut edge into view. */
  width: 134%;
  margin-left: -17%;
  height: clamp(180px, 30vh, 360px);
  /* Grows upward from the base rather than about its middle */
  transform-origin: 50% 100%;
}

/* Starting pose for the scroll drift. Scripted only: without JS nothing
   would ever animate it back out of the squash. */
.js .wave {
  transform: translate3d(7%, 0, 0) scaleY(0.8);
  will-change: transform;
}

.wave path {
  fill: none;
  stroke: rgba(255, 255, 255, 0.4);
  stroke-width: 1;
  /* Keeps every line hairline-thin however the SVG is stretched */
  vector-effect: non-scaling-stroke;
  /* pathLength=1 makes the draw-on independent of real path length */
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 1.8s var(--ease-out);
  transition-delay: calc(var(--i) * 55ms);
}

.wave-wrap.is-in path { stroke-dashoffset: 0; }

/* Slow breathing once drawn, staggered so the ribbon undulates */
.wave-wrap.is-in path {
  animation: waveBreathe 9s ease-in-out infinite;
  animation-delay: calc(var(--i) * -0.42s);
}

@keyframes waveBreathe {
  50% { transform: translateY(-9px); }
}

/* The contact content must sit above the ribbon */
.cta .shell { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .wave path { stroke-dashoffset: 0; transition: none; }
  .wave-wrap.is-in path { animation: none; }
  /* The scroll driver is skipped too, so leave it at rest, not squashed. */
  .js .wave { transform: none; }
}

/* ============================================================
   36. WORK: SWIPE ON MOBILE
   Six cards stacked one per screen made the fold a long scroll
   with nothing new in it. On phones the grid becomes a single
   snapping row you swipe sideways instead.
   ============================================================ */

@media (max-width: 767px) {
  .work-grid {
    display: flex;
    gap: 0.85rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    /* Bleed to both edges so the row reads as continuing off-screen */
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    padding-bottom: 0.75rem;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .work-grid::-webkit-scrollbar { display: none; }

  .work-grid .card {
    flex: 0 0 78%;
    scroll-snap-align: start;
    min-height: 13rem;
    gap: 1.75rem;
  }

  /* One wide card in a swipe row would break the snap rhythm */
  .work-grid .card--wide { grid-column: auto; }

  .work-foot {
    margin-top: 1.5rem;
    /* The row already says "there is more"; the hint would be noise */
    gap: 0.75rem;
  }
}

/* ============================================================
   37. WORK ON TOUCH
   No hover on a phone, so position in the swipe row carries the
   state instead: whichever card sits nearest the middle of the
   screen wears the same treatment hover gives on desktop.
   ============================================================ */

/* Desktop keeps hover; the rail is a swipe affordance only. */
.work-rail { display: none; }

@media (max-width: 767px) {
  .work-rail {
    display: block;
    position: relative;
    height: 2px;
    margin-top: 1.25rem;
    background: var(--ink-12);
    border-radius: 2px;
    overflow: hidden;
  }

  .work-rail-fill {
    position: absolute;
    inset: 0;
    background: var(--accent);
    border-radius: inherit;
    /* --p is the 0..1 swipe position, set by the scroll driver */
    transform: scaleX(var(--p, 0));
    transform-origin: left center;
  }

  /* The lift reads as a wobble mid-swipe, so the live card holds still
     and lets the colour flood do the work. */
  .work-grid .card.is-live { transform: none; }

  /* Touch has no hover, but it does have press. Immediate, and it
     springs back if the finger lifts without following the link. */
  .work-grid .card:active { transform: scale(0.985); }
  .work-grid .card { transition: transform 0.25s var(--ease), color 0.7s var(--ease), box-shadow 0.7s var(--ease); }
}

@media (prefers-reduced-motion: reduce) {
  .work-rail-fill { transition: none; }
}

/* ============================================================
   38. SERVICES AS A LIST
   Six stacked blocks of number + title + prose + tags ran to
   1134px of near-identical shape. The names now carry the fold
   and the detail for the active one sits beside them.
   ============================================================ */

.svc-split { border-top: 1px solid var(--ink-12); }

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


.svc-pick {
  display: flex;
  align-items: baseline;
  gap: clamp(1rem, 2.5vw, 2.5rem);
  width: 100%;
  padding: clamp(0.75rem, 1.4vw, 1.1rem) 0;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.svc-num {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  color: var(--accent);
  transition: opacity 0.4s var(--ease);
}

.svc-name {
  font-size: clamp(2rem, 5vw, 4.25rem);
  line-height: 1;
  letter-spacing: -0.04em;
  text-transform: lowercase;
  /* Inactive names step back so the active one reads as chosen */
  color: var(--ink-20);
  transition: color 0.45s var(--ease), transform 0.45s var(--ease);
}

.svc-item.is-active .svc-name { color: var(--ink); }
.svc-pick:hover .svc-name { color: var(--ink); }
.svc-item:not(.is-active) .svc-num { opacity: 0.35; }

.svc-pick:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 6px;
}

.svc-desc {
  max-width: 44ch;
  color: var(--ink-90);
  font-size: clamp(1rem, 0.86rem + 0.32vw, 1.125rem);
  line-height: 1.6;
}

.svc-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.25rem;
  list-style: none;
  padding: 0;
}

/* ---------- desktop: the panel lives in the second column ---------- */
@media (min-width: 900px) {
  .svc-split { position: relative; }

  /* Keeps the names clear of the panel column */
  .svc-nav { padding-right: 44%; }

  /* Positioned against .svc-split, not the row, so every panel lands in
     the same place at the top of the second column. */
  .svc-panel {
    position: absolute;
    top: clamp(0.5rem, 1.1vw, 1.1rem);
    right: 0;
    width: 38%;
    opacity: 0;
    pointer-events: none;
    transform: translateY(0.6rem);
    transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
  }

  .svc-item.is-active .svc-panel {
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }

  .svc-item.is-active .svc-name { transform: translateX(0.5rem); }
}

/* ---------- touch: an accordion under the name ---------- */
@media (max-width: 899px) {
  .svc-item { border-bottom: 1px solid var(--ink-12); }

  .svc-panel {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.45s var(--ease), opacity 0.35s var(--ease);
  }

  .svc-item.is-active .svc-panel {
    grid-template-rows: 1fr;
    opacity: 1;
  }

  /* min-height:0 lets the 0fr row actually collapse */
  .svc-panel-inner {
    min-height: 0;
    overflow: hidden;
    padding-bottom: 1.25rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .svc-name,
  .svc-panel { transition: none; }
}

/* ============================================================
   39. SERVICE SHOT
   A real client site for the active service. It wipes up as the
   panel takes over, and the image settles out of a slight zoom,
   so the swap has some weight rather than just a fade.
   ============================================================ */

.svc-shot {
  margin: 0 0 1.15rem;
  border-radius: 10px;
  overflow: hidden;
  background: var(--paper);
  box-shadow: 0 24px 48px -30px rgba(22, 21, 19, 0.5);
  /* Closed: the wipe reveals it from the bottom up */
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.75s var(--ease);
}

.svc-shot img {
  display: block;
  width: 100%;
  /* Matches the screenshots, so nothing is cropped */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  transform: scale(1.07);
  transition: transform 1.2s var(--ease);
}

.svc-item.is-active .svc-shot { clip-path: inset(0 0 0 0); }
.svc-item.is-active .svc-shot img { transform: none; }

@media (max-width: 899px) {
  /* The accordion already clips; a second wipe reads as a stutter. */
  .svc-shot { clip-path: none; }
  .svc-shot img { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .svc-shot { clip-path: none; transition: none; }
  .svc-shot img { transform: none; transition: none; }
}

/* ============================================================
   40. WHO THIS SERVICE IS FOR
   A logo row under the copy. The screenshot says what the work
   looks like; the logos say how many businesses it covers.
   ============================================================ */

.svc-clients {
  margin-top: 1.15rem;
  padding-top: 1rem;
  border-top: 1px solid var(--ink-12);
}

.svc-clients-label {
  display: block;
  margin-bottom: 0.85rem;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.svc-logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 1.6rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.svc-logos img {
  display: block;
  /* Logos vary wildly in aspect, so cap the height and let width follow */
  height: clamp(18px, 2vw, 24px);
  width: auto;
  max-width: 90px;
  object-fit: contain;
  /* The strip is supporting evidence, not the headline */
  opacity: 0.55;
  filter: grayscale(1);
  transition: opacity 0.4s var(--ease), filter 0.4s var(--ease);
}

.svc-item.is-active .svc-logos img { opacity: 0.75; }
.svc-logos img:hover { opacity: 1; filter: none; }

/* ============================================================
   41. OPS ANIMATION
   Digital operations is the one service with nothing to
   photograph: it is work that happens while nobody looks. So it
   gets an instrument instead of a screenshot. The mark holds
   still, checks pulse out of it, two watchers orbit, and a row
   of ticks runs its cycle underneath.
   ============================================================ */

.svc-shot--anim { background: var(--ink); }

.ops {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
}

/* --- uptime checks --- */
.ops-pulse {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  transform-origin: 400px 250px;
  opacity: 0;
  animation: opsPulse 5.1s var(--ease-out) infinite;
  animation-delay: var(--d, 0s);
}

@keyframes opsPulse {
  0%   { transform: scale(0.72); opacity: 0; }
  12%  { opacity: 0.55; }
  100% { transform: scale(3.05); opacity: 0; }
}

/* --- the paths they travel --- */
.ops-track {
  fill: none;
  stroke: rgba(246, 245, 242, 0.16);
  stroke-width: 1;
  stroke-dasharray: 3 7;
}

.ops-orbit { transform-origin: 400px 250px; }
.ops-orbit--slow { animation: opsSpin 26s linear infinite; }
.ops-orbit--fast { animation: opsSpin 16s linear infinite reverse; }

@keyframes opsSpin {
  to { transform: rotate(360deg); }
}

.ops-node {
  fill: var(--accent);
  animation: opsBreathe 3.4s var(--ease) infinite;
}

.ops-node--sm { fill: var(--paper); opacity: 0.55; animation-duration: 4.6s; }

@keyframes opsBreathe {
  50% { opacity: 0.45; }
}

/* --- the mark, deliberately the only still thing --- */
.ops-mark {
  fill: none;
  stroke: var(--paper);
  stroke-width: 26;
}

.ops-dot { fill: var(--accent); }

/* --- backups and updates working through their queue --- */
.ops-tick {
  fill: var(--paper);
  opacity: 0.32;
  animation: opsTick 4.2s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.18s);
}

@keyframes opsTick {
  0%, 62%, 100% { fill: var(--paper); opacity: 0.32; }
  18%, 40%      { fill: var(--accent); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .ops-pulse,
  .ops-orbit,
  .ops-node,
  .ops-tick { animation: none; }
  .ops-pulse { opacity: 0.28; }
  .ops-tick:nth-child(-n + 3) { fill: var(--accent); opacity: 1; }
}

/* The new background layers */
.ops-glow {
  /* Breathes with the checks, so the panel feels powered rather than printed */
  animation: opsGlow 5.1s var(--ease) infinite;
  transform-origin: 400px 250px;
}

@keyframes opsGlow {
  0%, 100% { opacity: 0.75; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.04); }
}

.ops-guide {
  fill: none;
  stroke: rgba(246, 245, 242, 0.07);
  stroke-width: 1;
}

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

/* ============================================================
   42. SERVICE ANIMATIONS (build / store / flow / mail / perf)
   Each has its own ground so the panels vary service to service.
   Performance: only the active panel is on screen, so every
   animation whose item is not active is paused. At most one runs
   at a time, and on a closed mobile accordion, none.
   ============================================================ */

.svc-anim-svg { display: block; width: 100%; aspect-ratio: 16 / 10; }

.svc-anim--build { background: var(--paper); }
.svc-anim--store { background: var(--canvas); }
.svc-anim--flow  { background: var(--ink); }
.svc-anim--mail  { background: var(--paper); }
.svc-anim--perf  { background: var(--ink); }

/* The optimisation: pause anything not currently shown. */
.svc-item:not(.is-active) .svc-shot--anim * { animation-play-state: paused !important; }

/* ---- web creation: blocks assembling ---- */
.build-b {
  fill: var(--ink);
  fill-opacity: 0.12;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: buildIn 5s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.22s);
}
.build-b--hero { fill: var(--accent); fill-opacity: 0.85; }

@keyframes buildIn {
  0%        { opacity: 0; transform: translateY(16px); }
  10%, 84%  { opacity: 1; transform: translateY(0); }
  100%      { opacity: 0; transform: translateY(-8px); }
}

/* ---- online stores: the belt and the checkout pop ---- */
.store-belt { animation: storeBelt 3.4s linear infinite; }
@keyframes storeBelt { to { transform: translateX(-128px); } }

.store-check {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: storeCheck 2.6s var(--ease) infinite;
}
@keyframes storeCheck {
  0%, 68%  { opacity: 0; transform: scale(0.4); }
  78%      { opacity: 1; transform: scale(1.12); }
  88%      { opacity: 1; transform: scale(1); }
  100%     { opacity: 0; transform: scale(1); }
}

/* ---- automations: signal running the wires ---- */
.flow-edge { fill: none; stroke: rgba(246, 245, 242, 0.12); stroke-width: 2; }
.flow-sig {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 14 86;
  animation: flowSig 2.6s linear infinite;
}
.flow-sig--b { animation-delay: 1.3s; }
@keyframes flowSig { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }

.flow-node {
  fill: var(--ink);
  stroke: var(--paper);
  stroke-opacity: 0.4;
  stroke-width: 2;
  animation: flowNode 2.6s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.3s);
}
.flow-node--hub { fill: var(--accent); stroke: none; animation: none; }
@keyframes flowNode { 0%, 100% { stroke-opacity: 0.3; } 45% { stroke-opacity: 0.9; } }

/* ---- email marketing: broadcast to targets ---- */
.mail-arc { fill: none; stroke: var(--ink); stroke-opacity: 0.14; stroke-width: 2; stroke-dasharray: 3 7; }
.mail-sig {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 12 88;
  animation: mailSig 3s linear infinite;
  animation-delay: var(--d, 0s);
}
@keyframes mailSig { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }

.mail-target {
  fill: var(--accent);
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0.25;
  animation: mailTarget 3s var(--ease) infinite;
  animation-delay: var(--d, 0s);
}
@keyframes mailTarget {
  0%, 78%  { opacity: 0.25; transform: scale(0.7); }
  90%      { opacity: 1; transform: scale(1.3); }
  100%     { opacity: 0.5; transform: scale(1); }
}

/* ---- seo & performance: the score dial ---- */
.perf-track { fill: none; stroke: rgba(246, 245, 242, 0.14); stroke-width: 10; stroke-linecap: round; }
.perf-fill {
  fill: none;
  stroke: var(--accent);
  stroke-width: 10;
  stroke-linecap: round;
  stroke-dasharray: 100;
  animation: perfFill 4.4s var(--ease) infinite;
}
@keyframes perfFill {
  0%        { stroke-dashoffset: 100; }
  55%, 82%  { stroke-dashoffset: 12; }
  100%      { stroke-dashoffset: 100; }
}

.perf-needle {
  transform-origin: 400px 300px;
  animation: perfNeedle 4.4s var(--ease) infinite;
}
@keyframes perfNeedle {
  0%        { transform: rotate(-82deg); }
  50%       { transform: rotate(58deg); }
  60%       { transform: rotate(50deg); }
  82%       { transform: rotate(52deg); }
  100%      { transform: rotate(-82deg); }
}

@media (prefers-reduced-motion: reduce) {
  .build-b { opacity: 1; transform: none; animation: none; }
  .store-belt,
  .store-check,
  .flow-sig,
  .flow-node,
  .mail-sig,
  .mail-target,
  .perf-fill,
  .perf-needle { animation: none; }
  .store-check { opacity: 1; transform: scale(1); }
  .flow-node { stroke-opacity: 0.7; }
  .mail-target { opacity: 1; }
  .perf-fill { stroke-dashoffset: 14; }
  .perf-needle { transform: rotate(52deg); }
}

/* ============================================================
   43. WORK ROW ON TOUCH: NOTHING MOVES VERTICALLY
   A finger fires :hover on tap and leaves it stuck, so the 3px
   lift meant for a mouse read as cards jumping up and settling
   back while you swiped past them. Keyed on the hover capability
   rather than width, since a narrow desktop window still has a
   real pointer and should keep the lift.
   ============================================================ */

@media (hover: none) {
  .card:hover,
  .work-grid .card:hover,
  .work-grid .card.is-live { transform: none; }

  /* Press feedback stays. It is deliberate, and it is not vertical.
     Listed last so it wins over the rules above at equal specificity. */
  .work-grid .card:active { transform: scale(0.985); }

  /* No grey flash on tap, which added to the restlessness. */
  .work-grid .card { -webkit-tap-highlight-color: transparent; }
}

/* ============================================================
   44. WORK ROW: LOCK THE SWIPE TO ONE AXIS
   Setting only overflow-x makes the other axis compute from
   visible to auto, and the row's 0.75rem padding-bottom gave it
   12px of vertical slack. So the row could scroll slightly up
   and down, which stole the gesture: a drag resolved as vertical
   and the row then refused to move sideways at all.
   ============================================================ */

@media (max-width: 767px) {
  .work-grid {
    /* One axis only. Vertical drags now pass through to the page. */
    overflow-y: hidden;

    /* The slack came from padding inside the scroll box. Moved outside it,
       so scrollHeight equals clientHeight and there is nothing to shift. */
    padding-bottom: 0;
    margin-bottom: 0.75rem;

    /* Dragging that begins on a link or its text can start a selection or
       an iOS link preview instead of a scroll. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }

  .work-grid .card {
    -webkit-user-drag: none;
  }
}

/* ---- web creation, alternative: sheets converging into a page ---- */
.svc-anim--layers { background: var(--paper); }

.layers-sheet {
  /* Centre of the sheet, so the fan-out rotates about its own middle. */
  transform-origin: 400px 271px;
  transform: translate(var(--tx), var(--ty)) rotate(var(--r));
  animation: layersIn 5.6s var(--ease) infinite;
}

@keyframes layersIn {
  0%, 10%   { transform: translate(var(--tx), var(--ty)) rotate(var(--r)); }
  40%, 74%  { transform: translate(0, 0) rotate(0deg); }
  100%      { transform: translate(var(--tx), var(--ty)) rotate(var(--r)); }
}

@media (prefers-reduced-motion: reduce) {
  .layers-sheet { animation: none; transform: translate(0, 0) rotate(0deg); }
}

/* ============================================================
   45. AI BUILDER ANIMATION
   The newest service, and the only panel on cobalt — it should
   read as the odd one out. A scan runs the table, then rows
   write themselves in and their status pills settle one by one.
   Same cost as the others: opacity, transform and fill only,
   and paused whenever the item is not the active one.
   ============================================================ */

.svc-anim--tools { background: var(--accent-deep); }

/* --- the pass that precedes the rows --- */
.tools-scan {
  opacity: 0;
  animation: toolsScan 5.6s var(--ease-out) infinite;
}

@keyframes toolsScan {
  0%          { transform: translateY(196px); opacity: 0; }
  6%          { opacity: 0.7; }
  26%         { transform: translateY(378px); opacity: 0; }
  100%        { transform: translateY(378px); opacity: 0; }
}

/* --- records arriving --- */
.tools-row {
  opacity: 0;
  animation: toolsRow 5.6s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.2s + 0.5s);
}

@keyframes toolsRow {
  0%        { opacity: 0; transform: translateY(14px); }
  8%, 88%   { opacity: 1; transform: translateY(0); }
  100%      { opacity: 0; transform: translateY(-6px); }
}

/* --- and then each one resolving to done --- */
.tools-pill {
  fill: rgba(246, 245, 242, 0.16);
  animation: toolsPill 5.6s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.2s + 1.2s);
}

@keyframes toolsPill {
  0%, 4%    { fill: rgba(246, 245, 242, 0.16); }
  12%, 82%  { fill: var(--paper); }
  100%      { fill: rgba(246, 245, 242, 0.16); }
}

.tools-check {
  opacity: 0;
  animation: toolsCheck 5.6s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.2s + 1.2s);
}

@keyframes toolsCheck {
  0%, 6%    { opacity: 0; }
  14%, 82%  { opacity: 1; }
  100%      { opacity: 0; }
}

/* --- the mark that says who wrote them --- */
.tools-spark {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0.5;
  animation: toolsSpark 5.6s var(--ease) infinite;
}

@keyframes toolsSpark {
  0%, 100%  { opacity: 0.45; transform: scale(0.88); }
  8%        { opacity: 1; transform: scale(1.18); }
  34%       { opacity: 0.75; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .tools-scan { animation: none; opacity: 0; }
  .tools-row,
  .tools-pill,
  .tools-check,
  .tools-spark { animation: none; }
  .tools-row { opacity: 1; }
  .tools-pill { fill: var(--paper); }
  .tools-check { opacity: 1; }
  .tools-spark { opacity: 0.9; transform: scale(1); }
}

/* ============================================================
   46. THE PRONOUN KEEPS ITS CAPITAL
   Display type across the site is lowercased on purpose. A bare
   "i" is the one place that reads as a typo rather than a style,
   so it opts out of the transform while everything else stays
   lowercase. Set in the copy itself, not with uppercase, so the
   source says exactly what the page says.
   ============================================================ */

.up { text-transform: none; }

/* ============================================================
   47. CONTACT FORM
   Lives on the cobalt band, so it is drawn in white on brand:
   underline fields rather than boxes, which keeps the section
   feeling like a page and not like an admin screen.
   ============================================================ */

.cform-wrap {
  position: relative; /* above the wave, which sits at z-index 0 */
  z-index: 1;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  max-width: 46rem;
}

.cform-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
}

.cform-field { margin: 0; display: grid; gap: 0.4rem; }
.cform-field--wide { grid-column: 1 / -1; }

@media (max-width: 599px) {
  .cform-grid { grid-template-columns: minmax(0, 1fr); }
}

.cform-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}

.cform-input {
  width: 100%;
  padding: 0.6rem 0 0.65rem;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 0; /* iOS rounds inputs unless told otherwise */
  background: transparent;
  color: var(--on-accent);
  font-family: var(--font-sans);
  font-size: 1.0625rem;
  line-height: 1.5;
  transition: border-color 0.3s var(--ease);
  -webkit-appearance: none;
  appearance: none;
}

.cform-input::placeholder { color: rgba(255, 255, 255, 0.5); }

.cform-input:hover { border-bottom-color: rgba(255, 255, 255, 0.6); }

.cform-input:focus {
  outline: none;
  border-bottom-color: var(--on-accent);
  border-bottom-width: 2px;
  /* Compensate so the text does not shift when the rule thickens. */
  padding-bottom: calc(0.65rem - 1px);
}

/* Focus has to be visible for a keyboard as well as a mouse. */
.cform-input:focus-visible { box-shadow: 0 2px 0 0 var(--on-accent); }

.cform-textarea { resize: vertical; min-height: 6.5rem; }

.has-error .cform-input { border-bottom-color: var(--ink); }

.cform-err {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ink);
}

/* Chrome paints its own pale autofill background, which is unreadable
   on cobalt. The long inset shadow is the only way to override it. */
.cform-input:-webkit-autofill,
.cform-input:-webkit-autofill:hover,
.cform-input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--on-accent);
  -webkit-box-shadow: 0 0 0 1000px var(--accent) inset;
  caret-color: var(--on-accent);
}

/* --- consent --- */
.cform-consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.6rem;
  align-items: start;
  margin: clamp(1.5rem, 3vw, 2rem) 0 0;
  font-size: 0.875rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}

.cform-consent input {
  width: 1.15rem;
  height: 1.15rem;
  margin: 0.15rem 0 0;
  accent-color: var(--ink);
  cursor: pointer;
}

.cform-consent label { cursor: pointer; }

.cform-consent a {
  color: var(--on-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.cform-consent a:hover { color: var(--ink); }

.cform-consent .cform-err { grid-column: 2; }

.cform-captcha { margin-top: clamp(1.25rem, 3vw, 1.75rem); }

/* The widget is a fixed-width iframe; on a narrow phone it would
   otherwise push the page sideways. */
.cform-captcha .g-recaptcha {
  transform-origin: 0 0;
  max-width: 100%;
  overflow: hidden;
}

@media (max-width: 359px) {
  .cform-captcha .g-recaptcha { transform: scale(0.86); height: 66px; }
}

.cform-send { margin-top: clamp(1.5rem, 3vw, 2rem); }

/* White button, because on cobalt the accent button would vanish. */
.btn--light {
  --btn-bg: var(--on-accent);
  --btn-fg: var(--accent);
}
.btn--light::after { background: var(--ink); }
.btn--light:hover { color: var(--paper); }

/* --- notes --- */
.cform-note {
  margin: 0 0 1.5rem;
  padding: 0.9rem 1.1rem;
  border-radius: 12px;
  font-size: 0.9375rem;
  line-height: 1.5;
}

.cform-note--ok {
  background: var(--on-accent);
  color: var(--accent);
  font-weight: 500;
}

.cform-note--bad {
  background: var(--ink);
  color: var(--paper);
}

/* --- the honeypot: gone, but not with display:none, which some bots
   read as a signal to skip the field --- */
.cform-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The wave is anchored to the bottom of the band and stands up to 360px
   tall. Without this the send button lands inside it. */
.cform-wrap { margin-bottom: calc(clamp(180px, 30vh, 360px) + 2.5rem); }

/* --- form sending state, and the note the script swaps in --- */
.cform.is-sending { opacity: 0.6; pointer-events: none; }

.cform-send[disabled] { cursor: progress; }

/* Empty until the script has something to say, so it must not
   occupy space or draw a box before then. */
.cform-status:empty { display: none; }

/* ============================================================
   48. CONTACT FORM: MAKE THE FIELDS READ AS FIELDS
   A 35%-white hairline on saturated cobalt gives almost no
   signal that there is somewhere to type — an empty field was
   just a label with a scratch beneath it. Each field now has a
   faint surface of its own, which is enough to be read as an
   input without turning into a hard white box.
   ============================================================ */

.cform-input {
  padding: 0.7rem 0.85rem;
  background: rgba(255, 255, 255, 0.1);
  border-bottom: 2px solid rgba(255, 255, 255, 0.5);
  /* Square where it meets its own underline, so the field still
     reads as an underline field rather than a pill. */
  border-radius: 10px 10px 0 0;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease);
}

.cform-input:hover {
  background: rgba(255, 255, 255, 0.15);
  border-bottom-color: rgba(255, 255, 255, 0.75);
}

.cform-input:focus {
  background: rgba(255, 255, 255, 0.19);
  border-bottom-color: var(--on-accent);
  /* The border no longer changes width, so nothing shifts. */
  padding-bottom: 0.7rem;
}

.cform-input::placeholder { color: rgba(255, 255, 255, 0.6); }

/* Labels were dimmer than the text they describe. */
.cform-label { color: rgba(255, 255, 255, 0.88); }

.cform-consent { color: rgba(255, 255, 255, 0.92); }

/* An error has to survive being on brand colour, so it gets the
   ink underline plus a warmer fill rather than colour alone. */
.has-error .cform-input {
  background: rgba(22, 21, 19, 0.22);
  border-bottom-color: var(--ink);
}

/* The whole thing sits apart from the details above it. */
.cform-wrap {
  padding-top: clamp(1.75rem, 3.5vw, 2.75rem);
  border-top: 1px solid rgba(255, 255, 255, 0.22);
}

/* ============================================================
   49. FORM ERRORS HAVE TO BE READABLE ON COBALT
   The error colour was the ink token, which measures 2.72:1 on
   the accent — worse than the 4.5:1 it needed, and on the one
   message a visitor must not miss. A pale rose reads as a
   warning and clears the bar at 5.01:1.
   ============================================================ */

:root { --warn: #ffd5cc; }

.cform-err {
  font-size: 0.8125rem;
  color: var(--warn);
}

.has-error .cform-input {
  background: rgba(255, 213, 204, 0.14);
  border-bottom-color: var(--warn);
}

.cform-note--bad {
  background: rgba(255, 213, 204, 0.16);
  box-shadow: inset 0 0 0 1px var(--warn);
  color: var(--warn);
}

/* The note the script swaps in is focused so it can be announced;
   that must not draw a second outline on top of its own box. */
.cform-status:focus { outline: none; }
.cform-status:focus-visible { outline: 2px solid var(--on-accent); outline-offset: 3px; }

/* ============================================================
   50. reCAPTCHA v3 DISCLOSURE
   v3 is invisible and normally plants a floating badge in the
   corner. Google permits hiding it provided the notice below
   appears in the form, which sits better in this layout than a
   badge over the wave.
   ============================================================ */

.grecaptcha-badge { visibility: hidden; }

.cform-legal {
  margin: 1.25rem 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.72);
}

.cform-legal a {
  color: rgba(255, 255, 255, 0.92);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* The mobile loader is a nod, not a performance: a shorter hold in the
   script, and a quicker exit here. The wipe was 0.1s delay plus 0.75s,
   which on a phone is most of the time the screen stays covered. */
.loader--brief.is-done .loader-curtain,
.loader--brief.is-done {
  animation-duration: 0.35s;
  animation-delay: 0s;
}

.loader--brief.is-done .loader-inner {
  transition-duration: 0.2s;
}
