/* Services page — services/index.html. Design node 384:77382 in the current
   file (1600 x 8713), built 2026-09-02, replacing the copy-deck build.

   THE FRAME IS TOO BIG FOR THE MCP — `get_metadata` and `get_design_context`
   both fail on it and on its section children, deterministically (see the
   note under "Figma MCP" in CLAUDE.md). Every number here was MEASURED off the
   1:1 render (get_screenshot at maxDimension 8713, sliced with PIL), which is
   the technique the project page used. The type comes from the file's own
   variables (`get_variable_defs` works): Display/XL 104 / 0.94 / -5,
   Display/L 88 / 1 / -4, Heading/M 32 / 1.1 / -2, Body/M Satoshi 18 / 1.4 at
   weight 575. THE TRACKING NUMBERS ARE PERCENTAGES: the headline's -5 is the
   -5.2px `get_design_context` reports on 104, so -4 on 88 is -3.52 and -2 on
   32 is -0.64. Inks are sampled: names #002300 (`--rdk-dark`), taglines
   and body #001407 (`ink/950`, `--rdk-black-green`) with the body at 90%,
   light copy on the dark ground white at 80%.

   Measured, frame coordinates:
     hero        0..622, title 104 at x 88, two lines, bottom at 544
     intro       622..1461 dark, rounded top; text x 160, photo 808..1510 x 710..1364
     cards       x 88..1508 (1421), radius 96, 24 apart, 80 inset
                 number cap +80, name 88, tagline 32, body 18/1.4
                 pills right-aligned to the inset, 40 tall, 46 pitch
                 strip (cards 2, 3, 5): 600x400, 436x300, 436x300, 20 gap
     cyber       title 88 at 6394, para 22/1.4 at x 748; logo cards 268x155,
                 25 gap, running off the right edge; tiles 689x659, 42 apart
     footer      7700.., the shared one

   Everything is in --u; the phone block at the bottom is straight px. */

/* ============================================================
   1. hero (622)
   ============================================================ */
/* STICKY, like the homepage hero: it pins at the top and the intro's dark
   panel slides up over it, its rounded shoulders revealing the green. Every
   section after this carries `z-index: 1` so the whole page covers it. */
.rdk-svc-hero {
  position: sticky;
  top: 0;
  z-index: 0;
  /* 622 shows; the extra 96 runs on under the intro's shoulders so the
     notches reveal green from the first frame, not the page ground. */
  height: calc(718 * var(--u));
  background: var(--rdk-green);
  overflow: clip;
}

.rdk-svc-hero__shapes {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Each object is THREE elements: a WRAPPER (placed, and moved by the scroll
   parallax), a RIG (rotated / mirrored here, in CSS, and never touched by
   GSAP) and the IMG (floated by the idle loop). Two GSAP tweens get an
   element each, so neither overwrites the other's `transform` — the hero's
   DONK is built the same way. THE RIG IS NOT OPTIONAL: GSAP clears the
   standalone `rotate` / `scale` on any element whose transform it takes over
   (see the footer bubbles note in CLAUDE.md), so the CSS rotation has to sit
   on an element GSAP never animates. */
.rdk-svc-hero__shape {
  position: absolute;
  width: calc(var(--w) * var(--u));
  height: calc(var(--h) * var(--u));
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u));
  will-change: transform;
}

.rdk-svc__rig {
  display: block;
  width: 100%;
  height: 100%;
  rotate: calc(var(--rot, 0deg) + var(--dr, 0) * 1deg);
  scale: var(--sx, 1) var(--sy, 1);
  /* THE FILL ADJUSTMENTS. Figma's image fills carry a saturation / exposure
     adjustment that the exported source does not (see the image rule in
     CLAUDE.md), and the frame's own renders of these nodes are clipped to
     the hero's edges, so they cannot float. Measured on four objects, raw
     against render (pencil 98/154/20 -> 60/158/7, laptop 111/175/32 ->
     65/181/15, play 121/180/42 -> 83/179/21): `saturate(1.8)
     brightness(0.92)` lands within a few points of all of them. On the rig,
     so GSAP never has to carry it. */
  filter: saturate(1.8) brightness(0.92);
}

.rdk-svc-hero__shape img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  will-change: transform;
}

/* FITTED TO THE FRAME, not eyeballed (2026-09-02). Each asset's silhouette
   was rotated through every angle, mirrored or not, and scored against the
   design render's silhouette for the same object; the best fit gives the
   angle, the box and the position (the box is the asset's own canvas, so the
   rotation about its centre is the same in CSS and in the fit). Where two
   fits scored alike the ends decide: the pencil's tip and the wand's stars
   sit where the frame has them. Boxes are square because the canvases are. */
.rdk-svc-hero__shape--pencil { --w: 289; --h: 289; --x: 297;  --y: -9;  --sx: -1; --rot: 9deg; }
.rdk-svc-hero__shape--play   { --w: 183; --h: 183; --x: 704;  --y: -30; --rot: 6deg; }
.rdk-svc-hero__shape--code   { --w: 296; --h: 296; --x: 903;  --y: 119; --rot: -45deg; }
.rdk-svc-hero__shape--wand   { --w: 355; --h: 355; --x: 1281; --y: 116; --rot: -93deg; }
.rdk-svc-hero__shape--laptop { --w: 339; --h: 339; --x: 1201; --y: 355; --rot: 33deg; }

/* Display/XL: 104 / 0.94 / -5.2. Two lines, bottom at the design's 544. */
.rdk-svc-hero__title {
  position: absolute;
  left: calc(88 * var(--u));
  bottom: calc(174 * var(--u));
  z-index: 1;
  max-width: calc(1100 * var(--u));
  margin: 0;
  font-family: var(--rdk-font-brand);
  font-weight: 700;
  font-size: calc(104 * var(--du));
  line-height: 0.94;
  letter-spacing: calc(-5.2 * var(--du));
  color: var(--rdk-white);
}

/* ============================================================
   2. the intro (840, dark, rounded top over the hero)
   ============================================================
   The homepage intro's move: the wrapper is transparent and only the panel is
   painted, so the rounded corners reveal the hero's green. */
.rdk-svc-intro {
  position: relative;
  z-index: 1;
  margin-top: calc(-96 * var(--u));
}

.rdk-svc-intro__panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(88 * var(--u));
  min-height: calc(840 * var(--u));
  /* 120 on the left = the cards' 40 inset + 80 inside, so the intro's copy
     and the cards' copy share an edge, as they do in the frame. */
  padding: calc(88 * var(--u)) calc(90 * var(--u)) calc(97 * var(--u)) calc(120 * var(--u));
  background: var(--rdk-black-green);
  border-radius: calc(96 * var(--u)) calc(96 * var(--u)) 0 0;
  color: var(--rdk-white);
}

.rdk-svc-intro__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: calc(540 * var(--u));
  flex: none;
}

/* Redonk Avant Garde at 20 in green — the design's own label for this page
   (client, 2026-09-02: it is the brand face, not Satoshi), NOT uppercase: it
   is a sentence ("What is it that we do here?"), so it keeps its case. The
   same label heads each card's "What you get". */
.rdk-svc__eyebrow {
  margin: 0 0 calc(40 * var(--u));
  font-family: var(--rdk-font-brand);
  font-weight: 700;
  font-size: calc(20 * var(--u));
  line-height: 1.2;
  letter-spacing: calc(-0.2 * var(--u));
  color: var(--rdk-green);
}

