/* Self-hosted — generated by _build/fetch_fonts.py, do not edit. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url(../fonts/inter-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url(../fonts/inter-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url(../fonts/inter-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url(../fonts/inter-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: italic;
  font-weight: 500 600;
  font-display: swap;
  src: url(../fonts/jakarta-latin-ext-italic.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: italic;
  font-weight: 500 600;
  font-display: swap;
  src: url(../fonts/jakarta-latin-italic.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url(../fonts/jakarta-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url(../fonts/jakarta-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  --ic-gobag: url("../gobag-icon.webp");
  --ic-fw: url("../FW.webp");
  --ic-mark: url("../mark.webp");
  --img-art: url("../halo-art.webp");
  --img-band: url("../halo-band.webp");
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Anything the script hides stays hidden. A rule further down that sets a
   display — .consent and .mic-btn both do — otherwise outranks the browser's
   own [hidden] rule and the element can never be put away. */
[hidden] { display: none !important; }

    /* ══════════════════════════════════════════
       Tokens — light is the base
       ══════════════════════════════════════════ */
    :root {
      --paper:       #FAFAFB;
      --surface:     #FFFFFF;
      --sunken:      #F1F0F4;
      --ink:         #0F0E13;
      --mid:         #56525E;
      --faint:       #6E6979;
      --hair:        #E2E0E7;
      --hair-soft:   #EDECF1;
      --accent:      #36187C;
      --accent-ink:  #FFFFFF;
      --accent-soft: rgba(54,24,124,0.07);
      --accent-line: rgba(54,24,124,0.22);
      /* These were once two steps either side of the accent. The palette was
         later flattened to the single violet, and they are kept as aliases so
         the rules that read them still say which end of the travel they mean —
         and so restoring the spread is a change to these two lines. Note that
         while they are aliases --sweep below renders as a flat fill. */
      --accent-lit:  var(--accent);
      --accent-deep: var(--accent);

      /* The near-black with a violet cast that the service-tile icon plates
         are drawn on. */
      --tile-ink:   #15121D;

      /* The second step of the same violet, not a second hue. The accent
         still states the work; this one only marks what moves — scroll
         progress, a card under the pointer, the word the headline lands on.
         --accent2 clears 4.8:1 on paper and is safe for text; --accent2-lit
         is for glows and washes only. */
      --accent2:      var(--accent);
      --accent2-lit:  var(--accent);
      --accent2-soft: rgba(54,24,124,0.09);
      --accent2-line: rgba(54,24,124,0.26);

      /* The closing band. The footer below it is the accent at full strength;
         this is the same violet let almost all the way out, so the page warms
         into its ending over two steps instead of switching colour at the very
         last edge. Light enough that the ink, the mid grey and the faint grey
         all keep their contrast on it. */
      --cta-top:    #F5F2FC;
      --cta-ground: #E9E2F7;
      /* the travel from deep to light, reused by every moving edge */
      --sweep: linear-gradient(90deg, var(--accent-deep), var(--accent) 30%, var(--accent-lit) 62%, var(--accent2-lit) 100%);
      --shadow:      0 1px 2px rgba(15,14,19,0.05), 0 8px 24px rgba(15,14,19,0.05);
      --shadow-lift: 0 2px 4px rgba(15,14,19,0.06), 0 18px 44px rgba(15,14,19,0.10);

      /* Plus Jakarta Sans has no basic Cyrillic; Inter covers it and is the
         other half of the same pairing, so /ru/ stays on-brand. */
      --f-display: 'Plus Jakarta Sans', 'Inter', system-ui, -apple-system, sans-serif;
      --f-body:    'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

      /* the page leans left: no centred column, a viewport-sized gutter, so
         copy starts near the edge instead of drifting inwards on a wide screen */
      --shell: none;
      --gut:   clamp(20px, 7vw, 180px);
      /* the card rows are centred on this rather than filling the shell, and
         the hero copy stands on its edge — one number for the whole column */
      --col:   1240px;
      --nav-h: 68px;

      /* the page is drawn in one light set: this stops a browser deciding to
         invert it on a dark system and guessing at every colour */
      color-scheme: light;
    }


    /* ══════════════════════════════════════════
       Base
       ══════════════════════════════════════════ */
    html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: 90px; }

    body {
      font-family: var(--f-body);
      font-size: 16px;
      line-height: 1.6;
      background: var(--paper);
      color: var(--ink);
      -webkit-font-smoothing: antialiased;
      overflow-x: hidden;
    }
    body.nav-open { overflow: hidden; }

    .shell { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gut); }

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

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

    .skip {
      position: absolute; left: -9999px; top: 0; z-index: 300;
      background: var(--accent); color: var(--accent-ink);
      padding: 12px 20px; border-radius: 0 0 8px 0; font-weight: 600; font-size: 14px;
    }
    .skip:focus { left: 0; }

    /* ── shared type ── */
    .eyebrow {
      font-size: 11px; font-weight: 600; letter-spacing: 0.16em;
      text-transform: uppercase; color: var(--accent);
    }

    .sec-title {
      font-family: var(--f-display);
      font-weight: 800;
      font-size: clamp(27px, 3.7vw, 40px);
      line-height: 1.15;
      letter-spacing: -0.018em;
      text-wrap: balance;
      color: var(--ink);
    }

    .sec-desc {
      font-size: 16.5px; line-height: 1.65; color: var(--mid);
      max-width: 56ch;
    }

    .sec-head { margin-bottom: clamp(32px, 4.6vw, 52px); }
    .sec-head .eyebrow { display: block; margin-bottom: 14px; }
    .sec-head .sec-desc { margin-top: 16px; }

    /* The two card sections centre their contents, so the header above them
       is centred too — a left-hung title over a centred row reads as a
       mistake. The description is width-capped, so it needs the auto margins
       as well as the text alignment. */
    #build .sec-head, #services .sec-head { text-align: center; }
    #build .sec-head .sec-desc,
    #services .sec-head .sec-desc { margin-inline: auto; }

    section { padding: clamp(64px, 9vw, 118px) 0; }

    .rule { height: 1px; background: var(--hair); }

    /* ══════════════════════════════════════════
       Nav
       ══════════════════════════════════════════ */
    .nav-spacer { height: var(--nav-h); }

    /* the bar is a slab held a few millimetres off the page: light catches its
       top edge, and once the page scrolls underneath it casts a real shadow */
    nav {
      position: fixed; top: 0; left: 0; right: 0; z-index: 100;
      height: var(--nav-h);
      background:
        linear-gradient(180deg, rgba(255,255,255,0.55), rgba(255,255,255,0) 46%),
        color-mix(in srgb, var(--paper) 88%, transparent);
      backdrop-filter: saturate(1.6) blur(14px);
      -webkit-backdrop-filter: saturate(1.6) blur(14px);
      border-bottom: 1px solid transparent;
      transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.3s ease;
    }
    nav.scrolled {
      border-bottom-color: var(--hair);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.85),
                  0 10px 20px -14px rgba(15,14,19,0.34),
                  0 26px 44px -30px rgba(15,14,19,0.40);
    }

    /* one vanishing point for the whole bar */
    .nav-in {
      max-width: var(--shell); margin: 0 auto; padding: 0 var(--gut);
      height: 100%; display: flex; align-items: center; gap: 18px;
      perspective: 1400px;
    }

    /* The company label: the bird stands free on the paper, no plate behind
       it and no movement — it sits still while the nav keys do the reacting.
       The mark is the hero illustration with the wall keyed out from behind
       it, so what the bar carries is the bird's own edge and the corner of
       stone he is standing on, not a tile of anything; the box below is their
       proportions, not a square they have been fitted into. */
    .nav-logo {
      display: inline-flex; align-items: center; gap: 9px;
      font-family: var(--f-display);
      font-size: 23px; font-weight: 800; letter-spacing: -0.02em;
      color: var(--ink); flex-shrink: 0;
    }
    .nav-mark {
      width: 26px; height: 34px; flex-shrink: 0;
      background: var(--ic-mark) center / contain no-repeat;
    }
    @media (max-width: 420px) {
      .nav-logo { gap: 7px; }
      .nav-mark { width: 23px; height: 30px; }
    }
    .nav-logo em {
      font-style: normal; color: var(--accent);
    }

    .nav-links {
      display: flex; gap: 4px; list-style: none; margin-left: auto;
    }
    /* Each link is a key: the tile lifts off the bar on hover and a click
       presses it back down. The label itself stays flat and still. */
    .nav-links a {
      display: block; padding: 8px 14px; border-radius: 100px;
      font-size: 14.5px; font-weight: 500; color: var(--mid);
      white-space: nowrap;
      transition: color 0.18s ease, background 0.18s ease, box-shadow 0.2s ease,
                  transform 0.2s cubic-bezier(0.2, 0.75, 0.25, 1);
    }
    .nav-links a.active,
    .nav-links a[aria-current="page"] {
      color: var(--accent);
      background: var(--accent-soft);
      box-shadow: inset 0 0 0 1px var(--accent-line),
                  inset 0 2px 3px -1px rgba(15,14,19,0.09);
    }
    .nav-links a:hover {
      color: var(--ink);
      background: var(--surface);
      transform: translateY(-2px);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.9),
                  0 1px 1px rgba(15,14,19,0.05),
                  0 7px 15px -7px rgba(15,14,19,0.28);
    }
    .nav-links a:active {
      transform: translateY(1px);
      box-shadow: inset 0 2px 4px rgba(15,14,19,0.12);
    }

    .nav-tools { display: flex; align-items: center; gap: 8px; margin-left: 10px; }

    /* the tool buttons sit on a visible edge, and travel down onto the bar
       when pressed */
    .icon-btn {
      width: 38px; height: 38px; flex-shrink: 0;
      display: inline-flex; align-items: center; justify-content: center;
      border: 1px solid var(--hair); border-radius: 50%;
      background: var(--surface); color: var(--mid); cursor: pointer;
      box-shadow: 0 2px 0 var(--hair-soft), 0 5px 10px -7px rgba(15,14,19,0.35);
      transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease,
                  transform 0.14s ease, box-shadow 0.14s ease;
    }
    .icon-btn:hover {
      color: var(--ink); border-color: var(--faint); background: var(--surface);
      transform: translateY(-1px);
      box-shadow: 0 3px 0 var(--hair-soft), 0 9px 16px -9px rgba(15,14,19,0.4);
    }
    .icon-btn:active {
      transform: translateY(2px);
      box-shadow: inset 0 2px 4px rgba(15,14,19,0.14);
    }

    /* language */
    .lang { position: relative; perspective: 900px; }
    .lang-btn {
      display: inline-flex; align-items: center; gap: 6px;
      height: 38px; padding: 0 12px;
      border: 1px solid var(--hair); border-radius: 100px;
      background: var(--surface); color: var(--mid); cursor: pointer;
      font-family: var(--f-body); font-size: 13px; font-weight: 600;
      letter-spacing: 0.04em;
      box-shadow: 0 2px 0 var(--hair-soft), 0 5px 10px -7px rgba(15,14,19,0.35);
      transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease,
                  transform 0.14s ease, box-shadow 0.14s ease;
    }
    .lang-btn:hover {
      color: var(--ink); border-color: var(--faint); background: var(--surface);
      transform: translateY(-1px);
      box-shadow: 0 3px 0 var(--hair-soft), 0 9px 16px -9px rgba(15,14,19,0.4);
    }
    .lang.open .lang-btn,
    .lang-btn:active {
      transform: translateY(2px);
      box-shadow: inset 0 2px 4px rgba(15,14,19,0.14);
    }
    .lang-btn svg { opacity: 0.75; }
    /* the panel is hinged on its top edge and swings down into view */
    .lang-menu {
      position: absolute; top: calc(100% + 8px); right: 0;
      min-width: 158px; padding: 6px;
      background: var(--surface); border: 1px solid var(--hair);
      border-radius: 11px; box-shadow: var(--shadow-lift);
      z-index: 110;
      transform-origin: top center;
      transform: rotateX(-78deg);
      opacity: 0; visibility: hidden;
      transition: transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1),
                  opacity 0.18s ease, visibility 0s linear 0.28s;
    }
    .lang.open .lang-menu {
      transform: none; opacity: 1; visibility: visible;
      transition: transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1),
                  opacity 0.18s ease, visibility 0s;
    }
    /* mobile menu */
    .burger { display: none; }

    .drawer {
      position: fixed; inset: 0; z-index: 200;
      background: var(--paper);
      display: flex; flex-direction: column;
      padding: 0 var(--gut) 40px;
      /* the menu is taller than a short phone in landscape, and taller than
         an SE even upright: it has to scroll, or the languages at the foot of
         it can never be reached. The chain stops here so the page behind does
         not scroll once the list hits its end. */
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      overscroll-behavior: contain;
      transform: perspective(1200px) rotateX(-9deg) translateY(-12px);
      transform-origin: top center;
      opacity: 0; pointer-events: none; visibility: hidden;
      transition: opacity 0.22s ease,
                  transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1),
                  visibility 0s linear 0.28s;
    }
    .drawer.open {
      opacity: 1; transform: none; pointer-events: auto; visibility: visible;
      transition: opacity 0.22s ease,
                  transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1),
                  visibility 0s;
    }
    .drawer-top {
      height: var(--nav-h); display: flex; align-items: center;
      flex-shrink: 0;
    }
    .drawer-top .nav-logo { margin-right: auto; }
    .drawer-links {
      list-style: none; display: flex; flex-direction: column;
      margin-top: 24px; border-top: 1px solid var(--hair);
    }
    .drawer-links a {
      display: flex; align-items: center; justify-content: space-between;
      padding: 20px 2px; border-bottom: 1px solid var(--hair);
      font-family: var(--f-display); font-size: 27px; font-weight: 700;
      letter-spacing: -0.015em; color: var(--ink);
      transform-origin: left center;
      transition: transform 0.2s cubic-bezier(0.2, 0.75, 0.25, 1), color 0.18s ease;
    }
    /* touch pushes the row back into the page */
    .drawer-links a:active {
      color: var(--accent);
      transform: perspective(700px) rotateY(4deg) translateZ(-10px);
    }
    .drawer-cta {
      display: flex; align-items: center; justify-content: center; gap: 9px;
      margin-top: 28px; padding: 17px 24px; border-radius: 100px;
      background: var(--accent); color: var(--accent-ink);
      font-size: 15.5px; font-weight: 600;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.22),
                  0 4px 0 color-mix(in srgb, var(--accent) 60%, #000),
                  0 12px 22px -12px rgba(54,24,124,0.6);
      transition: transform 0.14s ease, box-shadow 0.14s ease;
    }
    .drawer-cta:active {
      transform: translateY(4px);
      box-shadow: inset 0 2px 5px rgba(15,14,19,0.35);
    }
    .drawer-langs {
      display: flex; gap: 8px; margin-top: auto; padding-top: 32px; flex-wrap: wrap;
    }
    /* ══════════════════════════════════════════
       Buttons
       ══════════════════════════════════════════ */
    .btn {
      display: inline-flex; align-items: center; gap: 9px;
      padding: 14px 24px; border-radius: 100px;
      font-size: 15px; font-weight: 600; letter-spacing: -0.005em;
      border: 1px solid transparent; cursor: pointer;
      transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
    }
    .btn svg { flex-shrink: 0; }
    .btn-primary {
      background: var(--accent); color: var(--accent-ink);
      box-shadow: 0 1px 2px rgba(15,14,19,0.10);
    }
    .btn-primary:hover { background: color-mix(in srgb, var(--accent) 86%, var(--ink)); }
    .btn-ghost {
      background: transparent; color: var(--ink); border-color: var(--hair);
    }
    .btn-ghost:hover { border-color: var(--faint); background: var(--surface); }

    /* the arrow rides in its own disc at the right end of the label */
    .btn-arrow { gap: 14px; padding: 8px 8px 8px 24px; }
    .btn-arrow svg {
      width: 15px; height: 15px; padding: 8px; box-sizing: content-box;
      border-radius: 50%; background: var(--accent-ink); color: var(--accent);
      transition: transform 0.2s cubic-bezier(0.2, 0.75, 0.25, 1);
    }
    .btn-arrow:hover svg { transform: translateX(3px); }

    /* ══════════════════════════════════════════
       Hero
       ══════════════════════════════════════════ */
    /* Tall hero, copy vertically centred in a left-hand column.
       The drawing is the band now, not a card standing in it. It runs the full
       width of the page, edge to edge, and the copy is laid on it. Two earlier
       backdrops failed at exactly this and for the same reason both times: a
       tint was laid over the whole picture and then had to be taken off again
       wherever the words fell. This one does not tint the picture at all. It
       lays paper on it — the same --paper the sections below are drawn on —
       opaque under the headline, thinning across the middle, and gone before
       it reaches the bird. So the copy is on the page's own ground, the
       picture is at full strength where it is the picture, and the join
       between them is a long fall rather than a line. */
    #hero {
      position: relative;
      /* the layers below stack against the band and not against the page */
      isolation: isolate;
      /* the picture is sized to the band, so anything the crop leaves over
         the edge is the band's business */
      overflow: hidden;
      /* fills a laptop screen, but capped so a tall display
         does not turn the hero into an empty band */
      /* svh, not vh: on a phone browser vh is the tall viewport, the one you
         only get once the address bar has scrolled away, so everything sized
         in vh reserves more room than the screen is actually showing and the
         picture lands under the fold. The vh line stays first as the fallback
         for anything that does not know svh. */
      min-height: min(760px, calc(90vh - var(--nav-h)));
      min-height: min(760px, calc(90svh - var(--nav-h)));
      display: flex;
      align-items: center;
      padding: clamp(48px, 7vw, 88px) 0;
      background-color: var(--paper);
    }
    /* The copy column stands on the same edge as the card grids in the
       sections below, which sit on a centred 1240 column rather than on the
       page gutter. The max() tracks that edge at every width: the column wins
       on a wide screen, the gutter takes over once 1240 no longer fits. */
    #hero .shell {
      position: relative;
      /* clear of the blur at the head of the band, which is at 2 */
      z-index: 3;
      width: 100%;
      padding-inline: max(var(--gut), calc((100% - var(--col)) / 2));
    }
    /* The copy stops where the paper stops. Both are fractions of the band, so
       the words and the ground they are on widen and narrow together and the
       headline can never reach the part of the picture that is at full
       strength. */
    .hero-inner { max-width: min(52%, 700px); }
    .hero-proof { max-width: min(56%, 760px); }

    /* ── the picture ──
       It runs the whole width of the page, and it is the picture the whole
       way: the wall is carried on past the drawing's left edge, the cornice
       with it, out to the far side of the screen. That continuation is drawn
       into halo-band.webp rather than faked here — see _build/make_band.py for
       what is taken from the drawing and how. What is left for the stylesheet
       is where the plate sits: anchored to the right, so that whatever a given
       screen has to crop is taken off the far end of the wall, where there is
       nothing to lose, and never off the bird. */
    .hero-art {
      position: absolute;
      inset: 0;
      z-index: 0;
      overflow: hidden;
      background: var(--img-band) right center / cover no-repeat;
    }
    /* The top of the band goes out of focus. The plate is cut off by the top
       of the section whatever height the band is, and a picture that ends on a
       straight line under a translucent bar reads as a picture that ran out of
       room. Blurring the backdrop rather than laying anything over it means
       the colour stays exactly where it was and only the edges go — and it
       gives the glass in the bar something soft to sit on. */
    #hero::before {
      content: "";
      position: absolute;
      /* Only as tall as the blur reaches, rather than the whole band with the
         mask doing the limiting. WebKit has a long history of applying a
         backdrop-filter to an element's full box and ignoring the mask on it,
         and the failure there would be the entire hero going soft. Bounded
         this way the worst case is a blurred top strip with a hard edge, which
         is the intent minus the fade. */
      inset: 0 0 auto 0;
      height: 27%;
      /* Above the picture, and that is the whole of it. ::before is generated
         as the band's FIRST child, so at the same z-index it painted before
         .hero-art and the only thing behind it to filter was the flat
         background colour — the blur was a no-op for as long as it was there.
         A backdrop-filter has to be above what it is meant to be filtering. */
      z-index: 2;
      pointer-events: none;
      -webkit-backdrop-filter: blur(34px);
              backdrop-filter: blur(34px);
      -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 22%, transparent 100%);
              mask-image: linear-gradient(180deg, #000 0%, #000 22%, transparent 100%);
    }
    /* The paper the words stand on, and the soft edges that keep the band from
       ending in a ruled line. It sits above the picture and below the copy. */
    /* The band is the picture, all of it. There used to be paper laid across
       the copy side — a wide white panel that the headline stood on and the
       wall started after — and with a drawn wall out there now that panel was
       covering the thing it was meant to make room for. It is gone. What is
       left here closes the foot of the band into the section below, and
       nothing else. The copy holds its own against the wall by being darker,
       not by having the wall taken away from under it. */
    #hero::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 0;
      pointer-events: none;
      background: linear-gradient(0deg,
        var(--paper) 0%,
        color-mix(in srgb, var(--paper) 52%, transparent) 4%,
        transparent 12%);
    }

    /* ── the copy, standing on the wall ──
       The ochre is bright, so black type clears it easily and the violet has
       better than 6:1 on it. The two greys the copy used on paper do not, and
       both step down to near-ink here. This is the whole cost of taking the
       panel away. */
    #hero .hero-sub { color: #262130; }
    #hero .hero-proof {
      color: #453F4E;
      border-top-color: rgba(15, 14, 19, 0.16);
    }
    /* the outlined button was a hairline on white; on a wall it needs a plate
       under it or it reads as a gap in the picture */
    #hero .btn-ghost {
      background: rgba(255, 255, 255, 0.74);
      border-color: rgba(15, 14, 19, 0.14);
      -webkit-backdrop-filter: blur(6px);
              backdrop-filter: blur(6px);
    }
    #hero .btn-ghost:hover {
      background: rgba(255, 255, 255, 0.92);
      border-color: rgba(15, 14, 19, 0.24);
    }

    .hero-eyebrow {
      display: inline-flex; align-items: center; gap: 9px;
      font-size: 11.5px; font-weight: 600; letter-spacing: 0.15em;
      text-transform: uppercase; color: var(--accent);
      padding: 7px 14px; border-radius: 100px;
      border: 1px solid var(--accent-line); background: var(--accent-soft);
      margin-bottom: 30px;
    }
    .hero-eyebrow::before {
      content: ""; width: 6px; height: 6px; border-radius: 50%;
      background: currentColor;
    }

    /* three stacked lines — the breaks live in the copy (site.json) so every
       language keeps the same shape instead of wrapping wherever it lands */
    .hero-title {
      font-family: var(--f-display);
      font-weight: 800;
      font-size: clamp(34px, 7.4vw, 76px);
      line-height: 1.05;
      letter-spacing: -0.028em;
      margin-bottom: 26px;
    }
    .hero-title em { font-style: normal; color: var(--accent); }

    .hero-body { max-width: 580px; }

    .hero-sub {
      font-size: clamp(17px, 1.75vw, 19.5px);
      line-height: 1.6; color: var(--mid);
      max-width: 52ch; margin-bottom: 34px;
      /* flush left, natural rag — no justification, no hyphenation;
         pretty only guards against a single stranded word */
      text-align: left; text-wrap: pretty;
    }


    .hero-cta { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }

    .hero-proof {
      display: flex; flex-wrap: wrap; gap: 10px 26px;
      margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--hair);
      font-size: 13.5px; color: var(--faint);
    }
    .hero-proof span { display: inline-flex; align-items: center; gap: 8px; }
    .hero-proof span::before {
      content: ""; width: 4px; height: 4px; border-radius: 50%;
      background: var(--accent); opacity: 0.65; flex-shrink: 0;
    }

    /* ══════════════════════════════════════════
       Services
       ══════════════════════════════════════════ */
    /* Cards on a tight gap rather than one hairline wall: a tile welded into
       a wall cannot rise out of it, and the row has to answer the pointer.
       The gap is kept small so four of them still read as one band. */
    .svc-grid {
      display: grid; grid-template-columns: repeat(4, 1fr);
      gap: clamp(8px, 0.9vw, 12px);
      max-width: var(--col);
      margin: 0 auto;
    }
    /* These are the build panels in another row: a flat tinted ground, no
       wash, no light arriving on hover. The colour is the statement. */
    .svc {
      background: var(--surface);
      padding: clamp(14px, 1.8vw, 20px) clamp(12px, 1.3vw, 16px);
      display: flex; flex-direction: column; align-items: center;
      text-align: center;
      border: 1px solid var(--hair); border-radius: 14px; overflow: hidden;
      box-shadow: 0 1px 2px rgba(15,14,19,0.04);
      position: relative;
    }

    /* ── the four panels are coloured, and that is all they do ──
       The same two tints the build wall carries, alternating across the row:
       the site purple laid on paper at a low alpha, answered by the ink at
       the same strength. Nothing here moves or lights up on hover. */
    .svc-grid .svc:nth-child(odd) {
      background: rgba(54,24,124,0.045);
      border-color: rgba(54,24,124,0.11);
    }
    .svc-grid .svc:nth-child(even) {
      background: rgba(15,14,19,0.055);
      border-color: rgba(15,14,19,0.13);
    }

    /* the type is the ordinary paper set — muted means the ground is tinted,
       not that the words have to shout over it */
    .svc-grid .svc h3 { color: var(--ink); }
    .svc-grid .svc p { color: var(--mid); }
    /* the one hard note on the tile: a black plate with the glyph drawn in
       light violet, so the pair of colours is stated at the top of the card */
    .svc-icon {
      width: 36px; height: 36px; border-radius: 10px;
      display: flex; align-items: center; justify-content: center;
      background: linear-gradient(150deg, #241E36, var(--tile-ink));
      /* the plate stays near-black, so the glyph inverts to white — the one
         purple is the ground it sits on, not the mark */
      color: #FFFFFF;
      border: 1px solid rgba(54,24,124,0.30);
      box-shadow: 0 5px 14px rgba(21,18,29,0.20),
                  inset 0 1px 0 rgba(255,255,255,0.07);
      margin-bottom: 14px;
    }
    .svc h3 {
      font-family: var(--f-display); font-weight: 700;
      font-size: 19px; letter-spacing: -0.015em; line-height: 1.2;
      margin-bottom: 7px;
    }
    .svc p {
      font-size: 14.5px; line-height: 1.55; color: var(--mid); margin-bottom: 14px;
    }
    .svc-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-top: auto; }
    .chip {
      font-size: 11.5px; font-weight: 500; letter-spacing: 0.03em;
      padding: 5px 10px; border-radius: 6px;
      background: var(--sunken); color: var(--mid);
      border: 1px solid var(--hair-soft);
    }

    /* ══════════════════════════════════════════
       Badges and product links — shared by the
       projects page and the cards that link to it
       ══════════════════════════════════════════ */
    .badge {
      font-size: 10.5px; font-weight: 700; letter-spacing: 0.09em;
      text-transform: uppercase; padding: 4px 9px; border-radius: 5px;
      background: var(--accent-soft); color: var(--accent);
      border: 1px solid var(--accent-line); white-space: nowrap;
    }

    .work-links { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
    .work-link {
      display: inline-flex; align-items: center; gap: 7px;
      font-size: 14px; font-weight: 600; color: var(--accent);
      border-bottom: 1px solid transparent; padding-bottom: 1px;
      transition: border-color 0.18s ease;
    }
    .work-link:hover { border-bottom-color: currentColor; }
    .work-link svg { opacity: 0.8; }

    @media (max-height: 680px) {
      #hero { min-height: 0; }
    }

    /* ── narrow ──
       Side by side stops working long before a phone: the paper would have to
       cover almost the whole band to hold the headline, and covering the
       picture is the one thing this hero does not do. So the arrangement turns
       through ninety degrees. The copy keeps the top of the band on plain
       paper; the picture stands up out of the bottom of it, still at its own
       proportions, still dissolved into the field on the sides it does not
       fill. It is the same band read down the page instead of across it. */
    @media (max-width: 1040px) {
      /* The blur at the head of the band comes off. It is there to soften the
         picture's top edge under the bar, and from here down there is no such
         edge — the band opens on the field, which is already out of focus.
         Left on it would sit exactly where the bird now is, and a blurred bird
         is the one thing this arrangement cannot afford. */
      #hero::before { display: none; }
      #hero {
        align-items: flex-start;
        /* With the bird at the head of the band there is nothing to reserve
           room for underneath it, so the band closes on its contents. */
        min-height: 0;
        padding-top: clamp(28px, 5vw, 64px);
        padding-bottom: clamp(48px, 7vw, 96px);
      }
      /* the rule over the proof line closes up: on a phone the copy has to
         hand over to the picture inside one screen */
      .hero-proof { margin-top: 30px; padding-top: 20px; }
      .hero-inner, .hero-proof { max-width: none; }
      /* The wide plate is the wrong shape here — cropped to a phone it is all
         wall and no bird. The band goes back to the drawing's own file, thrown
         out of focus to make the field, with the picture standing on it. */
      .hero-art {
        background: none;
      }
      .hero-art::before {
        content: "";
        position: absolute;
        inset: -14%;
        background: var(--img-art) 42% 14% / auto 190% no-repeat;
        filter: blur(84px) saturate(1.24) brightness(1.04);
        transform: scaleX(1.08);
      }
      /* Top right, beside the headline — the same reading as the wide band,
         where the copy takes the left and the bird holds the right. Off the
         right edge rather than flush to it: flush, his head landed on the end
         of the longest line of copy, and pushed out he clears it without being
         made smaller. What goes off-screen is the wall to his right, the part
         with nothing in it.

         The lift is a transform and not a `top`, because a percentage on `top`
         is a percentage of the band and this has to be a fraction of the
         picture: the bird sits at 42-76% of his own frame, so lifting the
         frame by a third of itself brings him to the head of the band whatever
         height the frame is given. Everything above him is wall, and the band
         clips it.

         The height is led by the width of the screen and only capped by its
         height. That is what keeps him clear of the sentence at every size:
         the headline's own size tracks the viewport width too, so the two grow
         together and the bird's foot stays above the first line of body copy
         from 360px all the way to 1040. */
      .hero-art::after {
        content: "";
        position: absolute;
        background: var(--img-art) 50% 48% / cover no-repeat;
        top: 0;
        bottom: auto;
        right: -8%;
        transform: translateY(-33%);
        width: auto;
        aspect-ratio: 940 / 1253;
        max-width: 86%;
        height: min(clamp(300px, 60vw, 600px), 56vh);
        height: min(clamp(300px, 60vw, 600px), 56svh);
        /* the right edge runs off the screen and the top is clipped, so the
           two that are dissolved are the left, and the foot where the cornice
           passes behind the sentence */
        -webkit-mask-image:
          linear-gradient(90deg, transparent 0%, #000 28%),
          linear-gradient(180deg, #000 0%, #000 78%, transparent 100%);
        -webkit-mask-composite: source-in;
                mask-image:
          linear-gradient(90deg, transparent 0%, #000 28%),
          linear-gradient(180deg, #000 0%, #000 78%, transparent 100%);
                mask-composite: intersect;
      }
      /* the picture starts lower down here, so the blur at the head of the band
         has less to do — it only takes the edge off what shows past the copy */
      #hero::before {
        height: 16%;
        -webkit-backdrop-filter: blur(20px);
                backdrop-filter: blur(20px);
        -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 25%, transparent 100%);
                mask-image: linear-gradient(180deg, #000 0%, #000 25%, transparent 100%);
      }
      /* the field carries the whole band here too — only the foot closes */
      #hero::after {
        background: linear-gradient(0deg,
          var(--paper) 0%,
          color-mix(in srgb, var(--paper) 58%, transparent) 6%,
          transparent 20%);
      }
    }

    /* Phones: the picture takes what width is left after the gutters, so the
       bird is not a stamp in the middle of a field. */
    /* ── phones ──
       A phone screen is not tall enough to hold this copy set at reading size
       and then a picture underneath it, so on a phone the hero is closed up
       until it is: the two buttons share a row instead of stacking, and every
       gap in the block comes in. What that buys is the room the picture needs,
       which is given as a fraction of the small viewport so it is room the
       screen really has. The picture is then made taller than that room, so it
       starts above the fold and the bird is on the first screen rather than
       under it — the rest of him is the reason to scroll. */
    /* ── phones ──
       The arrangement is the one set above; what changes here is that a phone
       screen has no slack in it. The copy is closed up until the picture fits
       beside it — the two buttons share a row instead of stacking full width,
       and every gap in the block comes in — and the picture is held to a
       narrower range of heights than the width-led one above would give it,
       because down here the sentence runs the full width of the screen and
       there is nothing to spare between it and the bird. */
    @media (max-width: 560px) {
      #hero {
        padding-bottom: clamp(44px, 7vh, 84px);
        padding-bottom: clamp(44px, 7svh, 84px);
      }
      .hero-art::after {
        height: clamp(250px, 34vh, 340px);
        height: clamp(250px, 34svh, 340px);
      }
      .hero-title { margin-bottom: 16px; }
      .hero-sub { font-size: 16px; margin-bottom: 20px; max-width: none; }
      .hero-proof {
        margin-top: 18px; padding-top: 13px;
        font-size: 12.5px; gap: 6px 18px;
      }
      /* side by side while they fit, and stacked the moment they do not.
         The id keeps this ahead of the 640px rule that stacks them full
         width; nowrap ran them off the edge of a 360 screen. */
      #hero .hero-cta { gap: 10px; }
      #hero .hero-cta .btn {
        width: auto; flex: 0 1 auto; padding: 12px 16px;
        font-size: 14.5px; white-space: nowrap;
      }
      #hero .hero-cta .btn-arrow { gap: 9px; padding: 6px 6px 6px 16px; }
      #hero .hero-cta .btn-arrow svg { padding: 6px; }
    }

    /* the narrowest phones: the pair only shares a row if the labels shrink */
    @media (max-width: 380px) {
      #hero .hero-cta .btn { padding: 10px 12px; font-size: 13.5px; }
      #hero .hero-cta .btn-arrow { gap: 8px; padding: 5px 5px 5px 12px; }
      #hero .hero-cta .btn-arrow svg { width: 13px; height: 13px; padding: 5px; }
    }

    /* A short window has no room to stand the picture up under the copy, so
       the band goes back to reading across: the copy takes the left, the
       picture keeps the right, and the hero stays one screen tall.
       Landscape only. On height alone this also caught a small phone held
       upright — a 360x600 screen is under 620 too — and handed it a 60% copy
       column and a full-height picture down one side, which is the one shape
       that cannot work at that width. */
    @media (max-width: 1040px) and (max-height: 620px) and (orientation: landscape) {
      #hero {
        padding-top: clamp(20px, 3vw, 40px);
        padding-bottom: clamp(20px, 3vw, 40px);
        align-items: center;
      }
      .hero-inner, .hero-proof { max-width: 60%; }
      /* a landscape phone has about 320px of height to give: the display face
         comes down to something that leaves room for the sentence under it */
      .hero-title { font-size: clamp(26px, 4.4vw, 40px); margin-bottom: 12px; }
      .hero-sub { font-size: 15px; margin-bottom: 18px; }
      .hero-proof { margin-top: 16px; padding-top: 12px; font-size: 12.5px; }
      .hero-art::after {
        top: 0; bottom: 0; right: 0; left: auto;
        height: auto; transform: none;
        width: clamp(340px, 40vw, 620px);
        aspect-ratio: auto;
        max-width: none;
        -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 32%);
        -webkit-mask-composite: source-over;
                mask-image: linear-gradient(90deg, transparent 0%, #000 32%);
                mask-composite: add;
      }
      #hero::after {
        background: linear-gradient(0deg, var(--paper) 0%, transparent 16%);
      }
    }

    /* ══════════════════════════════════════════
       Process
       ══════════════════════════════════════════ */
    #process { background: var(--sunken); }

    /* the band behind the run: deep violet where the work starts, lighter
       violet where it ships, the same travel the end cards state */
    .steps-band {
      position: relative;
      border-radius: clamp(18px, 2vw, 26px);
      padding: clamp(18px, 2.4vw, 34px);
      border: 1px solid var(--hair-soft);
      background:
        radial-gradient(88% 128% at 2% 0%, rgba(54,24,124,0.16), transparent 60%),
        radial-gradient(88% 128% at 98% 100%, rgba(54,24,124,0.22), transparent 60%),
        var(--sunken);
    }

    .steps {
      display: grid; grid-template-columns: repeat(5, 1fr);
      gap: clamp(10px, 1vw, 16px);
    }

    /* the card answers the pointer: it lifts off the band, its shadow deepens
       and its numeral comes up to full accent */
    .step {
      position: relative;
      background: var(--surface);
      border: 1px solid var(--hair);
      border-radius: 14px;
      padding: clamp(20px, 2.2vw, 28px);
      box-shadow: 0 1px 2px rgba(15,14,19,0.04);
      transition: transform 0.26s cubic-bezier(0.2, 0.7, 0.2, 1),
                  box-shadow 0.26s ease, border-color 0.2s ease;
    }
    .step:hover {
      transform: translateY(-5px);
      box-shadow: var(--shadow-lift);
      border-color: var(--accent-line);
      z-index: 1;
    }

    .step .step-n {
      font-family: var(--f-display); font-weight: 800;
      font-size: clamp(30px, 3.2vw, 44px); line-height: 1;
      letter-spacing: -0.03em; color: var(--accent-line);
      margin-bottom: clamp(12px, 1.4vw, 18px);
      font-variant-numeric: tabular-nums;
      transition: color 0.2s ease;
    }
    .step:hover .step-n { color: var(--accent); }
    .step h3 {
      font-family: var(--f-display); font-weight: 700;
      font-size: clamp(17px, 1.7vw, 21px); line-height: 1.25;
      letter-spacing: -0.016em; margin-bottom: 10px;
    }
    .step p { font-size: 14.5px; line-height: 1.62; color: var(--mid); }

    /* the two ends carry the colour: the audit in ink, the scaling in the
       accent, with everything between left plain so the ends read as bookends */
    .step:first-child {
      background: var(--ink); border-color: var(--ink);
      box-shadow: 0 10px 30px rgba(15,14,19,0.18);
    }
    .step:first-child:hover { box-shadow: 0 18px 44px rgba(15,14,19,0.28); }
    .step:first-child h3 { color: var(--surface); }
    .step:first-child p { color: rgba(255,255,255,0.70); }
    .step:first-child .step-n { color: rgba(255,255,255,0.22); }
    .step:first-child:hover { border-color: var(--ink); }
    .step:first-child:hover .step-n { color: rgba(255,255,255,0.46); }

    .step:last-child {
      background: linear-gradient(150deg, var(--accent-lit), var(--accent) 58%, var(--accent-deep));
      border-color: var(--accent-deep);
      box-shadow: 0 10px 30px rgba(54,24,124,0.26);
    }
    .step:last-child:hover { box-shadow: 0 18px 44px rgba(54,24,124,0.38); }
    .step:last-child h3 { color: var(--accent-ink); }
    .step:last-child p { color: rgba(255,255,255,0.78); }
    .step:last-child .step-n { color: rgba(255,255,255,0.26); }
    .step:last-child:hover { border-color: var(--accent-deep); }
    .step:last-child:hover .step-n { color: rgba(255,255,255,0.52); }

    /* ══════════════════════════════════════════
       About — the city line under the studio copy;
       the page itself borrows the shared page-head
       ══════════════════════════════════════════ */

    .about-loc {
      display: inline-flex; align-items: center; gap: 9px;
      margin-top: 24px; font-size: 14px; font-weight: 500;
      letter-spacing: 0.01em; color: var(--faint);
    }
    .about-loc svg { color: var(--accent); flex: none; }

    /* ══════════════════════════════════════════
       Contact
       ══════════════════════════════════════════ */
    .contact-box {
      background: var(--surface);
      border: 1px solid var(--hair);
      border-top: 3px solid var(--accent);
      border-radius: 16px;
      padding: clamp(32px, 5vw, 60px);
      text-align: center;
    }
    .contact-box .eyebrow { display: block; margin-bottom: 16px; }
    .contact-title {
      font-family: var(--f-display); font-weight: 800;
      font-size: clamp(32px, 5vw, 52px); line-height: 1.08;
      letter-spacing: -0.022em; margin-bottom: 18px;
    }
    .contact-desc {
      font-size: 17px; line-height: 1.65; color: var(--mid);
      max-width: 48ch; margin: 0 auto 32px;
    }
    .contact-note { font-size: 13.5px; color: var(--faint); margin-top: 20px; }

    /* ══════════════════════════════════════════
       Footer
       ══════════════════════════════════════════ */
    footer { border-top: 1px solid var(--hair); background: var(--surface); }
    .footer-in {
      max-width: var(--shell); margin: 0 auto; padding: clamp(40px, 5vw, 60px) var(--gut) 0;
      display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr);
      gap: clamp(28px, 4vw, 56px);
    }
    .footer-logo {
      font-family: var(--f-display); font-size: 26px; font-weight: 800;
      letter-spacing: -0.02em;
      display: inline-flex; align-items: center; gap: 11px; margin-bottom: 10px;
    }
    .footer-logo .nav-mark { width: 34px; height: 44px; }
    .footer-logo em { font-style: normal; color: var(--accent); }
    .footer-tagline { font-size: 14.5px; color: var(--mid); line-height: 1.6; max-width: 34ch; }
    .footer-col-lbl {
      font-size: 16px; font-weight: 700; letter-spacing: 0.08em;
      text-transform: uppercase; color: var(--faint); margin-bottom: 16px;
    }
    .footer-col a {
      display: block; font-size: 14.5px; color: var(--mid); padding: 5px 0;
      transition: color 0.18s ease;
    }
    .footer-col a:hover { color: var(--accent); }
    .footer-social { display: flex; gap: 8px; }
    .footer-social a {
      width: 40px; height: 40px; border-radius: 9px;
      display: inline-flex; align-items: center; justify-content: center;
      border: 1px solid var(--hair); color: var(--mid); padding: 0;
      transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
    }
    .footer-social a:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }

    /* the rule over the bottom row is a short centred mark rather than a
       full-width border: it separates the two halves of the footer without
       drawing a line across the whole page */
    .footer-bottom {
      position: relative;
      max-width: var(--shell); margin: 0 auto;
      padding: 24px var(--gut) 30px; margin-top: clamp(32px, 4vw, 48px);
      display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
      font-size: 13px; color: var(--faint);
    }
    .footer-bottom::before {
      content: ""; position: absolute; top: 0; left: 50%;
      transform: translateX(-50%);
      width: min(320px, 60%); height: 1px;
      background: var(--hair);
    }

    #back-top {
      position: fixed; right: 20px; bottom: 20px; z-index: 90;
      width: 42px; height: 42px; border-radius: 10px;
      background: var(--surface); border: 1px solid var(--hair);
      color: var(--mid); cursor: pointer;
      display: inline-flex; align-items: center; justify-content: center;
      opacity: 0; pointer-events: none; visibility: hidden;
      box-shadow: var(--shadow);
      transition: opacity 0.22s ease, color 0.18s ease, border-color 0.18s ease,
                  visibility 0s linear 0.22s;
    }
    #back-top.visible { opacity: 1; pointer-events: auto; visibility: visible; }
    #back-top:hover { color: var(--accent); border-color: var(--accent-line); }

    /* ══════════════════════════════════════════
       Responsive
       ══════════════════════════════════════════ */
    /* five across needs the full width; below it the row folds to three,
       then two, so a step card never gets narrower than its copy */
    @media (max-width: 1180px) {
      .steps { grid-template-columns: repeat(3, 1fr); }
    }

    @media (max-width: 1000px) {
      .svc-grid { grid-template-columns: repeat(2, 1fr); }
      .steps { grid-template-columns: repeat(2, 1fr); }
    }

    /* six links plus the longer French and Russian labels need the room back
       before the menu collapses into the drawer */
    @media (max-width: 1080px) {
      .nav-links { gap: 0; }
      .nav-links a { padding: 8px 9px; font-size: 13.5px; }
      .nav-in { gap: 12px; }
    }

    @media (max-width: 820px) {
      .nav-links { display: none; }
      .nav-tools .lang { display: none; }
      .burger { display: inline-flex; }
      /* with the links hidden nothing else pushes the tools over */
      .nav-tools { margin-left: auto; }
    }

    @media (max-width: 640px) {
      .svc-grid { grid-template-columns: 1fr; }
      .steps { grid-template-columns: 1fr; }
      .footer-in { grid-template-columns: 1fr; }
      .hero-cta .btn { width: 100%; justify-content: center; }
      .footer-bottom { flex-direction: column; gap: 8px; }
      /* stacked and left-aligned, a short centred rule no longer lines up
         with anything: it runs the width of the copy it sits over */
      .footer-bottom::before {
        left: var(--gut); transform: none;
        width: calc(100% - var(--gut) * 2);
      }
    }

    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-delay: 0s !important;
        transition-duration: 0.01ms !important;
      }
    }

    /* ══════════════════════════════════════════
       Nav CTA
       ══════════════════════════════════════════ */
    /* the button stands on an accent-coloured edge; hovering lifts it, and a
       click travels the full height of that edge */
    .nav-cta {
      display: inline-flex; align-items: center; gap: 7px;
      height: 38px; padding: 0 17px; border-radius: 100px;
      background: var(--accent); color: var(--accent-ink);
      font-size: 13.5px; font-weight: 600; white-space: nowrap;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.24),
                  0 3px 0 color-mix(in srgb, var(--accent) 60%, #000),
                  0 8px 16px -8px rgba(54,24,124,0.55);
      transition: background 0.18s ease, transform 0.14s ease, box-shadow 0.14s ease;
    }
    .nav-cta svg { transition: transform 0.2s cubic-bezier(0.2, 0.75, 0.25, 1); }
    .nav-cta:hover {
      background: color-mix(in srgb, var(--accent) 86%, var(--ink));
      transform: translateY(-2px);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.28),
                  0 5px 0 color-mix(in srgb, var(--accent) 56%, #000),
                  0 13px 24px -10px rgba(54,24,124,0.6);
    }
    .nav-cta:hover svg { transform: translateX(3px); }
    .nav-cta:active {
      transform: translateY(3px);
      box-shadow: inset 0 2px 5px rgba(15,14,19,0.35);
    }
    /* tighten the button before the links do, and drop it only when the
       drawer takes over at 820px */
    @media (max-width: 1080px) {
      .nav-cta { height: 34px; padding: 0 12px; font-size: 13px; }
      .nav-cta svg { display: none; }
    }
    @media (max-width: 820px) { .nav-cta { display: none; } }

    /* the depth stays when motion is turned down, the movement does not:
       nothing jumps under the pointer. This sits below every nav rule so it
       wins on source order. */
    @media (prefers-reduced-motion: reduce) {
      .nav-links a:hover, .nav-links a:active,
      .nav-cta:hover, .nav-cta:active,
      .icon-btn:hover, .icon-btn:active,
      .lang-btn:hover, .lang-btn:active,
      .drawer-cta:active, .drawer-links a:active { transform: none; }
      .lang-menu { transform: none; }
      .drawer { transform: none; transition: opacity 0.22s ease, visibility 0s linear 0.22s; }
      .drawer.open { transition: opacity 0.22s ease, visibility 0s; }
    }

    /* ══════════════════════════════════════════
       What we build — numbered cards
       ══════════════════════════════════════════ */
    .build-grid {
      display: grid; grid-template-columns: repeat(3, 1fr);
      gap: clamp(8px, 0.9vw, 12px);
      max-width: var(--col);
      margin: 0 auto;
    }
    .build {
      background: var(--surface); padding: clamp(14px, 1.8vw, 20px) clamp(12px, 1.3vw, 16px);
      display: flex; flex-direction: column;
      text-align: center;
      border: 1px solid var(--hair); border-radius: 14px; overflow: hidden;
      box-shadow: 0 1px 2px rgba(15,14,19,0.04);
      position: relative;
    }
    .build-n {
      font-family: var(--f-display); font-size: 14px; font-weight: 600;
      color: var(--accent); letter-spacing: 0.05em;
      font-variant-numeric: tabular-nums;
      padding-bottom: 10px; margin-bottom: 14px;
      border-bottom: 1px solid var(--hair);
    }
    .build h3 {
      font-family: var(--f-display); font-weight: 700;
      font-size: clamp(19px, 2.1vw, 22px); letter-spacing: -0.018em;
      line-height: 1.15; margin-bottom: 8px;
    }
    .build p {
      font-size: 14.5px; line-height: 1.55; color: var(--mid); margin-bottom: 16px;
    }
    .build-link {
      display: inline-flex; align-items: center; gap: 7px; margin-top: auto;
      font-size: 14px; font-weight: 600; color: var(--accent);
      border-bottom: 1px solid transparent; padding-bottom: 1px;
      align-self: center; transition: border-color 0.18s ease;
    }
    .build-link:hover { border-bottom-color: currentColor; }

    /* ── the three panels are coloured, and that is all they do ──
       The outer two are the site purple laid on paper at a low alpha; the
       middle one is the ink at the same strength, so the row still alternates
       purple / black the way it always did — only pale and muted now, a tint
       on paper rather than three solid blocks.
       Nothing here moves or lights up on hover: the colour is the statement. */
    .build-grid .build:nth-child(2n + 1) {
      background: rgba(54,24,124,0.045);
      border-color: rgba(54,24,124,0.11);
    }
    .build-grid .build:nth-child(2n + 1) .build-n {
      border-bottom-color: rgba(54,24,124,0.14);
    }

    .build-grid .build:nth-child(2) {
      background: rgba(15,14,19,0.055);
      border-color: rgba(15,14,19,0.13);
    }
    .build-grid .build:nth-child(2) .build-n {
      border-bottom-color: rgba(15,14,19,0.16);
    }

    /* the type on all three is the ordinary paper set — muted means the
       ground is tinted, not that the words have to shout over it */
    .build-grid .build h3 { color: var(--ink); }
    .build-grid .build p { color: var(--mid); }
    .build-grid .build .build-n,
    .build-grid .build .build-link { color: var(--accent); }

    /* the panels opt out of the shared card motion entirely */
    .build .build-link::after { display: none; }
    .build .build-link:hover { border-bottom-color: currentColor; }
    .build .build-link svg { transition: none; }
    .build .build-link:hover svg { transform: none; }

    /* ── the panels stand up ──
       Each one turns toward the pointer as it grows. Hover only: nothing
       here answers a click, and there is nothing to click.

       The turn and the growth are written as the `rotate` and `scale`
       properties rather than as `transform`, for a reason worth keeping:
       the pointer turn parks a `transform: none` on every card, at a
       specificity nothing here can reach. Own properties keep
       the two out of each other's way — and they compose, so the press does
       not have to restate the turn to avoid straightening the panel out.

       The depth comes from perspective on the grid, so the row shares one
       camera and the outer panels turn slightly off-axis, the way three real
       cards on a table would. site.js keeps --ax / --ay / --ang current. */
    .build-grid, .svc-grid { perspective: 1500px; }
    .build, .svc {
      rotate: var(--ax, 1) var(--ay, 0) 0 var(--ang, 0deg);
    }

    @media (hover: hover) and (prefers-reduced-motion: no-preference) {
      .build, .svc {
        transition: rotate 0.26s cubic-bezier(0.2, 0.75, 0.25, 1),
                    scale 0.26s cubic-bezier(0.2, 0.75, 0.25, 1);
      }
      .build:hover, .svc:hover { scale: 1.015; z-index: 2; }
    }

    /* ══════════════════════════════════════════
       Final CTA
       ══════════════════════════════════════════ */
    /* The band runs the full width of the page — no card, no gutter — and
       the copy inside is what is held to a measure. This is the flat ground;
       the gradient that lifts it towards the footer is added further down. */
    #contact { background-color: var(--cta-ground); }
    .final-cta {
      text-align: center;
      max-width: 820px; margin-inline: auto;
    }
    .final-title {
      font-family: var(--f-display); font-weight: 800;
      font-size: clamp(26px, 3.4vw, 38px); line-height: 1.1;
      letter-spacing: -0.024em; margin-bottom: 8px; text-wrap: balance;
    }
    .final-sub {
      font-family: var(--f-display); font-style: italic; font-weight: 500;
      font-size: clamp(18px, 2vw, 21px); line-height: 1.3;
      color: var(--accent); margin-bottom: 12px; text-wrap: balance;
    }
    .final-body {
      font-size: 15.5px; line-height: 1.55; color: var(--mid);
      max-width: 66ch; margin: 0 auto 22px; text-wrap: balance;
    }
    .final-actions {
      display: flex; gap: 12px; flex-wrap: wrap; justify-content: center;
    }
    .final-note { font-size: 13px; color: var(--faint); margin-top: 16px; }
    /* a hairline outline is legible on paper and disappears on the wash, so
       the second button gets a plate under it — the same fix the hero makes */
    #contact .btn-ghost {
      background: rgba(255,255,255,0.78);
      border-color: var(--accent-line);
    }
    #contact .btn-ghost:hover {
      background: var(--surface); border-color: var(--accent2-line);
    }

    /* ══════════════════════════════════════════
       Footer additions
       ══════════════════════════════════════════ */
    .footer-in { grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr)); }
    .footer-tag {
      font-family: var(--f-display); font-style: italic; font-weight: 500;
      font-size: 13.5px; color: var(--faint); letter-spacing: -0.005em;
    }
    .footer-social { margin-top: 18px; }

    /* ── the footer runs purple ──
       The page ends on the accent rather than on paper: a deep violet ground
       with the type inverted onto it. The tokens are re-pointed for this one
       block, so every rule written above keeps working — only the values it
       reads change. The ground is its own value rather than --accent-deep:
       the accent has to stay bright enough to carry buttons and links on
       paper, while down here it only has to be dark enough to sit still. */
    footer {
      --f-ground: #36187C;
      --f-ink:   #FFFFFF;
      --f-mid:   rgba(237,233,254,0.78);
      --f-faint: rgba(221,214,254,0.70);
      --f-hair:  rgba(255,255,255,0.22);
      --f-lit:   #FFFFFF;

      position: relative; isolation: isolate;
      background: var(--f-ground);
      border-top: none;
      color: var(--f-mid);
    }
    /* the same drift that sits behind the hero and the closing card, held
       far down so the ground is not a flat slab */
    footer::before {
      content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
      background:
        radial-gradient(46% 62% at 12% 0%, rgba(54,24,124,0.10), transparent 68%),
        radial-gradient(40% 56% at 88% 100%, rgba(54,24,124,0.14), transparent 70%);
    }

    footer .footer-logo { color: var(--f-ink); }
    footer .footer-logo em { color: var(--f-lit); }
    footer .footer-tagline { color: var(--f-mid); }
    /* the column headings sit above their links rather than below them: full
       white against the lavender the links carry, which reads as a heading
       at a glance and clears 15:1 on this ground — where anything dark, the
       label hue included, disappears into it */
    footer .footer-col-lbl { color: var(--f-ink); }
    footer .footer-col a { color: var(--f-mid); }
    footer .footer-col a:hover { color: var(--f-ink); }

    footer .footer-social a { border-color: var(--f-hair); color: var(--f-mid); }
    footer .footer-social a:hover {
      color: var(--f-ground); border-color: var(--f-lit); background: var(--f-lit);
    }

    footer .footer-bottom { color: var(--f-faint); }
    footer .footer-bottom::before { background: var(--f-hair); }
    footer .footer-tag { color: var(--f-faint); }

    @media (max-width: 1000px) {
      .build-grid { grid-template-columns: 1fr; }
      .footer-in { grid-template-columns: 1fr 1fr; }
    }
    @media (max-width: 640px) {
      .footer-in { grid-template-columns: 1fr; }
      .final-actions .btn { width: 100%; justify-content: center; }
    }

