/* AGN Flow — production studio site
   Two typefaces, one accent, black ground. The work carries the colour. */

:root {
  /* Brand. --accent-bright is for text ON the page, never behind white text:
     7.0:1 on the ground, 6.2:1 on the lightest card and 5.9:1 where the edge
     light is heaviest, but only 3.0:1 as a background, which is why solid
     buttons darken on hover to --accent-deep (9.7:1). The older #a259f5 gives
     4.4:1 over that coloured edge, under the floor for the 11 and 12px text
     that uses it. */
  --accent: #7c1ed2;
  --accent-bright: #b478f8;
  --accent-deep: #66159f;

  /* Neutral ramp. Black, so the grid lines and the photography are the only
     things with weight; the colour arrives from the edges instead of washing
     the whole field. */
  --ink: #000000;
  /* Every scrim over a photo is mixed from this, so an image never sits on a
     tone different from the page it overlays. */
  --ink-rgb: 0, 0, 0;
  --surface: rgba(255, 255, 255, 0.035);
  --surface-raised: rgba(255, 255, 255, 0.07);
  --line: rgba(255, 255, 255, 0.12);
  --line-strong: rgba(255, 255, 255, 0.30);
  --white: #ffffff;
  --grey: #a1a1aa;
  /* Form outcomes. Both clear 9:1 anywhere the card can sit, and neither is
     the only signal: the wording says what happened on its own. */
  --ok: #6ee7a8;
  --danger: #ff9b9b;

  /* Type. Three deliberate roles: a hard grotesque for structure, a neutral
     for reading, and a handwritten accent for emphasis. Not the old seven. */
  --font-display: "Archivo", system-ui, sans-serif;
  --font-body: "Instrument Sans", system-ui, sans-serif;
  /* Allura ships weight 400 only - never ask for bold or the browser
     synthesises a smeared faux-bold. It is also thin and steeply slanted,
     so every use below is set larger than the Caveat it replaced. */
  --font-script: "Allura", "Segoe Script", cursive;

  /* The light that comes in from the left and right edges. Declared once: the
     page carries it, and so does every full-bleed cover, otherwise the cover
     ends in a hard horizontal seam where the colour stops. 0.48 and 0.38 are
     ceilings — see the note on --accent-bright. */
  --edge-light:
    linear-gradient(to right,
      rgba(124, 30, 210, 0.48) 0%,
      rgba(124, 30, 210, 0.19) 16%,
      transparent 48%),
    linear-gradient(to left,
      rgba(47, 99, 237, 0.38) 0%,
      rgba(47, 99, 237, 0.15) 16%,
      transparent 48%);

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 180ms;
  --dur: 450ms;
  --dur-slow: 900ms;

  /* Rhythm */
  /* The fixed bar: 46px of logo between 12px of padding, plus its border.
     Anything that must not end up underneath it clears this. */
  --header-h: 71px;
  --section-y: clamp(72px, 10vw, 140px);
  --gutter: clamp(20px, 4vw, 40px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  background: var(--ink);
  /* The two grid planes below are transformed well past the viewport edges.
     Fixed elements should not extend the scrollable area, but browsers have
     disagreed about that, so the clip is explicit. */
  overflow-x: hidden;
}

body {
  background: transparent;
  color: var(--white);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- Consent ----------
   Analytics loads only after someone says yes, so the banner is not decoration
   over something already running. Accept and refuse are the same size and the
   same weight: a refusal that is harder to find than an acceptance is not a
   choice, and regulators treat it as one. */

.consent {
  position: fixed;
  left: 50%;
  bottom: clamp(14px, 2.5vw, 26px);
  z-index: 200;
  width: min(760px, calc(100vw - 2 * clamp(14px, 4vw, 32px)));
  transform: translate(-50%, 140%);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(16px, 2.5vw, 30px);
  padding: clamp(18px, 2.2vw, 24px) clamp(20px, 2.6vw, 28px);
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(10, 10, 16, 0.94);
  backdrop-filter: blur(16px);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.9);
  transition: transform 520ms var(--ease), opacity 320ms var(--ease);
  opacity: 0;
}

.consent.is-open {
  transform: translate(-50%, 0);
  opacity: 1;
}

.consent p {
  font-size: 14px;
  line-height: 1.55;
  color: var(--grey);
  text-wrap: pretty;
}

.consent p strong {
  display: block;
  margin-bottom: 4px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 15px;
  letter-spacing: 0.01em;
  color: var(--white);
}

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

.consent-actions {
  display: flex;
  gap: 10px;
}

/* Same size, same weight, same prominence. */
.consent-actions .btn {
  padding: 11px 22px;
  font-size: 13px;
  white-space: nowrap;
}

@media (max-width: 620px) {
  .consent {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }
  .consent-actions .btn { flex: 1 1 0; justify-content: center; }
}

/* The footer link that reopens it: consent has to be withdrawable as easily
   as it was given. */
.consent-reopen {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: none;
}

/* ---------- Intro ----------
   Built by JavaScript, never written into the HTML. An overlay that covers the
   page has to be removable, and markup that hides the site while a script
   fails to load hides it forever.

   Runs once per tab and lasts 1.5s. It is a greeting, not a gate: a click, a
   key or a scroll ends it early, and prefers-reduced-motion skips it outright.
   The page underneath is fully built the whole time, so nothing is waiting on
   the animation. */

.intro {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--ink);
  perspective: 900px;
  /* Held back, so the exit is watched rather than wiped over. */
  transition: opacity 260ms var(--ease) 240ms, visibility 0s linear 500ms;
}