/* 40 / 0.94 — measured line pitch 38 over four lines. */
.rdk-svc-intro__headline {
  margin: 0 0 calc(40 * var(--u));
  font-family: var(--rdk-font-brand);
  font-weight: 700;
  font-size: calc(40 * var(--du));
  line-height: 0.94;
  letter-spacing: calc(-0.91 * var(--du));
}

/* Body/M: Satoshi 18 / 1.4 at the file's weight 575. Paragraphs 24 apart. */
.rdk-svc__body,
.rdk-svc__body p {
  margin: 0;
  font-family: var(--rdk-font-body);
  font-weight: var(--rdk-body-weight);
  font-size: var(--rdk-body-m);
  line-height: var(--rdk-body-lh);
  letter-spacing: var(--rdk-body-track);
  font-feature-settings: "ss03" 1, "ss02" 1;
}

.rdk-svc__body p + p {
  margin-top: calc(24 * var(--u));
}

.rdk-svc-intro__text .rdk-svc__body {
  opacity: 0.8;
}

/* 702 x 654, radius 40, the photo `cover`-cropped to it. */
.rdk-svc-intro__photo {
  flex: none;
  width: calc(702 * var(--u));
  height: calc(654 * var(--u));
  margin: 0;
  border-radius: calc(40 * var(--u));
  overflow: clip;
  background: #0a2a1a;
}

.rdk-svc-intro__photo img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  will-change: transform;
}

/* ============================================================
   3. the five cards
   ============================================================ */
/* The cards sit 40 from the browser edge, as the case-study cards do on the
   homepage (client, 2026-09-02) — the frame had them at 88. INSIDE the card
   the frame's 80 stands. */
.rdk-svc {
  position: relative;
  z-index: 1;
  padding: 0 calc(40 * var(--u));
  background: var(--rdk-black-green);
}

.rdk-svc__cards {
  display: flex;
  flex-direction: column;
  /* 64, up from the frame's 24 (client, 2026-09-02). */
  gap: calc(64 * var(--u));
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The homepage services panel's card: white, 96 radius. `overflow: clip` is
   what trims the object where it pokes above the card's top edge — the
   design shows the pawn and the wand cut by the card, not sitting on it. */
/* NO STACK (client, 2026-09-02, end of day). The bowery-style pin-and-cover
   was built twice — pinned by the foot, then pinned by the head with the
   covered card rising — and both looked awkward around the short cards, so
   the cards sit in plain flow until there is an answer for that. The rise
   entrance stays. */
/* WHERE AN ANCHOR JUMP LANDS (client, 2026-09-18: coming from the homepage's
   services rows, "I'd like the title to be lower so its under the logo and not
   hidden"). Without this the card's top goes to the viewport's top and the
   fixed nav sits over it — measured, the NUMBER is behind the nav at every
   width (it sits at the card's own padding-top, 79 / 41 / 40 at 1600 / 820 /
   390, against a nav bottom of 99 / 71 / 53) and the title clears by 16 on the
   desktop, which reads as crowded rather than as placed.

   A `max()` because the nav does not scale the way the card does: its offset
   carries px floors (see `--nav-top` in css/nav.css) while the card's padding
   is pure `--u`, so one expression cannot track both. 120u clears the desktop
   nav by 21, and the 88 floor covers the tablet (17 clear) and the phone (35).
   It is deliberately the CARD's top rather than the title's — landing the
   whole card below the nav reads as arriving at it, where offsetting just
   enough to free the title leaves the card clipped. */
.rdk-svc__card {
  scroll-margin-top: max(88px, calc(120 * var(--u)));

  position: relative;
  padding: calc(80 * var(--u));
  background: var(--rdk-white);
  border-radius: calc(96 * var(--u));
  overflow: clip;
  color: var(--rdk-dark);
}

/* Strategy and Optimization end in copy rather than a strip, and read
   tighter at the foot for it — 24 more below (client, 2026-09-02). */
.rdk-svc__card--flat {
  padding-bottom: calc(104 * var(--u));
}

/* Satoshi BLACK 20 in green — the services row number (node 19:12890). */
.rdk-svc__num {
  display: block;
  margin: 0 0 calc(16 * var(--u));
  font-family: var(--rdk-font-body);
  font-weight: 700;
  font-size: calc(20 * var(--u));
  line-height: 1;
  color: var(--rdk-green);
}

/* Display/L: 88 / 1 / -4. */
.rdk-svc__name {
  position: relative;
  z-index: 1;
  max-width: calc(700 * var(--u));
  margin: 0 0 calc(88 * var(--u));
  font-family: var(--rdk-font-brand);
  font-weight: 700;
  font-size: calc(88 * var(--du));
  line-height: 1;
  letter-spacing: calc(-3.52 * var(--du));
  color: var(--rdk-dark);
}

/* The flocked object, top-centre, poking above the card. Wrapper + img, as
   in the hero, for the same two-tween reason. */
/* FOUR DESKTOP NUDGES, ALL AT THE IDENTITY, the mirror of the phone's four
   further down (client, 2026-09-18: "desktop tuner wont work" — it did not,
   because there was nothing on this breakpoint for it to drive). `--dk`
   multiplies the drawn box, `--dx`/`--dy` move it in DESIGN UNITS (not px —
   everything here is `--u`-scaled) and `--dr` adds degrees to the rig's
   designed angle. The designed values stay in `--w/--h/--x/--y/--rot`, which
   js/svc-tuner.js never writes.

   The phone block overrides width/height/left/top outright, so none of these
   leak across the breakpoint. */
.rdk-svc__object {
  position: absolute;
  z-index: 0;
  width: calc(var(--w) * var(--dk, 1) * var(--u));
  height: calc(var(--h) * var(--dk, 1) * var(--u));
  left: calc((var(--x) + var(--dx, 0)) * var(--u));
  top: calc((var(--y) + var(--dy, 0)) * var(--u));
  pointer-events: none;
  will-change: transform;
}

.rdk-svc__object img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  will-change: transform;
}

/* Card-relative boxes from the render's ink extents. The pawn is the cursor
   icon as it comes (its lean is baked in); the wand is turned 180 so the
   stars land at the bottom-left; the laptop is upright (the frame has it on
   its lid; the client wants it the right way up); the megaphone is mirrored
   so its bell opens to the upper left. */
.rdk-svc__object--pawn   { --w: 300; --h: 300; --x: 640; --y: -40; }
.rdk-svc__object--wand   { --w: 470; --h: 470; --x: 600; --y: -120; --rot: 180deg; }
.rdk-svc__object--laptop { --w: 520; --h: 440; --x: 560; --y: -150; --rot: -22deg; }
/* THE PANEL IS FITTED, NOT EYEBALLED (node 941:73609, the client's own
   render of this card). The design CLIPS it at the card's top edge, so a
   plain silhouette IoU cannot find it — scoring the VISIBLE part only,
   over rotation and scale, lands 24deg clockwise at IoU 0.963. The asset
   (944:85810) is the unclipped group, so nothing is cut here: the card's
   own overflow does the clipping, exactly as the design does it. */
.rdk-svc__object--panel  { --w: 519; --h: 436; --x: 604; --y: -167; --rot: 24deg; }

/* NO FILTER ON THIS ONE. The other four are raw exports and the rig's
   saturate(1.8) brightness(0.92) stands in for the fill adjustment Figma
   applies and the export drops. The panel is different: the client
   delivered it colour-corrected and re-cut on 2026-09-18, so the
   adjustment is already IN the file. Measured, the shared filter would
   take it from (90,173,37) to (42,179,0) and the per-object fit I had
   before it to (20,186,0) — either one crushes the red channel and
   fights the client's own edit.

   So: a delivered asset that already carries its adjustment must have the
   rig filter turned OFF, not re-fitted. Check which kind an asset is
   before adding one here. */