/* ══════════════════════════════════════════
   Language switcher — real links now
   ══════════════════════════════════════════ */
.lang-menu a {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 9px 11px; border-radius: 7px;
  color: var(--mid); font-size: 14px;
  transition: color 0.15s ease, background 0.15s ease;
}
.lang-menu a:hover { background: var(--sunken); color: var(--ink); }
.lang-menu a[aria-current="true"] { color: var(--accent); font-weight: 600; }
.lang-menu a[aria-current="true"]::after { content: "✓"; font-size: 12px; }

.drawer-langs a {
  padding: 9px 15px; border-radius: 100px;
  border: 1px solid var(--hair); color: var(--mid);
  font-size: 13.5px; font-weight: 500;
}
.drawer-langs a[aria-current="true"] {
  border-color: var(--accent-line); background: var(--accent-soft);
  color: var(--accent); font-weight: 600;
}

/* ══════════════════════════════════════════
   Contact page
   ══════════════════════════════════════════ */
.page-head { max-width: 780px; margin-bottom: clamp(38px, 5vw, 58px); }
.page-head .page-title {
  font-family: var(--f-display); font-weight: 800;
  font-size: clamp(30px, 4.4vw, 50px); line-height: 1.12;
  letter-spacing: -0.026em; text-wrap: balance; margin-bottom: 18px;
}
.page-head .page-sub {
  font-size: clamp(17px, 2vw, 19.5px); line-height: 1.62;
  color: var(--mid); max-width: 54ch;
}