/* The same two planes as the page, rushing in rather than sitting still. */
.intro-grid {
  position: absolute;
  left: -60%;
  right: -60%;
  height: 500px;
  background-image:
    repeating-linear-gradient(to right,
      rgba(255, 255, 255, 0.16) 0 1px, transparent 1px 92px),
    repeating-linear-gradient(to bottom,
      rgba(255, 255, 255, 0.16) 0 1px, transparent 1px 92px);
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0%, #000 30%, #000 76%, transparent 100%);
          mask-image: linear-gradient(to bottom,
    transparent 0%, #000 30%, #000 76%, transparent 100%);
  opacity: 0;
  animation: intro-grid 1500ms var(--ease) forwards;
}

.intro-grid.is-floor {
  top: 50%;
  transform-origin: 50% 0;
  --tilt: 64deg;
}

.intro-grid.is-ceiling {
  bottom: 50%;
  transform-origin: 50% 100%;
  --tilt: -64deg;
}

@keyframes intro-grid {
  0%   { opacity: 0; transform: perspective(620px) rotateX(var(--tilt)) translateZ(-420px); }
  45%  { opacity: 1; }
  100% { opacity: 1; transform: perspective(620px) rotateX(var(--tilt)) translateZ(0); }
}

/* The mark arrives out of the depth and settles. */
.intro-logo {
  position: relative;
  width: min(300px, 48vw);
  opacity: 0;
  transform: translateZ(-500px) rotateX(38deg) scale(1.3);
  animation: intro-logo 1050ms var(--ease) 260ms forwards;
  filter: drop-shadow(0 0 26px hsl(272 96% 66% / 0.85))
          drop-shadow(0 0 64px hsl(272 96% 60% / 0.45));
}

@keyframes intro-logo {
  0%   { opacity: 0; transform: translateZ(-500px) rotateX(38deg) scale(1.3); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: translateZ(0) rotateX(0deg) scale(1); }
}

/* The way out mirrors the way in. The mark came out of the depth and settled;
   leaving, it keeps coming — past the viewer and out of frame — and the planes
   pull away the way they arrived. It reads as passing through the logo into
   the site rather than as the logo being taken away.

   Each exit keyframe begins on exactly the settled state the entrance ended
   on. Replacing a running animation snaps the element to the new one's first
   frame, and anything else there shows as a jump when the intro is cut short. */
.intro.is-done {
  opacity: 0;
  visibility: hidden;
}

.intro.is-done .intro-logo {
  animation: intro-logo-out 500ms var(--ease) forwards;
}

@keyframes intro-logo-out {
  0%   { opacity: 1; transform: translateZ(0) rotateX(0deg) scale(1); }
  100% { opacity: 0; transform: translateZ(420px) rotateX(-10deg) scale(1.55); }
}

.intro.is-done .intro-grid {
  animation: intro-grid-out 500ms var(--ease) forwards;
}

@keyframes intro-grid-out {
  0%   { opacity: 1; transform: perspective(620px) rotateX(var(--tilt)) translateZ(0); }
  100% { opacity: 0; transform: perspective(620px) rotateX(var(--tilt)) translateZ(360px); }
}

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

/* ---------- Edge light ----------
   Colour enters from the left and right and falls away to nothing before it
   reaches the middle, so the centre of every page stays black and the
   photography is the only thing in it carrying colour. Spread across the
   whole field, the same colour read as a wash over grey.

   inset is 0, not negative: these bands have to start exactly at the viewport
   edges. Fixed layer rather than background-attachment: fixed, which
   misrenders in Playwright captures. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--edge-light);
  /* Two cycles of different length, so the pair never visibly repeats. The
     hue swing is deliberately narrow: wider and it leaves the purple-to-blue
     family the brand lives in and starts turning green. */
  animation: aurora-drift 34s ease-in-out infinite alternate,
             aurora-hue 26s ease-in-out infinite alternate;
  will-change: transform;
}

/* ---------- Grid planes ----------
   Two line planes in perspective, one underfoot and one overhead, converging
   on a horizon at the middle of the viewport. They give the page depth that a
   flat pattern cannot, and they meet where the content sits, which is exactly
   where they fade out, so nothing is ever read through a line.

   One plane per pseudo-element because an element can only hold one transform,
   and they are fixed so the horizon stays put while the page scrolls. */

html::before,
body::after {
  content: "";
  position: fixed;
  left: -60%;
  right: -60%;
  /* 500px, not a vh value. The projection magnifies 3.6x at the far edge, so
     the plane covers 797px of screen below the horizon — enough down to a
     1600px-tall window. Much taller and the far edge approaches the 620px
     eye distance and the scale runs away. */
  height: 500px;
  z-index: -1;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(to right,
      rgba(255, 255, 255, 0.12) 0 1px, transparent 1px 92px),
    repeating-linear-gradient(to bottom,
      rgba(255, 255, 255, 0.12) 0 1px, transparent 1px 92px),
    /* Diagonals at a third of the strength: enough to triangulate the squares,
       not enough to read as a second grid competing with the first. */
    repeating-linear-gradient(45deg,
      rgba(255, 255, 255, 0.042) 0 1px, transparent 1px 130px),
    repeating-linear-gradient(-45deg,
      rgba(255, 255, 255, 0.042) 0 1px, transparent 1px 130px);
  /* Faded at both ends: solid at the horizon the lines bunch into a smear,
     and solid at the near edge they compete with the footer. */
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0%, #000 30%, #000 76%, transparent 100%);
          mask-image: linear-gradient(to bottom,
    transparent 0%, #000 30%, #000 76%, transparent 100%);
  backface-visibility: hidden;
}

/* Overhead. Its lower edge is the horizon, so it hangs from the middle up. */
html::before {
  bottom: 50%;
  transform-origin: 50% 100%;
  transform: perspective(620px) rotateX(-64deg);
}

/* Underfoot. Its upper edge is the same horizon. */
body::after {
  top: 50%;
  transform-origin: 50% 0;
  transform: perspective(620px) rotateX(64deg);
}

/* The line the two planes converge on. Drawn rather than implied: without it
   the grids stop at nothing and the space has no floor. */
.horizon {
  position: fixed;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to right,
    transparent 0%,
    rgba(255, 255, 255, 0.26) 22%,
    rgba(180, 120, 248, 0.52) 50%,
    rgba(255, 255, 255, 0.26) 78%,
    transparent 100%);
}

.horizon::after {
  content: "";
  position: absolute;
  left: 20%;
  right: 20%;
  top: -22px;
  height: 44px;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(180, 120, 248, 0.16), transparent 70%);
}

@media (max-width: 720px) {
  .horizon { display: none; }
}

/* Below roughly a tablet the planes are steeper than they are wide and read as
   noise behind the text rather than as space. */
@media (max-width: 720px) {
  html::before,
  body::after { display: none; }
}

/* Vertical only. A sideways drift would pull one band off the screen and
   leave that edge bare. */
@keyframes aurora-drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(0, -2.5%, 0) scale(1.06); }
}

@keyframes aurora-hue {
  from { filter: hue-rotate(-18deg) saturate(1); }
  to   { filter: hue-rotate(30deg) saturate(1.15); }
}

@media (prefers-reduced-motion: reduce) {
  body::before { animation: none; }
}

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

.container {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

section { padding: var(--section-y) 0; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--white);
  color: var(--ink);
  padding: 12px 20px;
  font-weight: 600;
  border-radius: 0 0 8px 0;
}

.skip-link:focus { left: 0; }

/* ---------- Motion primitives ---------- */

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}

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

.reveal-group > * {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms);
}

.reveal-group.is-visible > * { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .reveal,
  .reveal-group > * { opacity: 1; transform: none; }
}

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

/* ---------- Type ---------- */

.accent { color: var(--accent-bright); }

/* Handwritten emphasis inside a hard uppercase headline. The contrast is the
   point: it reads like something written on top of the printed word. */
.script-accent {
  font-family: var(--font-script);
  font-weight: 400;
  font-size: 1.5em;
  line-height: 0.75;
  letter-spacing: 0;
  text-transform: none;
  color: var(--accent-bright);
}

.hero-period {
  position: relative;
  display: inline-block;
  width: 0.18em;
  color: transparent;
}

.hero-period::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 72%;
  width: max(0.14em, 3px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--white);
  transform: translate(-50%, -50%);
}

.section-title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(34px, 5.6vw, 68px);
  line-height: 1.0;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  text-wrap: balance;
}

/* ---------- Section eyebrow ----------
   Replaces the old pill chip, which was 12px and disappeared. The label is
   set in the script face at display size so a section announces itself. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: clamp(8px, 1.2vw, 16px);
}

.eyebrow-label {
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(42px, 5.6vw, 70px);
  /* Room for Allura's descenders and swashes, which a tight leading clips. */
  line-height: 1.05;
  padding-right: 0.12em;
  color: var(--accent-bright);
  flex-shrink: 0;
}

.eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--line-strong), transparent);
}

/* Centred sections drop the rule and centre the pair. */
.team .eyebrow,
.clients .eyebrow,
.work-cta .eyebrow { justify-content: center; }

.team .eyebrow::after,
.clients .eyebrow::after,
.work-cta .eyebrow::after { display: none; }