.rdk-svc__object--panel .rdk-svc__rig { filter: saturate(1.2); }
/* A TOUCH ONLY (client, 2026-09-18), NOT the shared correction. 1.2 takes
   the mean (90,173,37) -> (79,178,15); past about 1.4 the blue channel
   clips at 0 and the shadows go flat, so that is the ceiling here. */
.rdk-svc__object--mega   { --w: 470; --h: 470; --x: 530; --y: -150; --rot: 20deg; --sx: -1; }

/* THE CLIENT'S OWN DESKTOP PASS (2026-09-18), from js/svc-tuner.js's Copy
   button, and the first one this breakpoint has had — until today every
   control in that panel drove the PHONE nudges, so Desktop moved nothing.
   Offsets from the design above, not replacements for it: 1 / 0 / 0 / 0 is
   the designed `--w/--h/--x/--y/--rot` and each icon's own `--rot`.

   Hand-placed by eye against this arrangement, so there is no rule to
   re-apply if the card padding or the name sizes move — they want redoing
   in `?tune`. Note FOUR OF THE FIVE MOVE DOWN (+8 to +64) and three shrink
   slightly: that is a composition, not a correction, so do not tidy any of
   them back toward the designed values. */
.rdk-svc__object--pawn   { --dk: 1.21; --dx: 0;    --dy: -23; --dr: -37; }
.rdk-svc__object--wand   { --dk: 0.91; --dx: 0;    --dy: 8;   --dr: -9; }
.rdk-svc__object--laptop { --dk: 0.92; --dx: 0;    --dy: 64;  --dr: 0; }
.rdk-svc__object--panel  { --dk: 1;    --dx: -84;  --dy: 43;  --dr: 0; }
.rdk-svc__object--mega   { --dk: 1.03; --dx: 47;   --dy: 49;  --dr: 0; }

/* The two columns: 576 of copy on the left, the "What you get" list on the
   right, its top 21 below the tagline's (measured). */
.rdk-svc__row {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: calc(80 * var(--u));
}

.rdk-svc__copy {
  flex: none;
  width: calc(576 * var(--u));
}

/* Heading/M: 32 / 1.1 / -2. */
.rdk-svc__tagline {
  margin: 0 0 calc(24 * var(--u));
  font-family: var(--rdk-font-brand);
  font-weight: 700;
  font-size: calc(32 * var(--du));
  line-height: 1.1;
  letter-spacing: calc(-0.64 * var(--du));
  color: var(--rdk-black-green);
}

.rdk-svc__copy .rdk-svc__body {
  color: var(--rdk-black-green);
  opacity: 0.9;
}

.rdk-svc__get {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  width: calc(530 * var(--u));
  padding-top: calc(21 * var(--u));
}

.rdk-svc__get .rdk-svc__eyebrow {
  margin-bottom: calc(24 * var(--u));
  text-align: right;
}

/* Right-aligned and wrapping, 40 tall on a 46 pitch, 24 of side padding,
   Satoshi at the body weight, the project tags' ink on the project tags'
   off-white. */
/* The pill itself is css/pill.css. What is left here is the one thing that is
   this page's and not the component's: the list rags to the RIGHT, because the
   column's ragged edge faces the copy. */
.rdk-svc__pills {
  justify-content: flex-end;
}

/* ---------- the strip (cards 2, 3, 5) ----------
   The case studies' strip (css/cases.css): 600x400, 436x300, 436x300, 16
   radius, top-aligned, running off the card's right edge — dragged, with the
   glass "Drag" pill following the pointer. The pill's markup and styles are
   the case studies' own (`.rdk-cases__drag*`); only the track is this
   page's. */
.rdk-svc__strip {
  position: relative;
  /* Out to the card's edges so the artwork can run under the rounded corner
     rather than stopping 80 short of it; the track pads the first tile back
     to the copy's edge. */
  margin: calc(80 * var(--u)) calc(-80 * var(--u)) calc(-80 * var(--u));
  padding-bottom: calc(80 * var(--u));
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  scrollbar-width: none;
}

.rdk-svc__strip::-webkit-scrollbar { display: none; }

.rdk-svc__strip.is-dragging { cursor: grabbing; }

.rdk-svc__thumbs {
  /* Read back by js/strip-focus.js, so there is one copy of each. */
  --tile-min: 300;
  --tile-max: 400;
  --squeeze: 0;
  height: calc(var(--tile-max) * var(--u));
  display: flex;
  align-items: flex-start;
  gap: calc(20 * var(--u));
  width: max-content;
  margin: 0;
  padding: 0 calc(80 * var(--u));
  list-style: none;
}

/* THE FOCAL SLOT (client, 2026-09-18) — the case studies' coverflow, driven
   by js/strip-focus.js. One tile stands full height in the slot at the left
   and the rest ease down as they leave; `--focus` (0..1, written per tile) is
   the only property, and every length stays in `--u`.

   THE MARKUP STATES EACH TILE'S RESTING BOX, so `--w`/`--h` is its aspect and
   `--focus: 0` reproduces exactly what the strip drew before — the lead tile
   at 600 x 400 and the rest at 436 x 300, which is what the imagery was
   delivered to (1200 x 800 and 872 x 582). Nothing needed re-cutting.

   THE TRACK'S HEIGHT IS PINNED TO `--tile-max`, and that is not tidiness: it
   is content-sized, where the case studies' strip sits in a reveal of fixed
   height. Mid-handover the outgoing and incoming tiles are ~350 each, so an
   auto height would dip 400 -> 350 -> 400 on every tile and the whole strip
   would breathe. */
.rdk-svc__thumb {
  --focus: 0;
  --tile-h: calc((var(--tile-min)
                  + (var(--tile-max) - var(--tile-min)) * var(--focus)) * var(--u));
  flex: none;
  width: calc(var(--tile-h) * var(--w) / var(--h)
              * (1 - var(--squeeze) * (1 - var(--focus))));
  height: var(--tile-h);
  border-radius: calc(16 * var(--u));
  overflow: clip;
  background: var(--rdk-off-white);
}

/* With no JS — and under reduced motion, where js/services-page.js never
   touches `--focus` — the first tile is the focal one, which is the state the
   strip has always drawn. */
.rdk-svc__thumb:first-child {
  --focus: 1;
}

.rdk-svc__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  pointer-events: none;
}

/* The pill lives on the CARD (the strip is a scroll container and would
   clip it) — same arrangement as the case study card. */
.rdk-svc__card .rdk-cases__drag {
  z-index: 3;
}

/* ============================================================
   4. cybersecurity
   ============================================================ */
.rdk-svc-sec {
  position: relative;
  z-index: 1;
  /* The logo marquee's clip lives here, not on the marquee — see its rule. */
  overflow-x: clip;
  padding: calc(240 * var(--u)) calc(91 * var(--u)) calc(168 * var(--u)) calc(88 * var(--u));
  background: var(--rdk-black-green);
  color: var(--rdk-white);
}

.rdk-svc-sec__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(88 * var(--u));
}

/* Display/L again, in the case-study off-white. */
.rdk-svc-sec__title {
  flex: none;
  width: calc(560 * var(--u));
  margin: 0;
  font-family: var(--rdk-font-brand);
  font-weight: 700;
  font-size: calc(88 * var(--du));
  line-height: 1;
  letter-spacing: calc(-3.52 * var(--du));
  color: var(--rdk-off-white);
}