.contact-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(34px, 5vw, 76px);
  align-items: start;
}

.form-card {
  background: var(--surface); border: 1px solid var(--hair);
  border-radius: 16px; padding: clamp(24px, 3.4vw, 40px);
}

form { display: flex; flex-direction: column; gap: 22px; }

.field-label {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 14px; font-weight: 600; color: var(--ink);
  margin-bottom: 9px; letter-spacing: -0.005em;
}
.req {
  font-size: 10px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--accent);
}

input[type="text"], input[type="email"], input[type="tel"], select, textarea {
  width: 100%; font-family: var(--f-body); font-size: 15.5px;
  line-height: 1.55; color: var(--ink); background: var(--paper);
  border: 1px solid var(--hair); border-radius: 10px;
  padding: 13px 15px;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
select {
  appearance: none; cursor: pointer; padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%237C7688' stroke-width='1.8'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 15px center;
}
textarea { min-height: 170px; resize: vertical; padding-right: 58px; }
::placeholder { color: var(--faint); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* iOS zooms the page toward any control it focuses that sets type under 16px,
   and does not zoom back out. A touch pointer is the tell, not the width —
   a tablet in landscape does it too. */
@media (pointer: coarse) {
  input[type="text"], input[type="email"], input[type="tel"], select, textarea {
    font-size: 16px;
  }
}

.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

/* The honeypot. Off the canvas rather than display:none, which the
   better bots read as "skip this one" — the point is that they fill it. */
.hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden;
}
/* Turnstile draws itself into this and the form gap spaces it. Blocked
   or not yet rendered it is empty, and an empty box should not leave a
   22px hole above the button. */
.cf-turnstile:empty { display: none; }
.textarea-wrap { position: relative; }

.mic-btn {
  position: absolute; top: 11px; right: 11px;
  width: 36px; height: 36px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--hair); background: var(--surface);
  color: var(--faint); cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.mic-btn:hover { color: var(--accent); border-color: var(--accent-line); }
.mic-btn.listening {
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
}
.mic-note { display: none; margin-top: 10px; font-size: 13.5px; color: var(--accent); }
.mic-lang-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 13px; }
.mic-lang-label { font-size: 13px; color: var(--faint); }
.mic-lang-btn {
  min-width: 44px; padding: 7px 12px; border-radius: 8px;
  border: 1px solid var(--hair); background: transparent; color: var(--mid);
  cursor: pointer; font-family: var(--f-body); font-size: 12.5px;
  font-weight: 600; letter-spacing: 0.05em;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.mic-lang-btn:hover { color: var(--ink); border-color: var(--faint); }
.mic-lang-btn.active {
  color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft);
}