.notfound-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: clamp(26px, 3.5vw, 36px);
}

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.04em;
  text-decoration: none;
  color: var(--white);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 100px;
  padding: 13px 30px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.btn:hover {
  background: var(--white);
  border-color: var(--white);
  color: var(--ink);
}

.btn-solid {
  background: var(--accent);
  border-color: var(--accent);
}

.btn-solid:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
  color: var(--white);
}

.play-icon {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 6px 0 6px 10px;
  border-color: transparent transparent transparent currentColor;
}

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

/* Floats over the hero instead of sitting above it: a solid bar on top of a
   full-bleed image read as a hard seam across the page. It only takes on a
   background once you have scrolled past the hero (class set in main.js). */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              backdrop-filter var(--dur) var(--ease);
}

.site-header.is-stuck {
  background: rgba(var(--ink-rgb), 0.72);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
}

/* Pages that do not open with a full-bleed hero need clearance under it. */
main > section:first-child:not(.hero):not(.page-header) {
  padding-top: calc(var(--section-y) + var(--header-h));
}

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-top: 12px;
  padding-bottom: 12px;
}

.logo img { height: 46px; width: auto; }

.nav-links {
  display: flex;
  gap: 32px;
  list-style: none;
  margin-left: auto;
}

.nav-links a {
  font-size: 14px;
  font-weight: 500;
  color: var(--grey);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}

.nav-links a:hover { color: var(--white); }

/* The page you are on, marked with aria-current in the markup. */
.nav-links a[aria-current="page"] {
  color: var(--white);
  text-decoration: underline;
  text-decoration-color: var(--accent-bright);
  text-underline-offset: 6px;
  text-decoration-thickness: 2px;
}

.header-cta { margin-left: 28px; padding: 10px 22px; }

.nav-toggle {
  display: none;
  background: none;
  border: none;
  color: var(--white);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  margin-left: auto;
}

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

.hero {
  position: relative;
  min-height: 92svh;
  display: flex;
  align-items: flex-end;
  padding: calc(var(--header-h) + clamp(12px, 2.5vh, 32px)) 0 clamp(48px, 9vh, 104px);
  overflow: hidden;
}

.hero-media { position: absolute; inset: 0; }

.hero-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--ink);
}

/* Dark from the bottom so the headline always has something to sit on. */
.hero-scrim {
  position: absolute;
  inset: 0;
  /* Runs up from the bottom and nothing crosses the frame sideways: the edge
     light and an older left-hand darkening both ended somewhere across the
     image and the line where they stopped was visible on it. Solid black for
     the first 15% so the hero disappears into the section below instead of
     ending on an edge. The headline can afford the loss — it is bottom
     aligned, so it sits where this is at 0.64 or heavier, which puts white
     past 13:1 against the brightest part of the frame behind it. */
  background:
    linear-gradient(to top,
      rgba(0, 0, 0, 1) 0%,
      rgba(0, 0, 0, 1) 15%,
      rgba(0, 0, 0, 0.90) 32%,
      rgba(0, 0, 0, 0.64) 52%,
      rgba(0, 0, 0, 0.32) 74%,
      transparent 100%);
  transition: opacity var(--dur) var(--ease);
}

/* The page below the hero is lit from both edges and the hero is not, so the
   join showed as a line where the purple started. This carries the same edge
   light across the bottom of the hero and fades it out upwards, so it only
   ever falls on parts of the frame that are already solid black. The image
   itself is never crossed by it — which was the reason for taking the side
   gradients off in the first place. */
.hero-scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--edge-light);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 10%, transparent 32%);
          mask-image: linear-gradient(to top, #000 0%, #000 10%, transparent 32%);
}

.hero-content {
  position: relative;
  z-index: 2;
  width: 100%;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.hero h1 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(18px, 5.55vw, 82px);
  /* Loosened from 0.94: the script word is set at 1.5em, so a tighter
     leading let its descenders collide with the line underneath. */
  line-height: 1.04;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  text-wrap: balance;
  max-width: none;
}

.hero .subtitle {
  font-size: clamp(15px, 1.5vw, 18px);
  line-height: 1.6;
  color: var(--grey);
  max-width: 52ch;
  margin: 22px 0 clamp(28px, 4vw, 40px);
  text-wrap: pretty;
}

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

/* While the showreel plays, get everything else out of the way. */
.hero.is-playing .hero-scrim,
.hero.is-playing .hero-content {
  opacity: 0;
  pointer-events: none;
}

.hero.is-playing .hero-content { transform: translateY(16px); }

/* ---------- Serviços ---------- */

.services-head { margin-bottom: clamp(36px, 5vw, 56px); }

.services-head .subtitle {
  font-size: 17px;
  line-height: 1.65;
  color: var(--grey);
  margin-top: 16px;
  max-width: 62ch;
  text-wrap: pretty;
}

/* Two up, not four: at four across each card was a narrow column and the
   colour and depth had no room to read. */
.services-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(16px, 1.8vw, 24px);
}

/* Each card owns a hue; everything tinted inside derives from it. */
.services-grid > :nth-child(1) { --hue: 272; }
.services-grid > :nth-child(2) { --hue: 218; }
.services-grid > :nth-child(3) { --hue: 186; }
.services-grid > :nth-child(4) { --hue: 322; }

.service-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: clamp(300px, 30vw, 380px);
  border: 1px solid var(--line);
  border-radius: 24px;
  padding: clamp(30px, 3vw, 44px) clamp(26px, 2.6vw, 40px) clamp(26px, 2.4vw, 34px);
  background:
    radial-gradient(130% 95% at 50% -10%, hsl(var(--hue) 88% 62% / 0.20), transparent 62%),
    var(--surface);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transform: perspective(900px);
  transform-style: preserve-3d;
  will-change: transform;
  transition: transform 300ms var(--ease),
              border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

/* Lit top edge in the card's own colour. */
.service-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, hsl(var(--hue) 92% 70%), transparent);
  opacity: 0.75;
}

/* Spotlight that follows the cursor; --mx/--my are set in main.js. */
.service-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(380px circle at var(--mx, 50%) var(--my, 0%),
              hsl(var(--hue) 92% 64% / 0.26), transparent 70%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}

.service-card:hover::after { opacity: 1; }
/* With the instrument up, the card already has something to look at. */
.service-card.is-peeking::after { opacity: 0.4; }

/* Depth carried by a black shadow, with the hue only hinted underneath. A
   purely coloured glow is the stock AI-interface look the detector flags. */
.service-card:hover {
  border-color: hsl(var(--hue) 70% 62% / 0.55);
  box-shadow:
    0 30px 62px -30px rgba(0, 0, 0, 0.85),
    0 12px 26px -20px hsl(var(--hue) 75% 45% / 0.4);
}

.service-num {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 14px;
  letter-spacing: 0.22em;
  color: hsl(var(--hue) 80% 74%);
  margin-bottom: clamp(20px, 2.4vw, 32px);
}

.service-card h3 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(26px, 3.2vw, 42px);
  line-height: 1.0;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.service-card p {
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.6;
  color: var(--grey);
  max-width: 46ch;
  text-wrap: pretty;
}

.service-foot {
  margin-top: auto;
  padding-top: clamp(22px, 2.4vw, 32px);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: hsl(var(--hue) 60% 80%);
}

.service-foot::before {
  content: "";
  display: block;
  width: 28px;
  height: 2px;
  background: hsl(var(--hue) 85% 62%);
  margin-bottom: 14px;
}

/* Tilting a blurred, shadowed card is expensive and unwelcome for anyone who
   asked for less motion; the colour stays, the movement does not. */