/* Re-read 2026-09-02 after the client's change: Body/M 18 / 1.4 (line pitch
   25) on a 640 measure from x 810 to 1447 — 62 short of the row's right
   edge — and centred on the title's height. */
.rdk-svc-sec__text {
  width: calc(640 * var(--u));
  margin: 0 calc(62 * var(--u)) 0 0;
  padding-top: 0;
  font-family: var(--rdk-font-body);
  font-weight: var(--rdk-body-weight);
  font-size: var(--rdk-body-m);
  line-height: var(--rdk-body-lh);
  letter-spacing: var(--rdk-body-track);
  font-feature-settings: "ss03" 1, "ss02" 1;
  opacity: 0.8;
}

/* ---------- the logo marquee ----------
   The homepage client card at 268 x 155 with a 25 gap, one row that runs
   off the right edge — so it moves: js/services-page.js loops the track and
   the list is duplicated once in the markup to make the loop seamless. Out
   to the page edge on the right so it really does run off. */
/* Edge to edge of the browser (client, 2026-09-02): the strip runs out
   under both edges of the window rather than being cut at the content
   edge on the left. The first card starts at the content edge on load —
   the track's own left padding — and everything after that is the loop. */
.rdk-svc-sec__marquee {
  margin: calc(72 * var(--u) - 2px) calc(-91 * var(--u)) -2px calc(-88 * var(--u));
  /* CLIP THE X AXIS ONLY. The track is exactly as tall as a card, so a plain
     `overflow: clip` puts the clip boundary on the card's own bottom border.
     `clip` is also the one overflow value that does NOT force the other axis
     from `visible` to `auto`, so this stays out of scroll-container territory
     — which matters, since a scroll container here would break the sticky
     hero the same way base.css's `hidden` note describes.

     THE PAIR ALONE DID NOT ACTUALLY FIX IT, AND THE 2026-09-09 NOTE THAT
     SAID SO WAS WRONG (client, 2026-09-17: "the bottoms of the logo
     containers are cut off again. This happens frequently"). That pass
     verified `overflow-y` COMPUTES to `visible` — it does, Chrome reports
     exactly that — and called it done. It still clips the bottom edge.
     Proved by putting a 3px red border on the cards: open at the foot at
     1600 AND at 820, and whole the moment the marquee is given
     `overflow: visible`. So it had been broken at every width since the
     "fix", which is why it kept coming back.

     VERIFY A CLIP BY LOOKING AT THE PIXELS, NOT BY READING THE COMPUTED
     VALUE — the same lesson the 0.5px hairline itself carries.

     2px of padding moves the card's border off the clip boundary and the
     matching negative margin gives the space straight back, so the section
     measures exactly as before. The blog cards' hover box uses the same
     pair for the same reason.

     AND 2px WAS NOT ENOUGH ON AN iPHONE (client, 2026-09-23, third report:
     "the bottom and tops of the logo containers are getting cut off on my
     phone"). `overflow-x: clip` evidently clips the other axis too, just
     how tightly varies by engine. So the clip moved UP to `.rdk-svc-sec`,
     whose own top and bottom padding (64px on a phone, more on desktop)
     puts any over-clipping far from the cards, while its sides are the
     window's edges, which is where the strip is meant to be cut.
     `clip-path` on the marquee was tried first and is WRONG: it hides
     pixels but does not stop the 4,500px track counting as page width, so
     the page scrolled sideways (body's `overflow-x: clip` reaches the
     viewport as `hidden`, which an iPhone can still pan). The 2px padding
     pair stays so the section's measurements do not move. */
  padding-block: 2px;
  /* SWIPEABLE (js/services-page.js). Vertical swipes stay the page's;
     horizontal ones come to the script. */
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}

.rdk-svc-sec__marquee.is-dragging { cursor: grabbing; }

.rdk-svc-sec__marquee img {
  -webkit-user-drag: none;
  pointer-events: none;
}

.rdk-svc-sec__track {
  display: flex;
  gap: calc(25 * var(--u));
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  will-change: transform;
}