.submit-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  padding: 16px 30px; border: none; border-radius: 11px;
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--f-body); font-size: 15.5px; font-weight: 600; cursor: pointer;
  transition: background 0.18s ease, opacity 0.18s ease;
}
.submit-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 86%, var(--ink)); }
.submit-btn:disabled { opacity: 0.6; cursor: default; }

.privacy-note {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--faint); margin-top: 14px;
}
.error-msg {
  display: none; padding: 14px 16px; border-radius: 10px;
  border: 1px solid #B42318; background: rgba(180,35,24,0.06);
  color: #B42318; font-size: 14.5px; line-height: 1.55;
}
.error-msg a { color: #B42318; text-decoration: underline; }

/* aside */
.contact-aside { display: flex; flex-direction: column; gap: 30px; }
.aside-card {
  background: var(--surface); border: 1px solid var(--hair);
  border-radius: 14px; padding: 24px;
}
.aside-title {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 18px;
}
.aside-item + .aside-item { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--hair-soft); }
.aside-lbl { font-size: 13.5px; font-weight: 600; color: var(--ink); margin-bottom: 3px; }
.aside-note { font-size: 12.5px; color: var(--faint); margin-bottom: 7px; }
.aside-link { font-size: 14.5px; font-weight: 600; color: var(--accent); word-break: break-word; }
.aside-link:hover { text-decoration: underline; }