@media (prefers-reduced-motion: reduce) {
  .service-card { transform: none !important; }
}

/* ---------- Serviços: o instrumento que segue o cursor ---------- */
/* Built by main.js, and only where there is a real pointer. Each card names
   its instrument in data-widget; the drawing is inline SVG, so there is
   nothing to download and nothing to cache-bust. The layer is fixed so the
   instrument escapes the card's own 3D tilt, which would shear it. */

.gizmo-layer {
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  display: none;
}

.gizmo-layer.is-live { display: block; }

/* transform is rewritten every frame by the follow loop, so nothing here may
   transition it — the easing lives in the JS lerp instead. */
.gizmo {
  position: absolute;
  top: 0;
  left: 0;
  width: 156px;
  padding: 11px 11px 8px;
  border-radius: 13px;
  color: #fff;
  background: rgba(8, 7, 12, 0.91);
  border: 1px solid hsl(var(--hue, 272) 72% 64% / 0.5);
  box-shadow:
    0 18px 38px -16px rgba(0, 0, 0, 0.95),
    0 0 32px -12px hsl(var(--hue, 272) 92% 56% / 0.55);
  opacity: 0;
  transform: translate3d(var(--x, 50vw), var(--y, 50vh), 0) translate(-50%, -50%);
  transition: opacity 200ms var(--ease);
  will-change: transform, opacity;
}

.gizmo.is-in { opacity: 1; }

.gizmo svg { display: block; width: 100%; height: auto; overflow: visible; }

/* Every animated group turns about its own centre, not the drawing's. */
.gizmo .g-brackets,
.gizmo .g-box,
.gizmo .g-rotor,
.gizmo .g-heart {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}

/* Everything is drawn in strokes so one weight change restyles the lot. */
.gizmo .g-line {
  fill: none;
  stroke: #fff;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.gizmo .g-tint { stroke: hsl(var(--hue, 272) 95% 76%); }
.gizmo .g-fill { fill: hsl(var(--hue, 272) 90% 72%); stroke: none; }
.gizmo .g-dim { stroke: rgba(255, 255, 255, 0.58); }

/* Readouts sit outside the SVG so they stay crisp and can hold live text. */
.gizmo-read {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 7px;
  font-family: var(--font-display, system-ui), monospace;
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.9);
}

.gizmo-read b { color: hsl(var(--hue, 272) 90% 76%); font-weight: 600; }

/* The exact pointer position, so the instrument reads as held rather than
   floating loose. */
.gizmo-dot {
  position: fixed;
  top: 0;
  left: 0;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: #fff;
  z-index: 41;
  opacity: 0;
  pointer-events: none;
  transform: translate3d(var(--x, 50vw), var(--y, 50vh), 0);
  transition: opacity 180ms var(--ease);
}

.gizmo-dot.is-in { opacity: 0.9; }

/* Only hidden once the instrument exists — see main.js. */
.service-card.has-gizmo { cursor: none; }

/* --- Fotografia: the viewfinder hunts focus once, on arrival --- */
.gizmo-camera .g-brackets { animation: gizmo-focus 620ms var(--ease) 1; }
.gizmo-camera .g-box { animation: gizmo-snap 620ms var(--ease) 1; }

@keyframes gizmo-focus {
  0%   { transform: scale(1.16); }
  55%  { transform: scale(0.97); }
  100% { transform: scale(1); }
}

@keyframes gizmo-snap {
  0%, 35% { transform: scale(1.45); }
  70%     { transform: scale(0.94); }
  100%    { transform: scale(1); }
}

/* --- Vídeo: the record light --- */
.gizmo-video .g-rec { animation: gizmo-blink 1.1s steps(1, end) infinite; }

@keyframes gizmo-blink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0.16; } }

/* --- Drone: rotors, and a body that will not sit still --- */
.gizmo-drone .g-rotor { animation: gizmo-spin 0.42s linear infinite; }
.gizmo-drone .g-rotor--b { animation-duration: 0.36s; animation-direction: reverse; }
.gizmo-drone .g-craft { animation: gizmo-hover 2.6s ease-in-out infinite; }

@keyframes gizmo-spin { to { transform: rotate(360deg); } }
@keyframes gizmo-hover { 0%, 100% { transform: translateY(-2px); } 50% { transform: translateY(2px); } }

/* --- Redes: the heart that lands when the count moves --- */
.gizmo-social .g-heart { animation: gizmo-beat 1.9s var(--ease) infinite; }

.gizmo-leads .g-funnel { animation: gizmo-funnel 1.8s var(--ease) infinite; }
.gizmo-leads .g-lead-dot { animation: gizmo-lead 1.8s var(--ease) infinite; }

.gizmo-automation .g-flow-node { animation: gizmo-flow 2.4s var(--ease) infinite; }
.gizmo-automation .g-flow-line { stroke-dasharray: 5 4; animation: gizmo-flow-line 2.4s linear infinite; }

@keyframes gizmo-funnel {
  0%, 100% { opacity: 0.75; }
  50% { opacity: 1; }
}

@keyframes gizmo-lead {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(5px); }
}

@keyframes gizmo-flow {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

@keyframes gizmo-flow-line { to { stroke-dashoffset: -18; } }

@keyframes gizmo-beat {
  0%, 72%, 100% { transform: scale(1); }
  78%           { transform: scale(1.28); }
  86%           { transform: scale(0.96); }
}

/* Anyone who asked for less motion still gets the instrument; it just stops
   twitching, and main.js pins it instead of letting it chase the pointer. */
@media (prefers-reduced-motion: reduce) {
  .gizmo *, .gizmo-dot { animation: none !important; }
  .gizmo, .gizmo-dot { transition-duration: 1ms; }
}

/* ---------- Áreas ---------- */

.areas { padding-top: 0; }

.areas .section-title { margin-bottom: clamp(28px, 4vw, 44px); }

/* Full-width rows rather than pills: each one fills with a real photo from
   that area on hover and opens the portfolio already filtered to it. */
.areas-list {
  list-style: none;
  border-top: 1px solid var(--line);
}

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

.area-row {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(14px, 2.6vw, 40px);
  padding: clamp(20px, 2.8vw, 34px) clamp(12px, 1.6vw, 24px);
  text-decoration: none;
  color: var(--white);
}

.area-row::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(90deg, rgba(var(--ink-rgb), 0.94) 28%, rgba(var(--ink-rgb), 0.42) 100%),
    var(--area-img);
  background-size: cover;
  /* Vertical framing is per row, set below. The rows are wide and short, so
     each photo crops to a band about a seventh of its height: a shared value
     misses the subject in four of the five. Each figure below is measured,
     not guessed — the band centre is put on the densest part of the frame,
     which is the car or the people rather than sky or tarmac. */
  background-position: center var(--area-focus, 50%);
  opacity: 0;
  transform: scale(1.07);
  transition: opacity var(--dur) var(--ease), transform 1000ms var(--ease);
}

.area-row:hover::before,
.area-row:focus-visible::before {
  opacity: 1;
  transform: none;
}

/* --area-focus is the vertical crop point that lands on the subject.
   Row photos follow the portfolio categories one-for-one. */

.areas-list > :nth-child(1) .area-row {
  --area-img: url("../assets/work/motorflow-american-cars-01.webp");
  --area-focus: 30%;
}

.areas-list > :nth-child(2) .area-row {
  --area-img: url("../assets/work/stands-img-20.webp");
  --area-focus: 50%;
}