.rdk-svc-sec__track li {
  flex: none;
  width: calc(268 * var(--u));
  height: calc(155 * var(--u));
  /* The homepage client card's rule and radius. */
  /* A flat 1px, not 1u: at any window but 1600 the design unit made this a
     fractional hairline, and fractional horizontal lines get dropped. */
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: calc(40 * var(--u));
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Each mark at its homepage width scaled to this card: 268 / 338 = 0.793. */
.rdk-svc-sec__track img {
  max-width: none;
  height: auto;
  width: calc(var(--lw) * 0.793 * var(--u));
}

.rdk-svc-sec__track [data-logo="arcticwolf"]     img { --lw: 103.128; }
.rdk-svc-sec__track [data-logo="orca"]           img { --lw: 97.177; }
.rdk-svc-sec__track [data-logo="exabeam"]        img { --lw: 153.71; }
.rdk-svc-sec__track [data-logo="beyondidentity"] img { --lw: 143; }
.rdk-svc-sec__track [data-logo="radiant"]        img { --lw: 139.372; }
/* The four added on 2026-09-09 have NO Figma node — the R2 deck names them
   and nothing draws them — so these widths are not designed values. Each is
   set so the mark's rendered width x height geometric mean matches the
   existing six (~50u), which balances a long thin wordmark against a short
   chunky one far better than equalising either dimension alone would. Sophos
   and Secureworks are then capped at 130u wide so nothing overruns exabeam's
   122 by much. Replace with real numbers if the client ever specs them. */
.rdk-svc-sec__track [data-logo="imperva"]        img { --lw: 119; }
.rdk-svc-sec__track [data-logo="sophos"]         img { --lw: 164; }
.rdk-svc-sec__track [data-logo="secureworks"]    img { --lw: 164; }
.rdk-svc-sec__track [data-logo="paloalto"]       img { --lw: 143; }
/* nuHarbor is whole-card artwork on the homepage; here its mark alone is
   drawn from the same file at the width the design shows. */
.rdk-svc-sec__track [data-logo="nuharbor"]       img { --lw: 338; width: 100%; height: 100%; object-fit: cover; }

/* ---------- the two tiles ----------
   The work index's GRID tile: brand colour, 24 radius, artwork inset 16 with
   its own 16 radius, name at 40, description at 16. 689 x 659, 42 apart. */
/* The grid, the gap and the card itself are css/card.css's — this page only
   owns where the list sits. Its gap was 42 against the project page's 40;
   one component means one gap, and 40 is the one node 705:82711 draws. */
.rdk-svc-sec__work {
  margin: calc(80 * var(--u)) 0 0;
}



/* The work index's "View project" pill (css/work.css, `.rdk-work__cursor`),
   following the pointer over a tile — the same affordance the tiles have on
   the work page. `cursor: none` only once JS has put the pill there. */
.rdk-svc-sec--cursor .rdk-svc-work { cursor: none; }

@media (prefers-reduced-motion: reduce) {
}

/* ============================================================
   MOBILE — straight px, after the other pages' phone frames
   ============================================================ */
@media (max-width: 767px) {

  .rdk-svc-hero { height: 473px; }

  .rdk-svc-hero__title {
    left: 20px;
    right: 20px;
    bottom: 65px;
    max-width: none;
    font-size: 48px;
    line-height: 0.94;
    letter-spacing: -2.4px;
  }

  /* A phone design unit for the objects only — see css/work.css. All five
     sit below the wordmark and the Menu pill, above the headline. */
  /* 393, THE NODE'S OWN FRAME (891:114124), not the 390 the rest of this
     block uses. Scoped to the shapes container, so every number below is the
     frame's literal and nothing else on the page moves. */
  .rdk-svc-hero__shapes { --mu: calc(100vw / 393); }

  .rdk-svc-hero__shape {
    width: calc(var(--mw) * var(--mu));
    height: calc(var(--mh) * var(--mu));
    left: calc(var(--mx) * var(--mu));
    top: calc(var(--my) * var(--mu));
  }

  /* THE PHONE HERO'S OBJECTS ARE NODE 891:114124's (client, 2026-09-17:
     "fix the header, specifically the positioning of the fuzzy service
     icons to match the design").

     IT IS A DIFFERENT COMPOSITION, not a nudge. The laptop moves from the
     bottom-left to the top-RIGHT, the code shape from the top-right into the
     middle-left, the pencil grows by half and bleeds off the left edge, and
     the wand drops to the right of the headline. The old arrangement ran the
     laptop and the wand ~85px INTO the headline, which is what this fixes.

     THE NODE REPORTS EACH OBJECT AS A ROTATED BOUNDING BOX AND WE POSITION
     THE UNROTATED ONE, so none of its left/top values transfer directly:
     each is `bbox centre - inner size / 2`. Taking them at face value would
     have put every object out by half the difference between the two boxes —
     up to 17px on the pencil, whose 163.6 art needs a 198.7 box once it is
     turned 104 degrees.

     ITS FIVE PLACEHOLDER FRAMES ARE NOT THE ARRANGEMENT, which is the trap
     in this node. `get_design_context` returns five empty "Text" divs at very
     nearly our CURRENT positions — they read as confirmation that nothing
     needs to change — and the real art is five `image` nodes parented inside
     the laptop's and the wand's containers at large negative offsets. The
     render is what settles it; the placeholders are leftovers.

     ROTATIONS COME WITH THEM, and two are the reason the old composition
     cannot simply be moved: the pencil is -104.18 with NO mirror, against a
     desktop 9deg that carries `--sx: -1`, so the mirror has to be cancelled
     here or the tip points the wrong way. The wand and the play button each
     move ~18 degrees; the code and the laptop are within 2 of desktop's. */
  .rdk-svc-hero__shape--pencil {
    --mw: 163.636; --mh: 163.636; --mx: -33.69; --my: 56.89;
    --rot: -104.18deg; --sx: 1;
  }
  .rdk-svc-hero__shape--play {
    --mw: 90.542; --mh: 95.528; --mx: 162.02; --my: 11.85;
    --rot: -11.77deg;
  }
  .rdk-svc-hero__shape--code {
    --mw: 128.64; --mh: 96.01; --mx: 108.9; --my: 128.97;
    --rot: -44deg;
  }
  .rdk-svc-hero__shape--wand {
    --mw: 196.52; --mh: 196.52; --mx: 264.7; --my: 156.2;
    --rot: -110.8deg;
  }
  .rdk-svc-hero__shape--laptop {
    --mw: 131.127; --mh: 109.693; --mx: 279.82; --my: 65.34;
    --rot: 35.2deg;
  }

  .rdk-svc-intro__panel {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: 32px;
    min-height: 0;
    padding: 20px 20px 56px;
    border-radius: 32px 32px 0 0;
  }

  .rdk-svc-intro__text { width: 100%; }
  .rdk-svc__eyebrow { margin-bottom: 20px; font-size: 17px; letter-spacing: -0.17px; }
  .rdk-svc-intro__headline { margin-bottom: 20px; font-size: 28px; letter-spacing: -0.84px; }

  .rdk-svc__body p + p { margin-top: 16px; }

  .rdk-svc-intro__photo {
    width: 100%;
    height: auto;
    aspect-ratio: 350 / 300;
    border-radius: 20px;
  }

  /* ---------- cards ---------- */
  .rdk-svc { padding: 0 12px; }
  .rdk-svc-intro { margin-top: -33px; }
  .rdk-svc__cards { gap: 24px; }

  .rdk-svc__card {
    padding: 40px 20px 40px;
    border-radius: 32px;
  }

  .rdk-svc__card--flat { padding-bottom: 52px; }

  .rdk-svc__num { margin-bottom: 8px; font-size: 12px; }

  .rdk-svc__name {
    max-width: none;
    margin-bottom: 24px;
    font-size: 44px;
    letter-spacing: -1.76px;
  }

  /* The objects shrink into the card's top-right corner beside the name,
     clear of the tagline below it, still clipped by the card.

     FOUR PER-ICON NUDGES, ALL AT THE IDENTITY (client, 2026-09-17: controls
     for size, position and rotation on mobile). `--nk` multiplies the drawn
     box, `--nx` / `--ny` move it in px and `--nr` adds degrees to the rig's
     designed angle — so a stored tuning session always reads as an OFFSET
     FROM the design rather than as a replacement for it, and Reset is
     unambiguous. The designed values stay in `--w/--h/--rot` and in the 0.24
     and the -10 / -28 here, none of which js/svc-tuner.js ever writes.

     At 1 / 0 / 0 / 0 this renders byte-identical to what it replaced.

     THE SIZE GROWS LEFT AND DOWN, because this breakpoint pins the box by
     its `right` and `top` — it is the corner the treatment is built on, and
     it is why the panel's tooltip says to re-place after a size change. */
  .rdk-svc__object {
    width: calc(var(--w) * 0.24 * var(--nk, 1) * 1px);
    height: calc(var(--h) * 0.24 * var(--nk, 1) * 1px);
    left: auto;
    right: calc(-10px - var(--nx, 0) * 1px);
    top: calc(-28px + var(--ny, 0) * 1px);
  }

  /* THE CLIENT'S OWN PASS (2026-09-18), from js/svc-tuner.js's Copy button.
     Offsets from the design, not replacements for it — 1 / 0 / 0 / 0 is the
     phone block's own 0.24 / -10 / -28 and each icon's designed `--rot`.

     They are hand-placed by eye against this arrangement, so there is no rule
     to re-apply if the card padding or the name sizes move: they want redoing
     in `?tune`. Every icon is bigger than the derived 0.24 (1.15 to 1.81) and
     three of the four are turned, which is a composition rather than a
     correction — do not "tidy" any of them back toward the desktop angles. */
  .rdk-svc__object--pawn   { --nk: 1.81; --nx: -21; --ny: 2;   --nr: 22;}
  .rdk-svc__object--wand   { --nk: 1.31; --nx: 19;  --ny: 37;  --nr: 70;}
  .rdk-svc__object--laptop { --nk: 1.23; --nx: -47; --ny: 8;   --nr: 0; }
  /* IDENTITY, PENDING A PHONE PASS. The four above are the client's own
     hand-placed set (2026-09-18) and there is no rule to derive a fifth
     from — it wants doing by eye in ?tune, like they were. */
  /* The panel is the one NOT placed by eye — it arrived after the client's
     pass, so it is matched TO that pass by measurement: at the identity it
     was the runt of the set (ink 118x110 against 145-179 wide, and only
     66% of it on the card against their 79-88). 1.28 brings the ink to 151,
     the family's own size, and the two px nudges land it 22 above the card
     and 3 past the right edge, which are their medians. 83% visible.
     Its 9px over the tagline looked tight and is not: the wand and the
     laptop OVERLAP theirs by 29 and 13. Re-place by eye if it wants it. */
  .rdk-svc__object--panel  { --nk: 1.3; --nx: -13; --ny: 16;  --nr: 0; }
  .rdk-svc__object--mega   { --nk: 1.15; --nx: -15; --ny: 10;  --nr: 8; }

  /* SCOPED TO THE CARD OBJECTS, not to `.rdk-svc__rig` itself — that rule is
     shared with the hero's five shapes, which are not what was asked for and
     have their own mobile arrangement. (0,2,0) beats the base rule's (0,1,0). */
  .rdk-svc__object .rdk-svc__rig {
    rotate: calc(var(--rot, 0deg) + var(--nr, 0) * 1deg);
  }

  .rdk-svc__row {
    flex-direction: column;
    align-items: stretch;
    gap: 28px;
  }

  .rdk-svc__copy { width: 100%; }
  .rdk-svc__tagline { margin-bottom: 16px; font-size: 24px; letter-spacing: -1px; }

  .rdk-svc__get {
    align-items: flex-start;
    width: 100%;
    padding-top: 0;
  }

  .rdk-svc__get .rdk-svc__eyebrow { margin-bottom: 12px; text-align: left; }
  /* Left-ragged on a phone; the size and gap are css/pill.css's. */
  .rdk-svc__pills { justify-content: flex-start; }

  /* The strip on a phone is the case studies' touch strip: a swipe, no
     pill; the tiles at half size. */
  .rdk-svc__strip {
    margin: 32px -20px -40px;
    padding-bottom: 40px;
  }
  .rdk-svc__eyebrow { font-size: 15px; }

  /* `height: auto` and both tile dimensions stated is what makes `--focus`
     inert here: the phone keeps its native swipe and six equal tiles, since
     there is no room to pick one out and no pointer to move between them. */
  .rdk-svc__thumbs { height: auto; gap: 10px; padding: 0 20px; }

  .rdk-svc__thumb {
    width: calc(var(--w) * 0.5px);
    height: calc(var(--h) * 0.5px);
    border-radius: 10px;
  }

  /* ---------- cybersecurity ---------- */
  .rdk-svc-sec { padding: 64px 20px 64px; }

  .rdk-svc-sec__row { flex-direction: column; gap: 20px; }
  .rdk-svc-sec__title { width: 100%; font-size: 40px; letter-spacing: -1.6px; }
  .rdk-svc-sec__text { width: 100%; margin: 0; }

  .rdk-svc-sec__marquee { margin: 32px -20px 0; }
  /* 1.5x the phone frame's 140 x 81 (client, 2026-09-23: the logos read too
     small on a phone). Cards, gap, radius and logos all scale together, so
     each mark keeps its designed share of its card. The loop's time per logo
     is unchanged, so the same number of logos pass per second. */
  .rdk-svc-sec__track { gap: 18px; }
  /* WHOLE PIXELS, and that is the fix for the "cut off" tops and bottoms
     (client, 2026-09-23, with a phone screenshot). The corners and the
     vertical sides drew and the straight top and bottom lines did not —
     not a clip, which would take the corners too, but a hairline landing
     on a half pixel (0.75px on a 121.5px card) and being dropped. 1px on a
     122px card puts every edge on a pixel. */
  .rdk-svc-sec__track li { width: 210px; height: 122px; border-width: 1px; border-radius: 30px; }
  .rdk-svc-sec__track img { width: calc(var(--lw) * 0.621px); }
  .rdk-svc-sec__track [data-logo="nuharbor"] img { width: 100%; }

  .rdk-svc-sec__work { margin-top: 40px; }

  /* NO PHONE LOGO WIDTH HERE (client, 2026-09-18). It was a flat 144.824px,
     from this page's own mobile frame back when these tiles were their own
     component — which drew EVERY mark the same width regardless of its
     designed share, the one rule css/card.css otherwise applies everywhere.

     It did not matter while Radiant had no logo. The moment it gained one the
     two cards disagreed: designed shares of 200 and 298, both drawn at 145, so
     Radiant's mark was ~55% larger relative to its design than nuHarbor's.

     Deleted rather than re-stated, so the shared share-based rule reaches the
     phone exactly as it reaches the project pages' cards. */
}

/* ==========================================================================
   TABLET PORTRAIT — node 890:106055
   ==========================================================================
   The node is an iPad PORTRAIT frame at 1:1 (768 x 6031), the same shape the
   About page's tablet node uses, so its numbers are read at face value rather
   than through the 0.937 scale the desktop frames in this file carry.

   `get_design_context` AND `get_metadata` BOTH FAIL ON IT with the SSE parse
   error at ~23 KB, so the type came from `get_variable_defs` (which does work
   on a frame this size) and the geometry was measured off a 1:1 render with
   PIL — the technique this file already records for the services desktop
   frame. The variables: `display/xl: 56`, `body/s: 16`, `label/m: 14`,
   `label/l: 15`.

   AND THE ANSWER IS MOSTLY THE PHONE'S OWN NUMBERS, WHICH IS WHAT KEEPS THIS
   CONSISTENT WITH THE REST OF THE SITE (client, 2026-09-17: "follow the
   design but make sure we're consistent with type sizes with other pages").
   Measured off the render against what `@media (max-width: 767px)` already
   ships:

     eyebrow        cap 12px -> ~17    phone 17    SAME
     intro headline pitch 25.4 / 0.94  -> 27.0     phone 28    SAME to 4%
     body           pitch 24 on 16 ink -> 16 / 1.5             ALREADY RIGHT

   So only the HERO is a new size: 56 against the phone's 48, one step up for
   the wider canvas, and the node states it as a bound variable rather than
   leaving it to be measured.

   WHY THE HERO READS "WAY TOO LARGE" ON THE DEVICE AND NOT IN THE PANE: the
   title is `104 * var(--du)`, and `--du`'s floor is gated on `hover: none`,
   which the browser pane never matches. The pane draws 104 x --u = 53 at 820;
   a real tablet draws 104 x 0.68 = **70.7**. The complaint is about the 70.7.
   Flat px here, as the phone and the About tablet hero both are — a floored
   display size is exactly what this node is overriding. */
@media (min-width: 768px) and (orientation: portrait) {
  /* 56 = display/xl. Tracking is the step's own -5%, as the phone's -2.4 on
     48 is. The measure is the hero's width less its two insets, which is what
     reproduces the designed break after "complicated" — at the desktop's
     `1100 * --u` (564 at 820) the first line does not fit and the headline
     breaks a word early. */
  .rdk-svc-hero__title {
    /* THE BREAK AFTER "complicated" IS PINNED, not left to the measure
       (client, 2026-09-17: "'things' should be on the second line to give
       room for the visual").

       At a flat 56 the line widths are fixed: "We make complicated" is 558
       and "We make complicated things" is 718. The hero's own width less its
       insets is 714 at 820 — so the designed break was surviving by FOUR
       PIXELS, and at 1024 portrait that measure is 895 and "things" jumps up
       to the first line. That is the knife edge this file already has a
       lesson about; it wants solving with slack, not by a hair.

       640 sits 82 clear of the long line and 78 clear of the short one, so
       no tablet width can break it either way. Stated over the font size, as
       the case description's measure is, and `min()`ed against the available
       width so a narrow tablet still cannot overflow. */
    max-width: min(calc(640em / 56), calc(100% - 178 * var(--u)));
    font-size: 56px;
    line-height: 0.94;
    letter-spacing: -2.8px;
  }

  /* 20 * --u is 10.25px at 820 — the collapsing unit, in the one place on
     this page with no floor. The design draws it at the phone's 17. */
  .rdk-svc__eyebrow {
    font-size: 17px;
    letter-spacing: -0.17px;
  }

  .rdk-svc-intro__headline {
    font-size: 28px;
    letter-spacing: -0.84px;
  }

  /* THE PHOTO IS THE THING THAT WAS TAKING THE ROOM (client: "the image needs
     to be smaller so we have more real estate for the copy"). The design's is
     261 x 244 at 768, i.e. **544** design units against the desktop's 702 —
     and the copy column goes the other way, 357px = 744 units against the
     desktop's fixed 540.

     THE COLUMN IS LEFT TO FLEX RATHER THAN RESTATED. Both are `flex: none` on
     the desktop, so two fixed widths plus a fixed gap have to add up inside a
     panel whose padding is also fixed — and at 820 the design's own pair
     overflows that sum by ~8px. Fixing the PHOTO and letting the text take
     the remainder keeps the designed gap and cannot overflow at any width.
     Measured at 820: photo 279, copy 373, against the design's 279 / 381
     scaled from 768. */
  .rdk-svc-intro__photo {
    width: calc(544 * var(--u));
    /* THE HEIGHT HAS TO COME WITH IT (client, 2026-09-17: "keep the office
       space image the same proportion, just make it smaller").

       The desktop rule states BOTH a width and a height — 702 x 654 — so
       overriding the width alone squashed the box to 544 x 654, an aspect of
       0.83 against the design's 1.07. The picture inside is `cover`, so it
       cropped rather than distorting, which is why it read as the wrong
       CROP rather than as a stretched photograph.

       544 x 654/702 = 506.8, i.e. the desktop's own aspect carried over
       exactly. Derived rather than measured on purpose: the render says 508
       and the two agree to 0.2%, but a ratio cannot drift from the desktop
       box the way a second transcribed number can. The design is the same
       proportion at 0.775 of the size. */
    height: calc(506.8 * var(--u));
  }

  .rdk-svc-intro__text {
    flex: 1 1 auto;
    width: auto;
  }
}

/* --------------------------------------------------------------------------
   TABLET PORTRAIT — the service cards, node 890:106188
   --------------------------------------------------------------------------
   The node is one card at 730 x 531, which is 1:1 with the 768 frame (the
   card is the frame less its 40u inset a side). It carries no type variables,
   so every number below was measured off the render and then inverted through
   the faces' real ink ratios — `actualBoundingBoxAscent/Descent` at 100px in
   the browser — rather than through an assumed cap height.

   TWO OF THE THREE COMPLAINTS TURNED OUT TO BE ALREADY CORRECT ON THE DEVICE,
   and worth recording so they are not "fixed" again: the name is
   `88 * --du` = 59.8 against the design's ~61, and the tagline `32 * --du` =
   21.8 against the design's ~21-24. Both only look small in the browser pane,
   which cannot match `--du`'s `hover: none` gate and draws them at 45 and 16.

   What was genuinely wrong is everything NOT on `--du`. */
@media (min-width: 768px) and (orientation: portrait) {
  /* `20 * --u` is 10.25px at 820 — the collapsing unit again. The design
     measures ~21.7, so the desktop's own 20 as flat px is the number. */
  .rdk-svc__num {
    font-size: 20px;
  }

  /* THE PILLS ARE THE PHONE'S GEOMETRY AT THE SITE'S 12 (client: "the
     labels/pills are too small"). They were `40/24/16 * --u` — 20.5 tall with
     8.2px type at 820, which is the collapsed-pill item this project has had
     open for a while.

     THE DESIGN MEASURES 39 TALL WITH 13.4px TYPE, and 12 is what every other
     category and service pill on the site takes at this breakpoint as of
     today. Within a pixel of each other, so the instruction to keep type
     sizes consistent and the design agree; 12 wins because it is the one that
     is also true on four other components. Height and padding take the
     phone's 36/16 for the same reason — the design's 39/26 is a rounder pill,
     which is one line to adopt if it is wanted. */
  .rdk-svc__pills {
    gap: 6px;
  }

  .rdk-svc__pills li {
    height: 36px;
    padding: 0 16px;
    font-size: 12px;
  }

  /* THE COPY TAKES THE ROOM THE PILLS NO LONGER NEED. The design's copy is
     422 of a 654 card inner — 879 design units against the desktop's fixed
     576 — and its pill column is 232 at its widest.

     Only the pill column is stated; the copy flexes into the remainder, the
     same call the intro panel's photo and text make. At 12px the widest pill
     ("Positioning and messaging") needs ~182, so 420 units (215 at 820)
     carries it with slack and hands the rest to the copy. Measured at 820:
     copy 461 against the design's 451 scaled from 768. */
  /* 24, NOT 40 (client, 2026-09-17: "decrease the space between copy and
     what you get areas a bit"). 12.3px at 820, and the 16 it frees goes to
     the pill column rather than the copy — the copy flexes, so without
     widening the column in the same breath the gap cut would have fed the
     wrong side and left the pills exactly as tall.

     The design's own body ink runs to 459 with its pill column starting at
     459, i.e. near enough touching, so a tight gap is what it draws. */
  .rdk-svc__row {
    gap: calc(24 * var(--u));
  }

  .rdk-svc__get {
    /* `flex: none` IS LOAD-BEARING. The desktop rule states a width and no
       flex, i.e. `0 1 auto` — which can SHRINK. Against a copy column set to
       `flex: 1 1 auto` it lost 51px and the pills wrapped to 8 rows against
       the design's 6, while the copy overshot to 498. Stating the width is
       not the same as holding it. */
    flex: none;
    /* 546 (280 at 820), NOT 420 (client, 2026-09-17: the tall pill column
       "is creating an issue with this large gap under the text. Can we make
       the labels one size smaller and increase the width of the container").

       THE WIDTH IS THE WHOLE LEVER AND THE TYPE SIZE IS VERY NEARLY NOTHING,
       which is worth recording because the instinct is the other way round.
       Measured as the pill column's height MINUS the copy's — positive is the
       hole under the copy — on the worst card:

         420u / 12px   +217      420u / 11px   +175
         546u / 12px    +73      546u / 11px    +25
         663u / 12px    -23

       At 420 the pills are one per row whichever size they are, so a 5%
       narrower pill still cannot let a second one in; at 546 most rows take
       two and the rows roughly halve. So the type stays at 12 — the size
       every other category and service pill on the site took today — and only
       the column moves.

       546 RATHER THAN 663 BECAUSE THE TRADE REVERSES. At 663 the column is
       SHORTER than the copy on every card, which just moves the hole to the
       right-hand side, and the copy falls to 322 against the design's 451.
       546 leaves the copy at 382 and a +25..+73 residual — the design's own
       Strategy card overhangs its copy by 62, so that is the shape it draws
       rather than a defect.

       THE REMAINING GAP CANNOT BE TUNED AWAY, only traded: the pills pack
       more per row only by taking width from the copy. The two asks pull
       against each other and this is the middle.

       640 SINCE THE SECOND PASS (client: "it's close but pills are still
       taking up too much vertical real estate"), with the row gap cut to 24
       to fund part of it. Measured at 820, 546/40 -> 640/24:

         rows      6/6/7/6  ->  4/5/6/4
         residual  +49/+73/+67/+25  ->  -83/+7/+1/-83
         card      502/736/823/736  ->  501/694/781/735
         copy      382  ->  342

       So the hole is gone on every card rather than reduced. THE PRICE IS
       THAT THE TWO COLUMNS ARE NOW ALMOST EQUAL — 342 of copy against 328 of
       pills, where the design draws 451 against 248. That is content and not
       taste: the design's Strategy card carries SIX pills and ours carries
       eight to thirteen after the R2 split, so its column can be narrow and
       ours cannot. Worth re-reading if the pill lists are ever shortened. */
    width: calc(640 * var(--u));
  }

  .rdk-svc__copy {
    flex: 1 1 auto;
    width: auto;
  }

  /* THE OBJECT IS 1.4x AND SITS FURTHER RIGHT AND HIGHER (client: "the image
     larger and positioned better").

     MEASURED THROUGH THE ASSET'S ALPHA, NOT ITS BOX. `icon-pawn.webp` fills
     only 53.7% of its own width and 84.6% of its height, so comparing the
     design's ink against our element box would have overstated the change
     four-fold — the first pass of this measurement did exactly that, by also
     catching the green "What you get" label in the same scan. The pawn's ink
     is 108 wide at 768 where ours draws 77: a factor of 1.4, which on a 300
     box gives the design's own 419.

     The shift is the same arithmetic: the design's box lands at x 740 / y -77
     against the desktop's 640 / -40.

     APPLIED TO ALL FOUR OBJECTS AS ONE RULE. The node only shows Strategy, so
     the +100 / -37 is derived from that one card — but every card uses the
     same geometry on the desktop, and a per-card hand placement is a set of
     numbers with no rule behind them.

     CHECKED BY EYE AS WELL AS BY BOX, because three of the four are ROTATED
     (180, -22 and 20 deg) and a rotated object's ink does not respect its
     box — the lesson this file already records against the wand and the
     megaphone. Box-to-name gaps run 50 to 171px at 820 and each card was
     looked at; the tightest, Marketing, is clear.

     A FIRST PASS "MEASURED" THIS WITH `scrollWidth` ON THE NAME AND WAS
     MEANINGLESS: the name is a block, so that returns the column width, not
     the text. All four reported an identical 421, which is the tell. Use a
     Range over the text node.

     FOUR NUDGES ON TOP OF IT (client, 2026-09-18), so this breakpoint can be
     tuned in `?tune` like the other two. `--tk` multiplies the 1.4, and
     `--tx`/`--ty` add to the +100 / -37, in DESIGN UNITS — so the identity is
     the derived pass above rather than the design, and Reset restores what is
     reasoned here rather than throwing it away.

     NOTE THIS BREAKPOINT IGNORES THE DESKTOP NUDGES for size and position —
     it has its own derivation — but INHERITS `--dr` for rotation, because the
     rig rule it falls through to includes it. That is pre-existing and is left
     alone; the tablet rotation nudge is added on top of it rather than
     replacing it, so nothing moves at the identity. */
  .rdk-svc__object {
    width: calc(var(--w) * 1.4 * var(--tk, 1) * var(--u));
    height: calc(var(--h) * 1.4 * var(--tk, 1) * var(--u));
    left: calc((var(--x) + 100 + var(--tx, 0)) * var(--u));
    top: calc((var(--y) - 37 + var(--ty, 0)) * var(--u));
  }

  .rdk-svc__object .rdk-svc__rig {
    rotate: calc(var(--rot, 0deg) + var(--dr, 0) * 1deg + var(--tr, 0) * 1deg);
  }

  /* THE CLIENT'S OWN TABLET PASS (2026-09-18), from the panel's Copy button,
     and the first this breakpoint has had. Offsets from the derived x1.4 /
     +100 / -37 above, not from the design.

     EVERY ONE MOVES LEFT, by 38 to 222 units, and four of the five shrink.
     So the derived +100 was too far right for every card — worth knowing if
     that derivation is ever revisited, because it is a pattern rather than
     five independent corrections. Hand-placed by eye all the same: there is
     no rule to re-apply if the card padding or the name sizes move, and
     they want redoing in `?tune`. */
  .rdk-svc__object--pawn   { --tk: 1;    --tx: -168;  --ty: -25; --tr: 0; }
  .rdk-svc__object--wand   { --tk: 0.85; --tx: -126;  --ty: -43; --tr: 0; }
  .rdk-svc__object--laptop { --tk: 0.86; --tx: -222;  --ty: 0;   --tr: 0; }
  .rdk-svc__object--panel  { --tk: 0.87; --tx: -213;  --ty: 45;  --tr: 0; }
  .rdk-svc__object--mega   { --tk: 0.72; --tx: -38;   --ty: 72;  --tr: 0; }

  /* THE COPY GOES UNDER THE HEADLINE (client, 2026-09-17: "put the copy
     under the headline and make it wider").

     THE DESIGN KEEPS TWO COLUMNS HERE AND IS WORSE FOR IT: at 768 its own
     title breaks mid-word, "We know / cybersecuri / ty.", because a 560u
     column cannot hold `88 * --du` at this width. So this is a correction to
     the node rather than a departure from it — stacking is also what the
     phone block already does.

     The copy goes 328 -> 713 at 820. That is a 44em measure, which is long
     for a paragraph; the alternative is capping it, and it is one line to do
     if it reads badly. */
  .rdk-svc-sec__row {
    flex-direction: column;
    align-items: flex-start;
    gap: calc(24 * var(--u));
  }

  /* TWO LINES, PINNED (client, 2026-09-17: "keep the We know cybersecurity
     headline to two lines. I like the way it looks"). Stacking gave the
     title the row's full 713 and it collapsed to one.

     460 IS THE ONE NUMBER THAT WORKS AT BOTH THE DEVICE'S SIZE AND THE
     PANE'S, which is why it is flat px and not an em. The type is `88 *
     --du`, floored to 59.8 on a tablet and drawn at 45.1 in the pane, so a
     single em value cannot serve both. Measured widths of the whole string
     and of "cybersecurity." alone: 679 / 406 on the device, 512 / 306 in the
     pane. Any cap in [406, 512) breaks after "We know" in both; 460 sits in
     the middle of that band rather than on either edge. */
  .rdk-svc-sec__title {
    width: 100%;
    max-width: 460px;
  }

  /* 80% of the row (client: "take up slightly less of the width for the body
     copy. Maybe 80%?"). 570 at 820, a 35em measure against the 44em the full
     width gave. */
  .rdk-svc-sec__text {
    width: 80%;
  }

  /* THE LOGO CARDS ARE 20% BIGGER, PROPORTIONS HELD — 268 x 155 -> 321.6 x
     186, with the radius and the marks inside taking the same 1.2 so the
     composition scales as one thing rather than the boxes growing around
     fixed logos.

     THE STROKE IS DELIBERATELY NOT SCALED AND NOT FLOORED (client,
     2026-09-17: "did you scale the outline around the logos? Keep that the
     same stroke width as before"). It had taken a 1px floor in the same pass
     as the size, which doubled it here — 0.51 -> 1. The card's own `1 * --u`
     stands, so only the box and the mark inside it are 20% bigger. The
     bottom edge showing again is the clip fix above, which is a separate
     thing from how thick the line is.

     ONE KNOWN CONSEQUENCE: js/services-page.js holds SECONDS PER LOGO, not
     px/s, so 20% wider cards run the loop ~20% faster here — 28 -> 34 px/s
     at 820, still well under the desktop's 55. Pinning true px/s would mean
     deriving the duration from `track.scrollWidth`, which would also double
     the tablet's speed to match desktop. Left alone deliberately. */
  .rdk-svc-sec__track li {
    width: calc(321.6 * var(--u));
    height: calc(186 * var(--u));
    border-radius: calc(48 * var(--u));
  }

  .rdk-svc-sec__track img {
    width: calc(var(--lw) * 0.9516 * var(--u));
  }
}