/* what happens next — a band under the form, three steps abreast, so the
   answer to "and then what?" is read rather than skimmed past in a sidebar */
.next { margin-top: clamp(56px, 7vw, 92px); }
.next-head { margin-bottom: clamp(24px, 3.2vw, 36px); }
.next-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2vw, 24px);
}
.next-card {
  position: relative;
  background: var(--surface); border: 1px solid var(--hair);
  border-radius: 16px; padding: clamp(22px, 2.6vw, 30px);
}
.next-n {
  width: 34px; height: 34px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-ink);
  font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
  margin-bottom: clamp(18px, 2.2vw, 26px);
}
.next-t {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(19px, 2.1vw, 23px); letter-spacing: -0.016em;
  line-height: 1.2; color: var(--ink); margin-bottom: 10px; text-wrap: balance;
}
.next-d { font-size: 14.5px; line-height: 1.6; color: var(--mid); }

.success-box { display: none; }
.success-icon {
  width: 52px; height: 52px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(27,127,75,0.08); color: #1B7F4B;
  border: 1px solid #1B7F4B; margin-bottom: 24px;
}

@media (max-width: 900px) {
  .contact-layout { grid-template-columns: 1fr; }
  .next-grid { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
  .row-2 { grid-template-columns: 1fr; }
  .submit-btn { width: 100%; }
}

/* ══════════════════════════════════════════
   Product marks — embedded so the pages work
   when opened straight from disk
   ══════════════════════════════════════════ */
.pic {
  display: block; flex-shrink: 0;
  background-size: cover; background-position: center;
  background-repeat: no-repeat;
}
.pic-gobag { background-image: var(--ic-gobag); }
.pic-fw    { background-image: var(--ic-fw); }

/* ══════════════════════════════════════════
   Services page
   ══════════════════════════════════════════ */

/* the lead reuses the sub-page head; the accent line under the title is the
   one the closing call to action already uses, so the two rhyme */
.sp-sub {
  font-family: var(--f-display); font-style: italic; font-weight: 500;
  font-size: clamp(19px, 2.4vw, 26px); line-height: 1.3;
  color: var(--accent); margin-bottom: 18px; text-wrap: balance;
}
.sp-actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 32px; }