.areas-list > :nth-child(3) .area-row {
  --area-img: url("../assets/work/eventos-noivas-07.webp");
  --area-focus: 42%;
}

.areas-list > :nth-child(4) .area-row {
  --area-img: url("../assets/work/photoshoot-ferrari-348ts-15.webp");
  --area-focus: 52%;
}

.areas-list > :nth-child(5) .area-row {
  --area-img: url("../assets/work/lifestyle-costa-algarvia-vista-de-drone-24.webp");
  --area-focus: 62%;
}

.area-num {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 13px;
  letter-spacing: 0.2em;
  color: var(--grey);
  transition: color var(--dur) var(--ease);
}

.area-name {
  position: relative;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(26px, 4.2vw, 56px);
  line-height: 1;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  transition: transform var(--dur) var(--ease);
}

/* The neon is a second copy of the word sitting exactly on the first, revealed
   left to right like a tube lighting along its length. A copy rather than a
   colour change on the original, because a glow needs to be drawn over solid
   letters — recolouring the text would take the white out from under it and
   the word would go dim before it goes bright. Deliberately not a flicker:
   repeated flashing is an accessibility problem, and this reads as neon
   without one. */
.area-name-neon {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  /* The letter stays white; the colour is only the halo around it. Because
     the copy sits exactly on the original, the sweep reveals the glow rather
     than a second, differently coloured word. */
  color: var(--white);
  text-shadow:
    0 0 6px hsl(var(--hue, 272) 96% 70% / 0.95),
    0 0 16px hsl(var(--hue, 272) 96% 64% / 0.85),
    0 0 34px hsl(var(--hue, 272) 96% 58% / 0.65),
    0 0 64px hsl(var(--hue, 272) 96% 54% / 0.40);
  clip-path: inset(0 100% 0 0);
  transition: clip-path 620ms var(--ease);
  pointer-events: none;
}

.area-row:hover .area-name-neon,
.area-row:focus-visible .area-name-neon { clip-path: inset(0 -18% 0 0); }

/* One hue per area, distinct from the three the team uses. */
.areas-list > :nth-child(1) { --hue: 272; }
.areas-list > :nth-child(2) { --hue: 212; }
.areas-list > :nth-child(3) { --hue: 340; }
.areas-list > :nth-child(4) { --hue: 162; }
.areas-list > :nth-child(5) { --hue: 34; }

@media (prefers-reduced-motion: reduce) {
  .area-name-neon { transition: none; }
}

/* The neon copy is set nowrap so it lands exactly on the original. On a narrow
   screen the original can wrap and the copy cannot, so it comes off. */
@media (max-width: 560px) {
  .area-name-neon { display: none; }
}

.area-desc {
  display: block;
  font-size: 14px;
  line-height: 1.55;
  color: var(--grey);
  margin-top: 8px;
  max-width: 46ch;
}

.area-go {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-bright);
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-10px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.area-row:hover .area-go,
.area-row:focus-visible .area-go {
  opacity: 1;
  transform: none;
  color: hsl(var(--hue, 272) 96% 76%);
}

.area-row:hover .area-num,
.area-row:focus-visible .area-num { color: hsl(var(--hue, 272) 96% 76%); }

.area-row:hover .area-name,
.area-row:focus-visible .area-name { transform: translateX(6px); }

/* The middle column holds the name and the description stacked. */
.area-row > .area-name { grid-column: 2; }
.area-row > .area-desc { grid-column: 2; grid-row: 2; }
.area-row > .area-num { grid-row: 1 / span 2; }
.area-row > .area-go { grid-row: 1 / span 2; }

/* ---------- Trabalho ---------- */

.work .section-title { margin-bottom: 26px; }

.work-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: clamp(26px, 4vw, 40px);
}

.filter {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--grey);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 100px;
  padding: 9px 20px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}

.filter:hover { color: var(--white); border-color: var(--line-strong); }

.filter.is-active {
  color: var(--ink);
  background: var(--white);
  border-color: var(--white);
}

/* Masonry via columns: handles mixed portrait/landscape without cropping. */
.work-grid {
  columns: 3;
  column-gap: 14px;
}

.work-grid figure {
  break-inside: avoid;
  margin: 0 0 14px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
}

.work-grid img {
  width: 100%;
  height: auto;
  transition: transform var(--dur-slow) var(--ease), opacity var(--dur) var(--ease);
}

.work-grid figure:hover img { transform: scale(1.03); }

/* Video tiles.
   The reels stay on Instagram: hosting them here would cost tens of MB each
   and the visitor would watch them without ever reaching the profile. The
   tile is a poster frame that links out, so it filters like any other item. */

.work-grid figure.is-video a {
  display: block;
  position: relative;
  line-height: 0;
  color: var(--white);
  text-decoration: none;
}

.work-grid figure.is-video a::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
              rgba(var(--ink-rgb), 0.78) 0%,
              rgba(var(--ink-rgb), 0.20) 42%,
              rgba(var(--ink-rgb), 0.05) 100%);
  opacity: 0.9;
  transition: opacity var(--dur) var(--ease);
}

.work-grid figure.is-video:hover a::after { opacity: 0.66; }

.video-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  width: 58px;
  height: 58px;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(var(--ink-rgb), 0.42);
  backdrop-filter: blur(6px);
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease);
}

.video-badge svg {
  width: 18px;
  height: 18px;
  margin-left: 3px; /* optical centring: the triangle's mass sits left */
  fill: var(--white);
}

.work-grid figure.is-video:hover .video-badge {
  transform: translate(-50%, -50%) scale(1.08);
  background: var(--accent);
}

.video-label {
  position: absolute;
  z-index: 1;
  left: 16px;
  bottom: 14px;
  right: 16px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.video-label span {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-bright);
}

/* The tile for a video we host ourselves. Same look as the link tiles: it is
   a button only because it opens a player instead of leaving the page. */

.work-grid figure.is-video .video-open {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  color: var(--white);
}

.work-grid figure.is-video .video-open:focus-visible {
  outline: 2px solid var(--accent-bright);
  outline-offset: 3px;
}

.video-dialog {
  max-width: min(1180px, 94vw);
  max-height: 92vh;
  padding: 0;
  border: 0;
  border-radius: 14px;
  background: #000;
  overflow: visible;
}

.video-dialog::backdrop {
  background: rgba(4, 4, 8, 0.86);
  backdrop-filter: blur(6px);
}

.video-dialog video {
  display: block;
  width: min(1180px, 94vw);
  max-height: 92vh;
  border-radius: 14px;
  background: #000;
}

.video-dialog-close {
  position: absolute;
  top: -14px;
  right: -14px;
  z-index: 1;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--ink);
  color: var(--white);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.video-dialog-close:hover {
  background: var(--accent);
  transform: scale(1.06);
}

.video-dialog-close:focus-visible {
  outline: 2px solid var(--accent-bright);
  outline-offset: 3px;
}

@media (max-width: 640px) {
  .video-dialog-close { top: -50px; right: 0; }
}

.work-grid figure.is-video a:focus-visible {
  outline: 2px solid var(--accent-bright);
  outline-offset: 3px;
}

.work-empty {
  color: var(--grey);
  font-size: 15px;
  padding: 20px 0;
}

/* ---------- Portfolio page header ---------- */

.page-header {
  /* The bar is fixed, so every page's first block has to clear it. At the
     clamp's own minimum this was 44px against a 71px bar. */
  padding: calc(var(--header-h) + clamp(28px, 4vw, 60px)) 0 clamp(20px, 3vw, 32px);
}

/* Full-bleed cover behind the portfolio page title. */
.page-header.has-cover {
  position: relative;
  min-height: 56svh;
  display: flex;
  align-items: flex-end;
  /* Bottom aligned, so the block grows upwards and needs the clearance kept
     even though the cover is tall. */
  padding-top: calc(var(--header-h) + clamp(12px, 2.5vh, 32px));
  padding-bottom: clamp(30px, 4.5vw, 56px);
  overflow: hidden;
}

.page-cover {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* The scrim is a pseudo-element so it stays above every slide: generated
   content paints after the real children. Weighted into the lower third,
   where the title sits, so the middle of the frame is not muddied. */
.page-cover::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Edge light first so it sits over the photo, then the darkening that the
     title needs. Without the first layer the cover's sides stay neutral while
     the page below them is lit, and the join reads as a band. */
  background:
    var(--edge-light),
    linear-gradient(to top,
      rgba(var(--ink-rgb), 1) 0%,
      rgba(var(--ink-rgb), 0.90) 7%,
      rgba(var(--ink-rgb), 0.78) 20%,
      rgba(var(--ink-rgb), 0.36) 54%,
      rgba(var(--ink-rgb), 0.18) 100%);
}

.cover-slide {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 800ms ease;
}

.cover-slide.is-on { opacity: 1; }

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

.page-header.has-cover .container {
  position: relative;
  z-index: 1;
}

.page-header .section-title { margin-bottom: 18px; }

.page-header .subtitle {
  font-size: 17px;
  line-height: 1.65;
  color: var(--grey);
  max-width: 56ch;
  text-wrap: pretty;
}

/* The page header already provides the top spacing. */
.page-header + .work { padding-top: 0; }

/* ---------- Portfolio call-to-action band ---------- */

.work-cta {
  text-align: center;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background-image:
    linear-gradient(rgba(0, 0, 0, 0.74), rgba(0, 0, 0, 0.86)),
    url("../assets/work/motorflow-lamera-cup-31.webp");
  background-size: cover;
  /* Not capa-website: that frame is already the first slide of the cover at
     the top of this same page, and seeing it twice on one screenful read as a
     mistake. This one is a different event entirely, and 43% puts the band on
     its subject. */
  background-position: center 43%;
}

/* The same band appears on both pages. The homepage keeps the Lamera Cup
   frame; the portfolio page takes capa-website, which used to open its cover
   rotation and now appears only here. */
.work-cta--portfolio {
  background-image:
    linear-gradient(rgba(0, 0, 0, 0.74), rgba(0, 0, 0, 0.86)),
    url("../assets/work/capa-website.webp");
  /* The cars sit below the middle of the frame, and this band crops harder
     still because it is shorter. */
  background-position: center 75%;
}

.work-cta .section-title { margin-bottom: 18px; }

.work-cta .subtitle {
  font-size: 17px;
  line-height: 1.65;
  color: var(--grey);
  max-width: 58ch;
  margin: 0 auto clamp(26px, 3.5vw, 36px);
  text-wrap: pretty;
}

/* ---------- Equipa ---------- */

.team { text-align: center; }

.team .section-title { margin-bottom: clamp(32px, 4vw, 48px); }

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

/* Editorial card: the portrait fills the frame and the name sits on it,
   instead of a bordered box with the photo boxed inside. */
.team-card {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  isolation: isolate;
  background: var(--surface);
}

/* Portrait crop: the team shots are 2:3, a landscape frame beheaded them. */
.team-card img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease);
}

.team-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(0, 0, 0, 0.94) 0%,
    rgba(0, 0, 0, 0.55) 34%,
    rgba(0, 0, 0, 0.05) 62%);
}

/* Each person gets their own light. Same mechanism as the service cards:
   one --hue drives the outline, the glow and the beam below. */
.team-grid > :nth-child(1) { --hue: 272; }   /* Rodrigo */
.team-grid > :nth-child(2) { --hue: 322; }   /* Ariana  */
.team-grid > :nth-child(3) { --hue: 196; }   /* Nuno    */

.team-card img {
  filter: saturate(0.72) contrast(1.02);
}

.team-card:hover img,
.team-card:focus-within img {
  transform: scale(1.05);
  filter: saturate(1.05) contrast(1.04);
}

/* The neon outline. A conic gradient spun behind the card and clipped to a
   1px ring, so the light travels round the edge instead of just switching on.
   Behind ::after, which is the darkening the name needs. */
.team-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--spin, 0deg),
    transparent 0deg,
    hsl(var(--hue) 95% 66%) 60deg,
    hsl(var(--hue) 95% 78%) 120deg,
    transparent 190deg,
    transparent 360deg);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
          mask-composite: exclude;
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}

.team-card:hover::before,
.team-card:focus-within::before {
  opacity: 1;
  animation: team-spin 2.4s linear infinite;
}

@keyframes team-spin {
  to { --spin: 360deg; }
}

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

/* A wash of the person's colour over the portrait, strongest at the bottom
   where the name sits. */
.team-card .team-glow {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  background: linear-gradient(to top,
    hsl(var(--hue) 90% 52% / 0.42) 0%,
    hsl(var(--hue) 90% 58% / 0.12) 38%,
    transparent 70%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
  pointer-events: none;
}

.team-card:hover .team-glow,
.team-card:focus-within .team-glow { opacity: 1; }

/* The rest of the row steps back, so the row reacts as a unit.

   Pointer devices only. The cards are not links, so on a touch screen a tap
   would dim the other two and leave them dimmed until something else was
   tapped — the same trap the client hit on the client logos. Keyboard focus
   keeps the effect, because focus moves on and clears itself. */
@media (hover: hover) and (pointer: fine) {
  .team-grid:has(.team-card:hover) .team-card:not(:hover) {
    opacity: 0.45;
    transform: scale(0.985);
  }
}

.team-grid:has(.team-card:focus-within) .team-card:not(:focus-within) {
  opacity: 0.45;
  transform: scale(0.985);
}

.team-card {
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

.team-card:hover,
.team-card:focus-within {
  box-shadow: 0 30px 70px -32px hsl(var(--hue) 90% 50% / 0.55);
}

.team-card-body {
  position: absolute;
  z-index: 3;   /* above the glow (1) and the travelling outline (2) */
  left: 0;
  right: 0;
  bottom: 0;
  padding: 24px 22px 22px;
  text-align: left;
}

/* The name is always there — it has to be readable without a mouse. What
   hover adds is the rule growing out from under it and the role arriving. */
.team-card h3 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(18px, 1.9vw, 22px);
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: -0.015em;
  transition: transform var(--dur) var(--ease), text-shadow var(--dur) var(--ease);
}

.team-card h3::after {
  content: "";
  display: block;
  height: 2px;
  width: 100%;
  margin-top: 10px;
  border-radius: 2px;
  background: hsl(var(--hue) 95% 68%);
  box-shadow: 0 0 14px hsl(var(--hue) 95% 60% / 0.9);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}

.team-card:hover h3,
.team-card:focus-within h3 {
  transform: translateY(-2px);
  text-shadow: 0 0 22px hsl(var(--hue) 95% 70% / 0.55);
}

.team-card:hover h3::after,
.team-card:focus-within h3::after { transform: scaleX(1); }

.team-card p {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-bright);
  margin-top: 8px;
  opacity: 0.62;
  transform: translateY(6px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease),
              color var(--dur) var(--ease);
}