/* six service lines on the same tight card grid as the capability and build
   rows, so all three answer the pointer the same way */
.line-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(10px, 1.1vw, 14px);
}
.line {
  background: var(--surface); padding: clamp(18px, 2.2vw, 26px) clamp(14px, 1.6vw, 18px);
  display: flex; flex-direction: column;
  border: 1px solid var(--hair); border-radius: 14px; overflow: hidden;
  box-shadow: 0 1px 2px rgba(15,14,19,0.04);
}
.line-cat {
  font-size: 11px; font-weight: 600; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--accent);
  padding-bottom: 9px; margin-bottom: 12px;
  border-bottom: 1px solid var(--hair);
}
.line h3 {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(18px, 2vw, 21px); letter-spacing: -0.016em;
  line-height: 1.18; margin-bottom: 7px;
}
.line-desc {
  font-size: 14.5px; line-height: 1.55; color: var(--mid); margin-bottom: 14px;
}
.line-list {
  list-style: none; display: flex; flex-direction: column; gap: 7px;
  margin-bottom: 16px;
}
.line-list li {
  position: relative; padding-left: 23px;
  font-size: 14px; line-height: 1.5; color: var(--ink);
}
/* a drawn tick rather than a bullet: the list reads as what is included */
.line-list li::before {
  content: ""; position: absolute; left: 1px; top: 5px;
  width: 11px; height: 6px;
  border-left: 1.7px solid var(--accent);
  border-bottom: 1.7px solid var(--accent);
  transform: rotate(-45deg);
}
.line-link {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: auto; align-self: flex-start;
  font-size: 14px; font-weight: 600; color: var(--accent);
  border-bottom: 1px solid transparent; padding-bottom: 1px;
  transition: border-color 0.18s ease;
}
.line-link:hover { border-bottom-color: currentColor; }

/* the blueprint: one vertical run of stages instead of a card wall. The
   number sits in its own column so every rule starts on the same line. */
#blueprint { background: var(--sunken); }
.stages { border-top: 1px solid var(--hair); }
.stage {
  display: grid;
  grid-template-columns: clamp(74px, 9vw, 124px) minmax(0, 1fr);
  gap: clamp(16px, 3vw, 46px);
  padding: clamp(24px, 3.2vw, 38px) 0;
  border-bottom: 1px solid var(--hair);
  align-items: start;
}
.stage .stage-n {
  font-family: var(--f-display); font-weight: 800;
  font-size: clamp(30px, 4.4vw, 52px); line-height: 1;
  letter-spacing: -0.03em; color: var(--accent-line);
  font-variant-numeric: tabular-nums;
  transition: color 0.2s ease;
}
.stage:hover .stage-n { color: var(--accent); }
.stage h3 {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(19px, 2.2vw, 24px); letter-spacing: -0.016em;
  line-height: 1.25; margin-bottom: 9px;
}
.stage p { font-size: 15.5px; line-height: 1.65; color: var(--mid); max-width: 62ch; }
.stages-note { margin-top: 30px; text-align: center; }

@media (max-width: 1000px) {
  .line-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .line-grid { grid-template-columns: 1fr; }
  .sp-actions .btn { width: 100%; justify-content: center; }
  /* the number moves above the stage rather than squeezing a column */
  .stage { grid-template-columns: 1fr; gap: 10px; }
}

/* ══════════════════════════════════════════
   Projects page
   ══════════════════════════════════════════ */

/* ══════════════════════════════════════════
   Category groups
   ══════════════════════════════════════════ */
.groups { display: flex; flex-direction: column; gap: clamp(56px, 7vw, 92px); }

/* the group announces itself across a hairline: the kind on the left, one
   line of plain English on the right, the count sitting on the rule */
.group-head {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(18px, 3vw, 48px); align-items: end;
  padding-bottom: 18px; margin-bottom: clamp(22px, 2.8vw, 30px);
  border-bottom: 1px solid var(--hair);
}
.group-kind {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
}
.group-title {
  font-family: var(--f-display); font-weight: 800;
  font-size: clamp(26px, 3.3vw, 36px); line-height: 1.1; letter-spacing: -0.024em;
}
.group-count {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.group-desc {
  font-size: 15.5px; line-height: 1.65; color: var(--mid); max-width: 48ch;
}

/* ══════════════════════════════════════════
   Website cards
   ══════════════════════════════════════════ */
/* A site is a thing you look at, so its card leads with a frame rather than a
   fact sheet: browser chrome, the real address in the bar, and a drawn
   miniature of the page behind it. Drawn, not screenshotted — it is a
   diagram of the layout, and never pretends to be a photograph of one. */
.site-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 2.6vw, 28px);
}

.site {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--hair); border-radius: 18px; overflow: hidden;
}
.site:hover { border-color: var(--accent-line); }

.site-frame {
  padding: clamp(20px, 2.4vw, 26px) clamp(20px, 2.4vw, 26px) 0;
  background: var(--sunken); border-bottom: 1px solid var(--hair);
}
/* the window is cut off by the bottom of the strip, the way a real one is
   cut off by the bottom of a screen */