.team-card:hover p,
.team-card:focus-within p {
  opacity: 1;
  transform: none;
  color: hsl(var(--hue) 95% 78%);
}

/* ---------- The beam ----------
   One line under all three. At rest it is a faint rule; hovering a card slides
   the lit segment under that person and takes their colour. This is the piece
   that makes the row read as one team. */
.team-beam {
  position: relative;
  height: 2px;
  margin-top: 26px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.10);
  overflow: hidden;
}

.team-beam::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: calc((100% - 44px) / 3);
  border-radius: 2px;
  background: hsl(var(--beam-hue, 272) 95% 66%);
  box-shadow: 0 0 18px hsl(var(--beam-hue, 272) 95% 60% / 0.85);
  opacity: 0;
  transform: translateX(var(--beam-x, 0));
  transition: transform 520ms var(--ease), opacity var(--dur) var(--ease),
              background var(--dur) var(--ease);
}

/* The gap is 22px, so each further card is one column plus one gap along. */
.team:has(.team-grid > :nth-child(1):hover) .team-beam::after,
.team:has(.team-grid > :nth-child(1):focus-within) .team-beam::after {
  opacity: 1; --beam-hue: 272; --beam-x: 0;
}

.team:has(.team-grid > :nth-child(2):hover) .team-beam::after,
.team:has(.team-grid > :nth-child(2):focus-within) .team-beam::after {
  opacity: 1; --beam-hue: 322; --beam-x: calc(100% + 22px);
}

.team:has(.team-grid > :nth-child(3):hover) .team-beam::after,
.team:has(.team-grid > :nth-child(3):focus-within) .team-beam::after {
  opacity: 1; --beam-hue: 196; --beam-x: calc(200% + 44px);
}

@media (max-width: 720px) {
  .team-beam { display: none; }
}

/* ---------- Clientes ---------- */

.clients { text-align: center; }

/* No bottom margin: the marquee supplies its own top spacing. */

/* ---------- Client marquee ---------- */

.marquee {
  margin-top: clamp(28px, 4vw, 44px);
  overflow: hidden;
  /* Fade the ends so logos enter and leave instead of being chopped off. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: marquee-scroll 38s linear infinite;
}

.marquee-row {
  display: flex;
  align-items: center;
  gap: clamp(26px, 4.2vw, 60px);
  padding-right: clamp(26px, 4.2vw, 60px);
  list-style: none;
  flex-shrink: 0;
}

/* Each logo is two images stacked in one grid cell: the white silhouette by
   default, the original artwork cross-fading in underneath the pointer. The
   track does not stop for it — a row that halts whenever the pointer crosses
   it reads as broken, and the pointer crosses it by accident far more often
   than on purpose. */
.marquee-row li {
  display: grid;
  place-items: center;
  padding: 10px 16px;
}

.marquee-row li > img {
  grid-area: 1 / 1;
  height: clamp(52px, 6.5vw, 86px);
  width: auto;
  max-width: none;
  object-fit: contain;
  transition: opacity var(--dur) var(--ease);
}

.logo-mono { opacity: 0.7; }
.logo-color { opacity: 0; }

/* Only where there is a real pointer. A touch screen has no hover: the
   browser fakes one on tap and then keeps it until something else is tapped,
   which is why a logo stayed coloured and the row appeared to seize up until
   you tapped elsewhere. */
@media (hover: hover) and (pointer: fine) {
  .marquee-row li:hover .logo-mono { opacity: 0; }
  .marquee-row li:hover .logo-color { opacity: 1; }
}

/* NOTE: three of the seven marks are dark artwork drawn for light backgrounds
   (JG Car, Supercar House, Different Auto) and read dimly in colour on black.
   A light plate behind them would fix it but would erase the three that are
   white artwork (CSC, Aura, AG Motors), so the hover stays uniform and the
   real fix is a light-background version of those three logos from the client. */

/* Four copies, sliding exactly one copy (-25%). Two copies left a gap on wide
   screens: the track has to stay wider than the viewport plus the travel. */
@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-25%); }
}

/* No pause control, by the client's decision on 2026-09-23. WCAG 2.2.2 asks
   for one on anything that moves by itself for more than five seconds, and
   what stands in for it here is the rule below: for anyone who has asked their
   system for less motion, the row does not move at all and becomes a plain
   swipeable strip. That covers the people the rule exists for, and not the
   rest. Reinstating it is a small button beside the row — it was offered and
   declined, so leave it out rather than adding it back as a tidy-up. */

/* Motion off: leave a static, swipeable row rather than a frozen half-view. */
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee-track { animation: none; }
  .marquee-row[aria-hidden="true"] { display: none; }
}

/* ---------- Contacto ---------- */

.contact-lead {
  font-size: clamp(16px, 1.7vw, 19px);
  line-height: 1.6;
  color: var(--grey);
  max-width: 62ch;
  margin-top: 18px;
  text-wrap: pretty;
}

/* Where we work. Set out as three plain statements so nobody has to email
   just to ask whether we travel. */
.coverage {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 1.8vw, 24px);
  margin: clamp(30px, 4vw, 48px) 0 clamp(36px, 5vw, 60px);
}

/* Same per-card hue system as the services grid, so the two sections read as
   one family rather than two unrelated card styles. */
.coverage > :nth-child(1) { --hue: 272; }
.coverage > :nth-child(2) { --hue: 206; }
.coverage > :nth-child(3) { --hue: 322; }

.coverage li {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: clamp(320px, 28vw, 400px);
  border: 1px solid var(--line);
  border-radius: 24px;
  padding: clamp(30px, 3vw, 44px) clamp(26px, 2.6vw, 38px) clamp(26px, 2.4vw, 34px);
  background:
    radial-gradient(130% 95% at 50% -10%, hsl(var(--hue) 88% 62% / 0.18), transparent 62%),
    var(--surface);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transform: perspective(900px);
  transform-style: preserve-3d;
  will-change: transform;
  transition: transform 300ms var(--ease),
              border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

.coverage li::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, hsl(var(--hue) 92% 70%), transparent);
  opacity: 0.75;
}

.coverage li::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(360px circle at var(--mx, 50%) var(--my, 0%),
              hsl(var(--hue) 92% 64% / 0.24), transparent 70%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}

.coverage li:hover::after { opacity: 1; }

.coverage li:hover {
  border-color: hsl(var(--hue) 70% 62% / 0.55);
  box-shadow:
    0 30px 62px -30px rgba(0, 0, 0, 0.85),
    0 12px 26px -20px hsl(var(--hue) 75% 45% / 0.4);
}

/* Ghosted index, outlined rather than filled so it sits behind the text. */
.coverage-num {
  position: absolute;
  top: 2px;
  right: clamp(18px, 2vw, 28px);
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(96px, 9vw, 140px);
  line-height: 1;
  letter-spacing: -0.06em;
  color: transparent;
  -webkit-text-stroke: 1.5px hsl(var(--hue) 60% 70% / 0.22);
  pointer-events: none;
  user-select: none;
}

.coverage-tag {
  display: block;
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(32px, 2.9vw, 46px);
  line-height: 1.1;
  color: hsl(var(--hue) 85% 74%);
  margin-bottom: clamp(14px, 1.6vw, 20px);
}

.coverage h3 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(24px, 2.9vw, 40px);
  line-height: 1.0;
  text-transform: uppercase;
  letter-spacing: -0.035em;
  margin-bottom: 14px;
  text-wrap: balance;
}

.coverage p {
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.6;
  color: var(--grey);
  max-width: 40ch;
  text-wrap: pretty;
}

/* Pinned to the bottom so the three cards line up however long the copy is. */
.coverage-foot {
  margin-top: auto;
  padding-top: clamp(22px, 2.4vw, 32px);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: hsl(var(--hue) 60% 82%);
}

.coverage-foot::before {
  content: "";
  display: block;
  width: 28px;
  height: 2px;
  background: hsl(var(--hue) 85% 62%);
  margin-bottom: 14px;
}

@media (prefers-reduced-motion: reduce) {
  .coverage li { transform: none !important; }
}

.contact-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}

/* The email is the primary action, so it is set as a headline rather than as
   the first of four identical boxes. Socials drop to a compact pill row, which
   also stops the column running out halfway down beside a tall form. */
.contact-info {
  display: flex;
  flex-direction: column;
  gap: clamp(26px, 3vw, 40px);
}

.contact-label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--grey);
  margin-bottom: 12px;
}

.contact-primary {
  position: relative;
  display: block;
  padding: clamp(24px, 2.6vw, 34px) clamp(22px, 2.4vw, 30px);
  border: 1px solid var(--line);
  border-radius: 20px;
  background:
    radial-gradient(120% 140% at 0% 0%, hsl(272 88% 62% / 0.20), transparent 62%),
    var(--surface);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  text-decoration: none;
  color: var(--white);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.contact-primary:hover {
  border-color: hsl(272 70% 64% / 0.55);
  transform: translateY(-3px);
}

.contact-value {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(19px, 2.2vw, 30px);
  line-height: 1.1;
  letter-spacing: -0.03em;
  word-break: break-word;
}

.contact-arrow {
  position: absolute;
  top: clamp(24px, 2.6vw, 34px);
  right: clamp(22px, 2.4vw, 30px);
  font-size: 20px;
  color: var(--accent-bright);
  transition: transform var(--dur) var(--ease);
}

.contact-primary:hover .contact-arrow { transform: translateX(5px); }

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

.social-pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 11px 18px;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--surface);
  text-decoration: none;
  color: var(--white);
  font-size: 14px;
  font-weight: 500;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.social-pill:hover { border-color: var(--line-strong); background: var(--surface-raised); }

.social-pill img { width: 20px; height: 20px; object-fit: contain; }

.contact-reply {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  color: var(--grey);
}

.contact-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #5ad18a;
  flex-shrink: 0;
}

.form-card {
  background: var(--surface);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: clamp(28px, 4vw, 40px);
}

.field { margin-bottom: 18px; }

/* Pairs short inputs side by side so the form stops towering over the
   column beside it. */
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.field label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--grey);
  margin-bottom: 7px;
}

.field .optional { color: #8b8b96; font-weight: 400; }

/* Lighter than the card, not darker: filled at --ink they read as holes
   punched in the panel rather than as fields to type in. */
.form-card input,
.form-card textarea {
  width: 100%;
  background: rgba(255, 255, 255, 0.05);
  color: var(--white);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 12px;
  padding: 14px 16px;
  font-family: var(--font-body);
  font-size: 15px;
  transition: border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}

.form-card input::placeholder,
.form-card textarea::placeholder { color: #7c7c86; }

.form-card input:hover,
.form-card textarea:hover {
  border-color: rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.07);
}

.form-card input:focus,
.form-card textarea:focus {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--accent-bright);
}

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

.form-card .btn-solid { width: 100%; justify-content: center; padding: 14px 0; }

/* The honeypot has to be reachable by a bot filling every input and invisible
   to everyone else. Not display:none and not hidden — both are the first
   things a bot skips. */
.honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  margin-top: 14px;
  font-size: 14px;
  line-height: 1.45;
}

.form-status:empty { margin-top: 0; }

.form-status.is-ok { color: var(--ok); }
.form-status.is-error { color: var(--danger); }

#contact-form button[disabled] {
  opacity: 0.6;
  cursor: progress;
}

.form-card .form-divider {
  height: 1px;
  border: none;
  background: var(--line);
  margin: 28px 0 18px;
}

.form-card .form-note {
  font-size: 13px;
  text-align: center;
  color: var(--grey);
}

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

.site-footer {
  border-top: 1px solid var(--line);
  padding: 36px 0 28px;
}

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

.footer-top .logo img { height: 40px; }

.footer-links {
  display: flex;
  gap: 24px;
  list-style: none;
  flex-wrap: wrap;
}

.footer-links a {
  font-size: 13px;
  color: var(--grey);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}

.footer-links a:hover { color: var(--white); }

.footer-social { display: flex; gap: 14px; align-items: center; }

.footer-social img {
  width: 26px;
  height: 26px;
  object-fit: contain;
  opacity: 0.7;
  transition: opacity var(--dur-fast) var(--ease);
}

.footer-social a:hover img { opacity: 1; }

.copyright {
  text-align: center;
  font-size: 13px;
  color: #8b8b96;
  margin-top: 28px;
}

/* ---------- Legal pages ---------- */

.legal-content { max-width: 760px; margin: 0 auto; }

.legal-content h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(30px, 4vw, 44px);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  margin-bottom: 28px;
}

.legal-content h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 21px;
  margin: 30px 0 12px;
}

.legal-content p,
.legal-content li {
  font-size: 15px;
  line-height: 1.7;
  color: var(--grey);
  margin-bottom: 12px;
}

.legal-content ul,
.legal-content ol { padding-left: 22px; }

/* Clause numbering. The legal text is written as numbered points, so it is
   marked up as one rather than as sentences that happen to start with a
   number: a screen reader then announces the count and the position. */
.legal-content ol { counter-reset: none; }

.legal-content li::marker { color: var(--grey); }

/* The standing note at the top of each document, set apart from the clauses
   that follow it. */
.legal-note {
  margin-bottom: 30px;
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent-bright);
  border-radius: 10px;
  background: var(--surface);
}

.legal-content .legal-note p { margin-bottom: 0; }

.legal-content h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  margin: 24px 0 10px;
  color: var(--white);
}

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

@media (max-width: 980px) {
  .team-grid { grid-template-columns: 1fr; max-width: 420px; margin: 0 auto; }
  .contact-layout { grid-template-columns: 1fr; }
  .work-grid { columns: 2; }
}

@media (max-width: 640px) {
  .nav-toggle { display: block; }

  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    background: rgba(0, 0, 0, 0.97);
    border-bottom: 1px solid var(--line);
    padding: 8px 0 16px;
    text-align: center;
    margin-left: 0;
  }

  .nav-links.open { display: flex; }

  .nav-links a { display: block; padding: 13px 0; }

  .header-cta { display: none; }

  .hero { min-height: 88svh; }
  .hero .script-accent {
    font-size: 1.12em;
    line-height: 1;
  }
  .hero-actions .btn { flex: 1 1 auto; justify-content: center; }

  .services-grid { grid-template-columns: 1fr; }
  .work-grid { columns: 1; }
  .coverage { grid-template-columns: 1fr; }
  .field-row { grid-template-columns: 1fr; gap: 0; }

  /* The areas row drops its call-to-action and lets the name wrap. */
  .area-row { grid-template-columns: auto minmax(0, 1fr); }
  .area-go { display: none; }
  .area-desc { font-size: 13px; }
}