.chrome {
  border: 1px solid var(--hair); border-bottom: none;
  border-radius: 11px 11px 0 0; overflow: hidden;
  background: var(--surface);
  box-shadow: 0 -1px 0 rgba(255,255,255,0.9) inset,
              0 12px 26px -18px rgba(15,14,19,0.45);
  transition: transform 0.35s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.site:hover .chrome { transform: translateY(-4px); }

.chrome-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  background: color-mix(in srgb, var(--sunken) 55%, var(--surface));
  border-bottom: 1px solid var(--hair-soft);
}
.chrome-dots { display: flex; gap: 5px; flex-shrink: 0; }
.chrome-dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--hair); }
.chrome-url {
  flex: 1; min-width: 0;
  font-size: 11px; line-height: 1.4; color: var(--faint);
  background: var(--paper); border: 1px solid var(--hair-soft);
  border-radius: 100px; padding: 4px 11px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chrome-url b { font-weight: 600; color: var(--mid); }

/* the miniature: a header with the mark and its nav, then the one screen
   every product site opens on — headline, two lines, the button. It is the
   shape of the real page, not a photograph of it. */
.mini {
  min-height: 156px;
  padding: 13px 16px 20px;
  display: flex; flex-direction: column;
  background:
    radial-gradient(120% 90% at 82% 0%, var(--accent-soft), transparent 62%),
    var(--surface);
}
.mini-nav {
  display: flex; align-items: center; gap: 10px;
  padding-bottom: 11px; margin-bottom: 15px;
  border-bottom: 1px solid var(--hair-soft);
}
.mini-mark {
  width: 26px; height: 26px; border-radius: 7px; flex-shrink: 0;
  border: 1px solid var(--hair-soft); background-color: var(--sunken);
}
.mini-links { display: flex; gap: 8px; margin-left: auto; }
.mini-links i { width: 22px; height: 5px; border-radius: 3px; background: var(--hair-soft); }
.mini-links i:last-child { width: 30px; background: var(--accent-line); }

.mini-hero { display: flex; flex-direction: column; gap: 10px; }
.mini-h {
  font-family: var(--f-display); font-weight: 700;
  font-size: 15px; letter-spacing: -0.018em; line-height: 1.2;
  color: var(--ink); max-width: 17ch; text-wrap: balance;
}
.mini-lines { display: flex; flex-direction: column; gap: 5px; max-width: 78%; }
.mini-lines span { height: 5px; border-radius: 3px; background: var(--hair-soft); }
.mini-lines span:nth-child(2) { width: 62%; }
.mini-btn {
  margin-top: 2px; width: 66px; height: 16px; border-radius: 100px;
  background: var(--accent); opacity: 0.92;
}

.site-body {
  padding: clamp(22px, 2.6vw, 28px);
  display: flex; flex-direction: column; flex: 1;
}
.site-top {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.site-name {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(20px, 2.2vw, 24px); line-height: 1.15; letter-spacing: -0.02em;
  margin-right: auto;
}
.site-desc {
  font-size: 15px; line-height: 1.65; color: var(--mid);
  margin-bottom: 18px;
}
/* the one fact worth pulling out of a marketing site: what it is there to do */
.site-job {
  font-size: 13.5px; line-height: 1.55; color: var(--ink);
  padding: 12px 14px; border-radius: 10px;
  background: var(--accent-soft); border: 1px solid var(--accent-line);
  margin-bottom: 18px;
}
.site-job b {
  display: block; font-size: 10.5px; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 5px;
}
.site-foot {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: auto; padding-top: 16px; border-top: 1px solid var(--hair-soft);
}
.site-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-right: auto; }

/* ══════════════════════════════════════════
   Product cards — apps and SaaS
   ══════════════════════════════════════════ */
.proj-list { display: flex; flex-direction: column; gap: clamp(20px, 2.6vw, 28px); }

.proj {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--hair); border-radius: 18px; overflow: hidden;
}
.proj:hover { border-color: var(--accent-line); }

/* a header strip numbers the product and states what it is before you read a
   word of it — the card announces itself the way a store listing does */
.proj-bar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 13px clamp(22px, 3vw, 34px);
  background: var(--sunken); border-bottom: 1px solid var(--hair);
}
.proj-n {
  font-family: var(--f-display); font-weight: 800;
  font-size: 13px; letter-spacing: 0.02em; color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.proj-cat {
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--faint); margin-right: auto;
}

.proj-body {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(24px, 3.4vw, 48px);
  padding: clamp(24px, 3.2vw, 38px);
  align-items: start;
}

.proj-id { display: flex; align-items: center; gap: 18px; margin-bottom: 20px; }
/* background-color, not the shorthand: the product marks are painted by
   .pic-* further up, and `background` here would wipe the image out */
.proj-icon {
  width: 72px; height: 72px; border-radius: 17px;
  flex-shrink: 0; object-fit: cover;
  border: 1px solid var(--hair-soft); background-color: var(--sunken);
}
.proj-name {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(25px, 3vw, 32px); line-height: 1.1; letter-spacing: -0.022em;
}
.proj-tagline {
  font-family: var(--f-display); font-style: italic; font-weight: 500;
  font-size: 15.5px; line-height: 1.35; color: var(--accent); margin-top: 5px;
}
.proj-desc {
  font-size: 15.5px; line-height: 1.68; color: var(--mid);
  max-width: 56ch; margin-bottom: 22px;
}

/* the same drawn tick the service lines use, so a feature reads as included */
.proj-points { list-style: none; display: flex; flex-direction: column; gap: 11px; }
.proj-points li {
  position: relative; padding-left: 23px;
  font-size: 14.5px; line-height: 1.5; color: var(--ink);
}
.proj-points li::before {
  content: ""; position: absolute; left: 1px; top: 6px;
  width: 11px; height: 6px;
  border-left: 1.7px solid var(--accent);
  border-bottom: 1.7px solid var(--accent);
  transform: rotate(-45deg);
}

/* the facts sit apart from the story, behind the same seam the work card used */
.proj-aside {
  align-self: stretch;
  border-left: 1px solid var(--hair);
  padding-left: clamp(20px, 2.6vw, 30px);
  display: flex; flex-direction: column; gap: 22px;
}
.proj-fact-lbl {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 9px;
}
.proj-fact-val { font-size: 14.5px; line-height: 1.6; color: var(--ink); }
.proj-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.proj-aside .work-links { margin-top: auto; }

/* a product whose site is also ours says so, and crosses to the sites page */
.proj-xref {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 600; color: var(--faint);
  transition: color 0.18s ease;
}
.proj-xref:hover { color: var(--accent); }
.proj-xref svg { opacity: 0.7; }

/* ══════════════════════════════════════════
   Projects · responsive
   ══════════════════════════════════════════ */
@media (max-width: 1000px) {
  .site-grid { grid-template-columns: 1fr; }
  .group-head { grid-template-columns: 1fr; align-items: start; gap: 12px; }
}

@media (max-width: 900px) {
  .proj-body { grid-template-columns: 1fr; gap: 26px; }
  .proj-aside {
    border-left: none; padding-left: 0;
    border-top: 1px solid var(--hair); padding-top: 24px;
  }
  .proj-aside .work-links { flex-direction: row; flex-wrap: wrap; gap: 18px; }
}

@media (max-width: 560px) {
  .proj-id { gap: 14px; }
  .proj-icon { width: 58px; height: 58px; border-radius: 14px; }
}


/* ══════════════════════════════════════════
   Live — motion, pointer response, the second hue
   ══════════════════════════════════════════
   Everything below is reaction: it fires on scroll, on hover, or on a slow
   loop, and it is where --accent2 lives. The static page underneath is
   unchanged, so a reader with reduced motion loses only the movement. */

/* ── scroll progress ──
   The nav's own bottom edge, filled left to right as the page runs. It only
   appears once the page has actually scrolled, so the bar at rest is clean. */
nav::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--sweep);
  /* clipped rather than scaled, so the ramp stays anchored to the full width:
     early in the page the edge is deep violet and it only reaches the light
     end once the reader has actually got to the bottom */
  clip-path: inset(0 calc(100% - var(--scroll, 0) * 100%) 0 0);
  opacity: 0; transition: opacity 0.3s ease;
  pointer-events: none;
}
nav.scrolled::after { opacity: 1; }

/* the closing card's slow travel — the hero used to share it, back when it had
   a painting pinned past its right edge to move */
@keyframes drift {
  from { transform: translate3d(-2%, 1.5%, 0) scale(1); }
  to   { transform: translate3d(4%, -3%, 0) scale(1.09); }
}

/* the proof line alternates the two hues and breathes */
.hero-proof span::before { animation: pip 3.4s ease-in-out infinite; }
.hero-proof span:nth-child(even)::before {
  background: var(--accent2); animation-delay: 1.1s;
}
@keyframes pip {
  0%, 100% { opacity: 0.35; transform: scale(0.75); }
  50%      { opacity: 1;    transform: scale(1); }
}

/* ── section labels ──
   A live pip in the second hue, so every section opens on the same signal.
   .hero-eyebrow is a class of its own and keeps its own dot. */
.eyebrow::before {
  content: ""; display: inline-block;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent2); margin-right: 9px; vertical-align: 1px;
  animation: pip 2.8s ease-in-out infinite;
}

/* ── the service lines ──
   An edge wipes in along the top and a soft pool of the second hue follows
   the pointer across the card. The build wall and the capability wall both
   opted out: they carry flat colour and turn toward the pointer instead. */
.line {
  position: relative; isolation: isolate;
}
.line::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--sweep);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.42s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.line:hover::before { transform: scaleX(1); }

.line::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 0%),
              var(--accent2-soft), transparent 70%);
  opacity: 0; transition: opacity 0.3s ease;
}
.line:hover::after { opacity: 1; }

/* ── the lift ──
   Every card answers the pointer the same way: it comes up off the paper, its
   shadow opens underneath it and it takes the accent edge. Four pixels — far
   enough to read as depth, not so far that the row shoves itself around. The
   raised z-index keeps the open shadow over its neighbours rather than under
   them. This is the move the process cards have always made; it is now what
   every card on the site does. */
.line, .site, .proj, .next-card {
  transition: transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1),
              box-shadow 0.28s ease, border-color 0.2s ease,
              background-color 0.36s ease;
}
.line:hover,
.site:hover, .proj:hover, .next-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lift);
  border-color: var(--accent-line);
  z-index: 2;
}

/* the rest of the service lines step back a little while one is being read */
.js .line-grid .line {
  transition: opacity 0.28s ease,
              transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1),
              box-shadow 0.28s ease, border-color 0.2s ease,
              background-color 0.36s ease;
}
.js .line-grid:hover .line:not(:hover) { opacity: 0.78; }


/* ── inline links ──
   The plain bottom border is replaced by an underline that draws itself in
   the two hues, and the arrow leads the way out */
.build-link, .work-link, .line-link { position: relative; }
.build-link::after, .work-link::after, .line-link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1.5px;
  background: var(--sweep);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.34s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.build-link:hover, .work-link:hover, .line-link:hover { border-bottom-color: transparent; }
.build-link:hover::after, .work-link:hover::after, .line-link:hover::after { transform: scaleX(1); }

.build-link svg, .work-link svg, .line-link svg {
  transition: transform 0.28s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.build-link:hover svg, .work-link:hover svg, .line-link:hover svg { transform: translateX(4px); }

/* ── primary button ──
   Lifts on hover with a warm second shadow under the violet one, and a light
   sweeps across the face once */
.btn-primary {
  position: relative; overflow: hidden;
  transition: background 0.18s ease, box-shadow 0.26s ease,
              transform 0.26s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.btn-primary::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: -45%; width: 34%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.32), transparent);
  transform: skewX(-18deg); opacity: 0; pointer-events: none;
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 26px -12px rgba(54,24,124,0.65),
              0 6px 16px -10px rgba(54,24,124,0.45);
}
.btn-primary:hover::after { opacity: 1; animation: sheen 0.8s ease-out; }
.btn-primary:active { transform: translateY(0); }
@keyframes sheen {
  from { left: -45%; }
  to   { left: 118%; }
}

/* ── closing band ──
   The top edge travels from deep violet to light across the whole page, the
   ground lifts from near-paper to the wash on the way down to the footer, and
   the same slow drift as the hero sits behind the copy */
#contact {
  position: relative; isolation: isolate; overflow: hidden;
  background-image:
    var(--sweep),
    linear-gradient(180deg, var(--cta-top) 0%, var(--cta-ground) 100%);
  background-repeat: no-repeat, no-repeat;
  background-size: 100% 3px, 100% 100%;
  background-position: 0 0, 0 0;
}
#contact::after {
  content: ""; position: absolute; inset: -30% -12%; z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(42% 40% at 20% 12%, rgba(54,24,124,0.07), transparent 70%),
    radial-gradient(40% 38% at 84% 88%, rgba(54,24,124,0.11), transparent 70%);
  animation: drift 28s ease-in-out infinite alternate;
}

/* ── back to top ── */
#back-top { transition: opacity 0.22s ease, color 0.18s ease, border-color 0.18s ease,
                        transform 0.24s cubic-bezier(0.2, 0.75, 0.25, 1); }
#back-top:hover { transform: translateY(-3px); border-color: var(--accent2-line); color: var(--accent2); }

/* ══════════════════════════════════════════
   Legal prose
   ══════════════════════════════════════════
   The privacy notice and the legal notice are the only pages on the site made
   of running text. They get a measure narrow enough to read and nothing else:
   no cards, no rules, no accent — a page you consult, not one you are sold on. */
.prose { max-width: 68ch; }
.prose h2 {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(19px, 2.2vw, 23px); letter-spacing: -0.018em;
  line-height: 1.25; color: var(--ink);
  margin-top: clamp(38px, 4.5vw, 54px); margin-bottom: 14px;
}
.prose h2:first-child { margin-top: 0; }
.prose p { color: var(--mid); font-size: 15.5px; line-height: 1.72; }
.prose p + p { margin-top: 13px; }
.prose ul { margin: 13px 0 0; padding-left: 20px; }
.prose li {
  color: var(--mid); font-size: 15.5px; line-height: 1.72; margin-bottom: 7px;
}
.prose li::marker { color: var(--accent); }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.prose b { color: var(--ink); font-weight: 600; }
/* A fact the studio still has to supply. It is marked rather than quietly
   left blank, because a legal notice with a silent gap in it reads as
   complete and is not. */
.todo {
  display: inline-block; padding: 1px 7px; border-radius: 5px;
  border: 1px dashed #B42318; color: #B42318;
  background: rgba(180,35,24,0.06);
  font-size: 13px; font-weight: 600; font-style: normal;
}
.prose-updated {
  margin-top: clamp(40px, 5vw, 60px); padding-top: 20px;
  border-top: 1px solid var(--hair);
  font-size: 13.5px; color: var(--faint);
}

/* ══════════════════════════════════════════
   404
   ══════════════════════════════════════════
   The page is built around the address that failed rather than around an
   apology. The numeral says which error it is — the nought is the halo, the
   same ring as the mark, with a dashed orbit still going round looking for the
   page — and under it the address the visitor actually asked for is set out
   with the part this site does not know underlined the way a spell-checker
   underlines a word. The suggestion that follows completes that figure: the
   script measures the address against every real page and offers the nearest
   one. Everything the script adds is written hidden and revealed; with no
   script the page is the headline and the four cards, which is the page it
   used to be. */
.nf {
  padding-top: clamp(0px, 1.5vw, 18px);
  padding-bottom: clamp(16px, 2.5vw, 40px);
  position: relative; isolation: isolate;
}
/* a wash of the accent behind the numeral, so the block has a light source
   rather than sitting flat on the paper */
.nf::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  left: -12vw; top: -14%; width: min(760px, 82vw); aspect-ratio: 1;
  background: radial-gradient(circle, var(--accent2-soft), transparent 68%);
}
.nf .eyebrow { display: block; margin-bottom: clamp(14px, 2vw, 20px); }

/* ── the numeral ──
   Two outlined fours with the halo between them. The digits are hollow so the
   ring is the only solid thing in the line and the eye lands there. */
.nf-code {
  display: flex; align-items: center; gap: clamp(2px, 0.8vw, 10px);
  font-family: var(--f-display); font-weight: 800;
  font-size: clamp(76px, 15vw, 168px); line-height: 0.86;
  letter-spacing: -0.05em;
  margin-bottom: clamp(18px, 3vw, 32px);
}
.nf-code span {
  color: var(--accent-soft);
  -webkit-text-stroke: 1.5px var(--accent-line);
}
.nf-halo { width: 0.88em; height: 0.88em; display: block; flex: none; }
.nf-halo-orbit {
  transform-box: view-box; transform-origin: 50% 50%;
  animation: nf-orbit 22s linear infinite;
}
@keyframes nf-orbit { to { transform: rotate(360deg); } }

/* ── the address ──
   Written by the script, so it is hidden until there is something true to put
   in it: opened from a file, or reached at /404 directly, there is no address
   worth reporting and the card never appears. */
.nf-addr {
  display: flex; align-items: flex-start; gap: 12px;
  max-width: 640px; margin-top: clamp(26px, 4vw, 38px);
  padding: 13px 17px;
  background: var(--surface); border: 1px solid var(--hair);
  border-radius: 12px; box-shadow: var(--shadow);
}
.nf-addr > svg { flex: none; margin-top: 2px; color: var(--faint); }
.nf-addr-in { min-width: 0; }
.nf-addr-lbl {
  font-size: 11px; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 5px;
}
.nf-url {
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 14px; line-height: 1.5; color: var(--mid);
  word-break: break-all;
}
/* the segment this site has no page for, marked the way a misspelling is */
.nf-bad {
  color: #B42318; font-weight: 600;
  text-decoration: underline wavy #B42318;
  text-decoration-thickness: 1.5px; text-underline-offset: 3px;
}
.nf-guess {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  margin-top: 16px; font-size: 14.5px; color: var(--mid);
}
.nf-guess a {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 7px 15px 7px 14px; border-radius: 100px;
  background: var(--accent-soft); border: 1px solid var(--accent-line);
  color: var(--accent); font-weight: 600; font-size: 14px;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.nf-guess a:hover { background: var(--surface); border-color: var(--accent); }
.nf-guess a svg { transition: transform 0.24s cubic-bezier(0.2, 0.75, 0.25, 1); }
.nf-guess a:hover svg { transform: translateX(3px); }

/* ── the four ways on ──
   The service cards' row, four across: same edge, same pool under the
   pointer, same lift, so the page a visitor lands on by accident behaves like
   every page they meant to reach. */
.nf-pick {
  display: flex; align-items: center; gap: 18px;
  margin: clamp(44px, 6vw, 72px) 0 clamp(14px, 2vw, 20px);
  font-size: 11px; font-weight: 600; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--faint);
}
.nf-pick::after { content: ""; flex: 1; height: 1px; background: var(--hair); }
.nf-grid { grid-template-columns: repeat(4, 1fr); }
.nf-card { gap: 0; }
.nf-card h2 {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(17px, 1.9vw, 19.5px); letter-spacing: -0.016em;
  line-height: 1.2; margin-bottom: 8px;
}
.nf-card p {
  font-size: 14px; line-height: 1.55; color: var(--mid); margin-bottom: 18px;
}
.nf-card-go {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: auto; font-size: 13.5px; font-weight: 600; color: var(--accent);
}
.nf-card-go svg { transition: transform 0.24s cubic-bezier(0.2, 0.75, 0.25, 1); }
.nf-card:hover .nf-card-go svg { transform: translateX(4px); }

@media (max-width: 1080px) {
  .nf-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .nf-grid { grid-template-columns: 1fr; }
  .nf-code { font-size: clamp(66px, 21vw, 110px); }
}
@media (prefers-reduced-motion: reduce) {
  .nf-halo-orbit { animation: none; }
  .nf-guess a svg, .nf-card-go svg { transition: none; }
}

/* ══════════════════════════════════════════
   Consent
   ══════════════════════════════════════════
   The classic notice: one line of text, two buttons, sitting in the corner.
   The page stays readable behind it and either answer is one click. It is
   rendered hidden and shown by script, so a returning visitor who has already
   answered never sees it flash. */
.consent {
  position: fixed; z-index: 250;
  left: 20px; right: 20px; bottom: 20px;
  margin-left: auto;
  width: fit-content; max-width: min(640px, calc(100% - 40px));
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 12px 14px 12px 18px;
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: 12px;
  box-shadow: var(--shadow-lift);
  animation: consent-in 0.34s cubic-bezier(0.2, 0.75, 0.25, 1);
}
@keyframes consent-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.consent-body { font-size: 13.5px; line-height: 1.5; color: var(--mid); }
.consent-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.consent-actions { display: flex; gap: 8px; margin-left: auto; }
.consent-actions button {
  padding: 8px 16px; border-radius: 100px; white-space: nowrap;
  font-family: var(--f-body); font-size: 13.5px; font-weight: 600;
  cursor: pointer; border: 1px solid transparent;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.consent-accept { background: var(--accent); color: var(--accent-ink); }
.consent-accept:hover { background: color-mix(in srgb, var(--accent) 88%, var(--ink)); }
.consent-decline {
  background: transparent; border-color: var(--hair); color: var(--mid);
}
.consent-decline:hover { border-color: var(--accent-line); color: var(--ink); }
@media (max-width: 560px) {
  .consent {
    left: 12px; right: 12px; bottom: 12px;
    width: auto; max-width: none; gap: 12px;
    padding: 14px 14px 12px;
  }
  .consent-actions { margin-left: 0; width: 100%; }
  .consent-actions button { flex: 1; }
}
@media (prefers-reduced-motion: reduce) { .consent { animation: none; } }

/* the legal row sits under the copyright, quieter than the sitemap above it */
.footer-legal { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.footer-legal a { color: var(--f-faint); }
.footer-legal a:hover { color: var(--f-ink); }
.footer-legal button {
  padding: 0; border: none; background: none; cursor: pointer;
  font-family: var(--f-body); font-size: inherit; color: var(--f-faint);
}
.footer-legal button:hover { color: var(--f-ink); text-decoration: underline; }

/* ── stillness ──
   Nothing above is load-bearing: with reduced motion the loops stop, the
   pointer pool and the wiping edges are dropped, and every element is left
   in the state it would have ended in. */
@media (prefers-reduced-motion: reduce) {
  #contact::after { animation: none; }
  .hero-proof span::before, .eyebrow::before { animation: none; opacity: 1; transform: none; }
  .line::after { display: none; }
  .line::before,
  .build-link::after, .work-link::after, .line-link::after { transition: none; }
  .btn-primary:hover { transform: none; }
  .btn-primary::after { display: none; }
  #back-top:hover { transform: none; }
  /* the cards keep the shadow and the accent edge, they just do not travel —
     including the two that were lifting before any of this was added */
  .build:hover, .svc:hover, .line:hover,
  .site:hover, .proj:hover, .next-card:hover,
  .step:hover, .site:hover .chrome { transform: none; }
  .build, .svc { transition: none; }
  nav::after { transition: none; }
}
