/* =============================================================================
   Services — ported from two Figma frames, one per breakpoint:

     · desktop — file v4zuHnfQeDWmJsqAyNqiDq, page "Services — Desktop"
       (canvas 503:6749), frame 503:6750 (1440x4351).
     · mobile  — same file, page "Services — Mobile" (canvas 1653:145),
       frame 1653:146 (392x4309).

   Every selector is prefixed .page-services so nothing leaks to another page.
   Sections are numbered to match the numbered comments in services/index.html.

   ON HARD-CODED VALUES. Same split as pages/home.css and pages/our-work.css:
   colours and font sizes are tokens with no exceptions; layout offsets are the
   literal pixels the frames measure, because this spacing does not sit on a 4px
   scale and minting a token used once would hide the measurement rather than
   document it. Structural values — band width, radii, grid tile — are tokens.

   WHAT IS DESIGNED AND WHAT IS NOT. Two frames exist, at 1440 and at 392:

     · 1100 and up   the desktop frame.
     · 768 .. 1099   DERIVED. One block, below, so a tablet window is usable
                     rather than horizontally scrolling. It carries no
                     authority; when a tablet frame arrives, re-derive it.
     · 767 and below the mobile frame.

   The shared header, footer, chat teaser and contact panel have their own
   responsive rules in base.css and components/, so they behave at every width.

   THE FRAME AND THE SHARED CHROME DISAGREE, and the chrome wins — same call the
   Our Work port made, for the same reason: 125 other pages use it.
     · the frame floats the header bar 47px in from each edge, 83 tall, starting
       at y=41; base.css draws it 45 in, 86 tall, starting at 28.
   Nothing here changes it. Raised with CLAUDE.md's open shared-chrome items.
   ========================================================================== */

.page-services {
  /* Measured off the frame, not assumed: every run of body copy on this page —
     the hero lead aside — sets 16px on an 18px line. Set once here so the rules
     below only ever restate a size. */
  font: 400 var(--text-base) / 1.125 var(--font-sans);
  color: var(--color-cream-dim);
  background: var(--color-black);
}

.page-services .site-footer {
  /* The closing lattice runs straight into the footer, as on the home page. */
  margin-top: 0;
}

.page-services {
  /* The content band. Sections on the desktop frame span roughly x 246..1189 —
     narrower than --content-max, the same way /our-work/ is. Local to the page
     rather than a token: one frame is not evidence of a site-wide measure.

     The frame's own left and right insets differ by up to 8px between sections
     (246/245/260/251) and its section centres land between x 717 and 728. That
     is drift in the frame, not a design: everything here is centred, which is
     the same "one geometry reads as a list" call the Our Work cards made. */
  --sv-band: 944px;

  /* The lattice, as four values so the mobile block can restate it rather than
     repeat the rule. Same mechanism as .ourwork-top. */
  --sv-lattice-image: url(../../media/home/grid-tile.svg);
  --sv-lattice-tile: var(--grid-tile);
  --sv-lattice-offset: var(--header-offset);
}

.page-services .services-hero__inner,
.page-services .services-method,
.page-services .services-ways,
.page-services .services-faq {
  max-width: var(--sv-band);
  margin-inline: auto;
}

/* =============================================================================
   The decorative lattice

   Two runs, not one: the frame draws it behind the hero and the method cards,
   drops it for the marquee, the service carousel and the engagement tiles, and
   brings it back for the FAQ and the closing band. Each run is a plain wrapper
   that owns the background, so the sections inside stay unaware of it.

   Height is snapped to the tile with round() so the bottom edge is always whole
   squares — the lesson the Our Work mobile port wrote down, and the reason the
   tail is expressed as a distance rather than the height as a number.
   ========================================================================== */

.page-services .services-top,
.page-services .services-bottom {
  position: relative;
}

.page-services .services-top::before,
.page-services .services-bottom::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  background-image: var(--sv-lattice-image);
  background-size: var(--sv-lattice-tile) var(--sv-lattice-tile);
  z-index: -1;
  pointer-events: none;
}

/* <body> reserves the fixed header's height; this pulls the top run back up
   under it. The frame ends this run 54px below the second method card. */
.page-services .services-top::before {
  top: calc(-1 * var(--sv-lattice-offset));
  height: round(
    calc(100% + var(--sv-lattice-offset) + 54px),
    var(--sv-lattice-tile)
  );
}

/* The closing run starts 100px above the FAQ eyebrow — the frame's first tile
   row there begins at y 2868 — and covers everything down to the footer, the
   contact panel included.

   The +172px is the collapsed contact panel's margin-bottom (contact-modal.css):
   as the last child's margin it collapses OUT of this wrapper, so 100% does not
   include it and it would otherwise be a black strip the lattice never reaches.
   round(up, …) guarantees the run overshoots into the footer rather than falling
   a partial tile short of it; the opaque footer hides the overshoot. */
.page-services .services-bottom::before {
  top: -100px;
  height: round(up, calc(100% + 100px + 172px), var(--sv-lattice-tile));
}

/* --- 1. Hero -------------------------------------------------------------- */

.page-services .services-hero {
  /* Frame: header 41..124, title ink starts at 357. <body> already reserves
     --header-offset (114), so 205 lands the title on the frame's line. */
  padding: 228px 0 0;
  text-align: center;
}

.page-services .services-hero__title {
  /* 810 is what reproduces the frame's three lines: its longest is 802px wide
     and the next word would not fit under 820. */
  max-width: 810px;
  margin-inline: auto;
  font: var(--display-l);
  letter-spacing: var(--ls-display);
  color: var(--color-white);
}

.page-services .services-hero__accent {
  color: var(--color-brand);
}

.page-services .services-hero__lead {
  max-width: 720px;
  margin: 32px auto 0;
  /* The one place the page leaves 16/18: the frame sets the lead at 20 on 22. */
  font: 400 var(--text-lg) / 1.1 var(--font-sans);
  color: var(--color-cream-dimmer);
}

/* Desktop reads as one paragraph; the mobile frame splits it in two. Keeping
   two <p> and collapsing them here is the only direction that works — a <br>
   cannot merge two paragraphs back into one. */
.page-services .services-hero__lead p {
  display: inline;
  margin: 0;
}

.page-services .services-hero__lead p + p::before {
  content: " ";
}

/* --- 2. How we build / What that means ------------------------------------ */

.page-services .services-method {
  /* 451 + 28 + 451, the frame's own pair. Narrower than --sv-band, so it is set
     here rather than inherited from it. */
  max-width: 930px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px;
  margin-top: 249px;
}

.page-services .services-method__card {
  /* 451x342 on the frame, 1.5px --color-sand border on a lifted black. */
  min-height: 342px;
  /* Asymmetric on purpose: the frame's body text runs x 294..683 inside a card
     at 260..711, which is 34 in from the left and 27 from the right. A
     symmetric 34 would take the column under the 390px its longest line needs
     and wrap the paragraph to six lines instead of five. */
  padding: 38px 24px 55px 32px;
  display: flex;
  flex-direction: column;
  background: var(--color-carbon-deep);
  border: 1.5px solid var(--color-sand);
  border-radius: var(--radius-card);
}

/* No height of its own: each diagram is a different size and sets its own, and
   the white piece in the lattice is allowed to overhang the top. The frame puts
   the lattice 880 and the hub 881 down a card whose top is 840, which is why the
   card's padding-top is 38 rather than the 24 its side gutter suggests. */
.page-services .services-method__art {
  margin-left: auto;
}

.page-services .services-method__art--grid {
  margin: 0 19px 28px auto;
}

.page-services .services-method__art--hub {
  margin: 0 14px 30px auto;
}

.page-services .services-method__eyebrow {
  margin: 0 0 6px;
  font: var(--label-int);
  letter-spacing: var(--ls-label);
  color: var(--color-brand);
}

.page-services .services-method__body {
  margin: 0;
  color: var(--color-cream-dim);
}

.page-services .services-method__body strong {
  font-weight: 700;
  color: var(--color-white);
}

/* The 3x3 lattice diagram. 28px cells on a 34px pitch, which is the frame's own
   measurement; the ninth slot is a dashed outline and the white piece parks
   above-right of it until the animation carries it in. */
.page-services .services-grid-art {
  --sv-art-cell: 28px;
  --sv-art-gap: 6px;
  /* At rest the white piece sits this far up and to the right of its cell,
     which is where the frame draws it: clear of the grid's corner, on a 45°
     line out of the hole. One value, because the slide is diagonal. */
  --sv-piece-travel: 8.5px;

  display: grid;
  grid-template-columns: repeat(3, var(--sv-art-cell));
  grid-template-rows: repeat(3, var(--sv-art-cell));
  gap: var(--sv-art-gap);
}

.page-services .services-grid-art > span {
  border: 1.5px solid var(--color-brand);
  border-radius: var(--radius-md);
}

.page-services .services-grid-art > span.is-filled {
  background: var(--color-brand);
}

/* Placed explicitly, not auto-flowed, because the white piece is placed in this
   same cell: an explicitly-placed item marks the cell occupied, and without a
   matching placement here the ghost would be pushed on to the next free slot
   and the whole lattice would shift by one. */
.page-services .services-grid-art > span.is-ghost {
  grid-area: 1 / 3;
  border-style: dashed;
  opacity: 0.45;
}

/* Placed in the dashed cell rather than positioned near it, so "slides into the
   hole" is literally a transform back to 0,0.

   Selected through `> span` so it outranks the rule that gives every cell an
   orange border — without the type selector that rule wins on specificity and
   the white piece comes out orange, indistinguishable from a cell. */
.page-services .services-grid-art > span.services-grid-art__piece {
  grid-area: 1 / 3;
  background: var(--color-black);
  border: 1.5px solid var(--color-white);
  border-radius: var(--radius-md);
  transform: translate(var(--sv-piece-travel), calc(-1 * var(--sv-piece-travel)));
}

/* The hub diagram: three nodes, a dashed harness, and the core. */
/* Measured off the frame: nodes 22 square on a 36 pitch at x 0, the spine at
   x 40, the core 51 square at x 59. The whole diagram is 110 x 94. */
.page-services .services-hub-art {
  --sv-hub-node: 22px;
  --sv-hub-pitch: 36px;
  --sv-hub-spine: 40px;
  --sv-hub-core: 51px;
  --sv-hub-wire: 1.5px;

  position: relative;
  width: 110px;
  height: 94px;
}

.page-services .services-hub-art__node {
  position: absolute;
  left: 0;
  width: var(--sv-hub-node);
  height: var(--sv-hub-node);
  border: var(--sv-hub-wire) solid var(--color-brand);
  border-radius: var(--radius-md);
}

.page-services .services-hub-art__node:nth-child(1) {
  top: 0;
}
.page-services .services-hub-art__node:nth-child(2) {
  top: var(--sv-hub-pitch);
}
.page-services .services-hub-art__node:nth-child(3) {
  top: calc(var(--sv-hub-pitch) * 2);
}

/* Every segment is a dash pattern painted as a repeating background, which is
   the only way to make the dashes travel: a `border-style: dashed` cannot be
   scrolled. The two periods are the frame's own — 4px on the horizontals, 6px
   on the spine — and the durations below keep both at the same 10px per second
   so the current stays continuous through the corners. */
.page-services .services-hub-art__wire {
  position: absolute;
  background-repeat: repeat;
}

.page-services .services-hub-art__wire--top,
.page-services .services-hub-art__wire--middle,
.page-services .services-hub-art__wire--bottom {
  left: var(--sv-hub-node);
  height: var(--sv-hub-wire);
  background-image: linear-gradient(
    90deg,
    var(--color-brand) 0 2px,
    transparent 2px 4px
  );
  background-size: 4px var(--sv-hub-wire);
}

/* The stubs stop at the spine; the middle one carries straight on into the core. */
.page-services .services-hub-art__wire--top,
.page-services .services-hub-art__wire--bottom {
  width: calc(var(--sv-hub-spine) + var(--sv-hub-wire) - var(--sv-hub-node));
}

.page-services .services-hub-art__wire--middle {
  width: calc(100% - var(--sv-hub-core) - var(--sv-hub-node));
}

.page-services .services-hub-art__wire--top {
  top: calc(var(--sv-hub-node) / 2);
}

.page-services .services-hub-art__wire--middle {
  top: calc(var(--sv-hub-pitch) + var(--sv-hub-node) / 2);
}

.page-services .services-hub-art__wire--bottom {
  top: calc(var(--sv-hub-pitch) * 2 + var(--sv-hub-node) / 2);
}

/* Two spine halves, not one line, because they carry opposite currents: the top
   node's data travels down to the junction and the bottom node's travels up. */
.page-services .services-hub-art__wire--spine-down,
.page-services .services-hub-art__wire--spine-up {
  left: var(--sv-hub-spine);
  width: var(--sv-hub-wire);
  height: var(--sv-hub-pitch);
  background-image: linear-gradient(
    180deg,
    var(--color-brand) 0 3px,
    transparent 3px 6px
  );
  background-size: var(--sv-hub-wire) 6px;
}

.page-services .services-hub-art__wire--spine-down {
  top: calc(var(--sv-hub-node) / 2);
}

.page-services .services-hub-art__wire--spine-up {
  top: calc(var(--sv-hub-pitch) + var(--sv-hub-node) / 2);
}

.page-services .services-hub-art__core {
  position: absolute;
  right: 0;
  top: calc(
    var(--sv-hub-pitch) + var(--sv-hub-node) / 2 - var(--sv-hub-core) / 2
  );
  width: var(--sv-hub-core);
  height: var(--sv-hub-core);
  display: grid;
  place-items: center;
  background: var(--color-brand);
  border-radius: var(--radius-lg);
}

.page-services .services-hub-art__core::before {
  content: "";
  width: 20px;
  height: 20px;
  background: var(--color-black);
  border-radius: var(--radius-pill);
}


/* --- 3. Client logo marquee ----------------------------------------------- */
/* The shared component — components/marquee.css, at its default speed. */

.page-services .services-marquee {
  --marquee-row-gap: 16px;
  margin-top: 84px;
}

/* --- 4. The four services ------------------------------------------------- */

.page-services .services-offer {
  margin-top: 75px;
}

.page-services .services-offer__panel {
  position: relative;
  max-width: 1157px;
  min-height: 629px;
  margin-inline: auto;
  padding: 20px 0 44px;
  background: var(--gradient-services-band);
  border-radius: var(--radius-panel);
  overflow: hidden;
}

.page-services .services-offer__eyebrow {
  margin: 0 0 26px 49px;
  font: var(--label-int);
  letter-spacing: var(--ls-label);
  color: var(--color-cream);
}

/* A snap-scroller at every width. Scroll padding puts the active card 49px from
   the panel's left edge, which is where the frame draws it, and the next card
   falls off the right edge at reduced opacity. */
.page-services .services-offer__track {
  display: flex;
  gap: 88px;
  padding-inline: 49px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 49px;
  scrollbar-width: none;
}

.page-services .services-offer__track::-webkit-scrollbar {
  display: none;
}

.page-services .services-card {
  flex: none;
  /* 844, not the 821 the frame's first card measures. Figma's auto-layout
     hugged each card's own copy, so the four run 821..844 wide and the widest
     is the only one whose title fits on one line. One geometry for all four is
     what makes the strip read as one card repeated — the same call the Our Work
     case cards made — and 844 is the width that keeps every title unwrapped. */
  width: 844px;
  min-height: 512px;
  scroll-snap-align: start;
  display: grid;
  /* The two panels touch; the wash shows through where their rounded corners
     pull apart, which is what the frame draws. */
  grid-template-columns: 1fr 254px;
  gap: 0;
  padding: 0;
  /* Only the card under the snap point is fully opaque; the rest sit back into
     the wash, which is what makes the strip read as one card at a time. The
     first card is rendered .is-active in the markup, so it is never faded in
     on load — Safari did not repaint that fade until the strip was scrolled. */
  opacity: 0.35;
  transition: opacity 0.35s ease;
}

.page-services .services-card.is-active {
  opacity: 1;
}

.page-services .services-card__body {
  padding: 34px 44px 76px;
  display: flex;
  flex-direction: column;
  background: var(--color-black);
  border-radius: var(--radius-panel-lg);
}

.page-services .services-card__title {
  margin: 0;
  font: 700 var(--text-display-s) / 1 var(--font-sans);
  letter-spacing: var(--ls-display);
  color: var(--color-brand);
}

.page-services .services-card__eyebrow {
  margin: 16px 0 0;
  font: var(--label-m);
  letter-spacing: var(--ls-label);
  color: var(--color-sand-soft);
}

.page-services .services-card__lead {
  margin: 16px 0 0;
  color: var(--color-cream-dim);
}

.page-services .services-card__points {
  /* Pinned to the bottom of the card rather than spaced off the lead: that is
     what keeps all four cards 512 tall although their points run two lines on
     one card and three on another. */
  margin: auto 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.page-services .services-card__points li {
  color: var(--color-cream-dim);
}

.page-services .services-card__points strong {
  font-weight: 700;
  color: var(--color-white);
}

.page-services .services-card__aside {
  padding: 46px 24px 66px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  background: var(--color-black);
  border: 1.5px solid var(--color-sand);
  border-radius: var(--radius-panel-inner);
}

.page-services .services-card__included {
  flex: 1;
  align-self: stretch;
  display: flex;
  flex-direction: column;
}

.page-services .services-card__included-title {
  margin: 0 0 27px;
  font: var(--label-m);
  letter-spacing: var(--ls-label);
  color: var(--color-sand-soft);
}

.page-services .services-card__included-list {
  flex: 1;
  margin: 0 0 52px;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 20px;
  font-weight: 700;
  color: var(--color-white);
}

.page-services .services-card__cta {
  width: 185px;
  height: 34px;
  padding: 0;
  font-size: var(--text-md);
}

/* The desktop controls: the same half-disc the FAQ and the home carousel use,
   in black on the wash, one on each side of the active card and --sv-step-gap
   clear of it (the frame leaves air between card and control; flush read as
   touching the content). The frame draws
   only the forward one; its mirror is here so the strip can be walked back.
   Each hides itself at the end it points at — services.js sets [hidden] — so
   the pair doubles as a position indicator. */
.page-services .services-offer__step {
  --sv-step-gap: 16px;
  position: absolute;
  top: 50%;
  width: 27px;
  height: 46px;
  padding: 0;
  transform: translateY(-50%);
  background: var(--color-black);
  border: none;
  cursor: pointer;
  transition: opacity var(--transition);
}

.page-services .services-offer__step--next {
  /* the track's 49px inset plus one 844px card, plus the gap */
  left: calc(893px + var(--sv-step-gap));
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}

.page-services .services-offer__step--prev {
  /* the same 49px inset, less the control's own width and the gap */
  left: calc(22px - var(--sv-step-gap));
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
}

.page-services .services-offer__step:hover {
  opacity: 0.75;
}

.page-services .services-offer__step[hidden] {
  display: none;
}

/* Dots and the swipe hint are the mobile control; the desktop frame has neither.
   home.js, labs.js and this file all generate dots the same way, one per item,
   so the count follows the data file. */
.page-services .services-offer__nav {
  display: none;
}

/* The dots are components/carousel-dots.css, at its defaults. */
.page-services .services-offer__dots {
  display: flex;
  justify-content: center;
  gap: 10px;
}

.page-services .services-offer__hint {
  margin: 14px 0 0;
  text-align: center;
  font: 400 var(--text-sm) / 1.2 var(--font-sans);
  color: var(--color-black);
}

/* --- 5. Ways we work together --------------------------------------------- */

.page-services .services-ways {
  margin-top: 118px;
  text-align: center;
}

.page-services .services-ways__eyebrow {
  margin: 0;
  font: var(--label-int);
  letter-spacing: var(--ls-label);
  color: var(--color-brand);
}

.page-services .services-ways__lead {
  max-width: 600px;
  margin: 13px auto 0;
  color: var(--color-cream-dim);
}

/* The mobile frame sets the last sentence in bold and the desktop frame does
   not, so the emphasis is in the markup and turned off here. */
.page-services .services-ways__lead strong {
  font-weight: inherit;
}

.page-services .services-ways__sub {
  margin: 39px 0 0;
  font: 700 var(--text-xl) / 1 var(--font-sans);
  letter-spacing: var(--ls-display);
  color: var(--color-sand-soft);
}

/* Six 184px squares in three columns with the detail panel beside them. The
   panel is a grid child rather than a sibling so the mobile block can drop it
   between the tile rows without moving any markup. */
.page-services .services-ways__board {
  width: 967px;
  margin: 48px auto 0;
  display: grid;
  grid-template-columns: repeat(3, 184px) 320px;
  grid-template-rows: repeat(2, 184px);
  gap: 26px 30px;
}

.page-services .services-way {
  display: grid;
  place-items: center;
  padding: 10px 14px;
  background: var(--color-cream);
  border: none;
  border-radius: var(--radius-lg);
  font: 700 var(--text-xl) / 1 var(--font-sans);
  letter-spacing: var(--ls-display);
  color: var(--color-black);
  text-align: center;
  cursor: pointer;
  transition: background var(--transition);
}

/* "Los 6 elementos parten de un estado off-white … al hacer hover el elemento
   activo cambia a orange, manteniendo las letras en negro." The state is driven
   from services.js so hover, focus and the mobile tap all reach it the same
   way; :hover alone could not also serve the keyboard. */
.page-services .services-way.is-active {
  background: var(--color-brand);
}

.page-services .services-ways__panel {
  grid-column: 4;
  grid-row: 1 / 3;
  margin-left: 5px; /* the frame's tile-to-panel gap is 35, the tile gap 30 */
  padding: 20px 28px 28px;
  display: flex;
  flex-direction: column;
  background: var(--color-black);
  border: 1.5px solid var(--color-sand);
  border-radius: var(--radius-card);
  text-align: left;
}

.page-services .services-ways__bulb {
  width: 80px;
  height: auto;
  margin: 0 0 0 auto;
}

.page-services .services-ways__copy {
  margin: 32px 0 0;
  color: var(--color-cream-dim);
}

/* --- 6. FAQ --------------------------------------------------------------- */

.page-services .services-faq {
  margin-top: 132px;
}

.page-services .services-faq__eyebrow {
  margin: 0 0 36px;
  font: var(--label-int);
  letter-spacing: var(--ls-label);
  color: var(--color-brand);
  text-align: center;
}

.page-services .services-faq__item {
  border-bottom: 1.5px solid var(--color-sand);
}

.page-services .services-faq__item + .services-faq__item {
  margin-top: 36px;
}

.page-services .services-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  /* The frame stops the icon 38px short of the rule's right end. */
  padding: 0 38px 14px 0;
  font: 700 var(--text-xl) / 1.1 var(--font-sans);
  letter-spacing: var(--ls-display);
  /* Secondary dark, #B0AA97 ("These titles should be displayed in color:
     B0AA97", staging review 2026-09-24, pinned on this element). */
  color: var(--color-sand-soft);
  cursor: pointer;
  list-style: none;
}

.page-services .services-faq__q::-webkit-details-marker {
  display: none;
}

/* The state indicator: a half-disc outline, flat edge up when closed, flipped
   when open. The same motif as the home carousel's arches, drawn here rather
   than shipped as an SVG so it takes the brand colour from a token and can
   rotate. */
.page-services .services-faq__icon {
  flex: none;
  width: 36px;
  height: 18px;
  border: 2.5px solid var(--color-brand);
  border-radius: 0 0 var(--radius-pill) var(--radius-pill);
  transition: transform 0.25s ease;
}

.page-services .services-faq__item[open] .services-faq__icon {
  transform: rotate(180deg);
}

.page-services .services-faq__a {
  overflow: hidden;
}

.page-services .services-faq__a p {
  margin: 7px 0 10px;
  max-width: 945px;
  color: var(--color-cream-dim);
}

/* --- 7. Final CTA --------------------------------------------------------- */

/* The band is components/final-cta.css; this page owns only the space above
   it. */
.page-services .final-cta {
  padding-top: 188px;
}

/* =============================================================================
   768 .. 1099 — DERIVED, NOT DESIGNED

   No frame exists between 392 and 1440. Everything in this block is here so a
   window in that range is usable instead of scrolling horizontally, and nothing
   in it should be cited as the design. The mobile block below IS designed and
   overrides whatever it needs to.
   ========================================================================== */

@media (max-width: 1100px) {
  .page-services {
    --sv-band: 100%;
  }

  .page-services .services-hero__inner,
  .page-services .services-method,
  .page-services .services-ways,
  .page-services .services-faq {
    padding-inline: var(--space-8);
  }

  .page-services .services-offer__panel {
    max-width: calc(100% - var(--space-8) * 2);
  }

  .page-services .services-card {
    width: min(844px, calc(100vw - 150px));
  }

  .page-services .services-offer__step--next {
    left: auto;
    right: 0;
    border-radius: var(--radius-pill) 0 0 var(--radius-pill);
  }

  .page-services .services-offer__step--prev {
    left: 0;
    border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  }

  .page-services .services-ways__board {
    width: 100%;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: auto auto;
  }

  .page-services .services-ways__panel {
    grid-column: 1 / -1;
    grid-row: auto;
    flex-direction: row-reverse;
    align-items: center;
    gap: 24px;
    margin-left: 0;
  }

  .page-services .services-ways__bulb {
    flex: none;
    margin: 0;
  }

  .page-services .services-ways__copy {
    margin: 0;
  }

}

/* =============================================================================
   767 and below — the mobile frame (1653:146, 392x4309)

   Type here is measured off that frame's own render, the same way the desktop
   block was: 32/32 on the hero title, 14 on an 18.9 line for every run of body
   copy, 16 on the questions, and the eyebrows unchanged at 20.
   ========================================================================== */

@media (max-width: 767px) {
  .page-services {
    --sv-lattice-tile: var(--grid-tile-sm);
    --sv-lattice-image: url(../../media/our-work/grid-tile-sm.svg);
    --sv-lattice-offset: var(--header-offset-sm);
    /* Thirteen logos, all reduced by the same factor: the frame's first row
       stands 35px against the desktop frame's 65, the second 38 against 73. */
    --marquee-logo-scale: 0.53;

    font: 400 var(--text-sm) / 1.35 var(--font-sans);
  }

  /* The mobile tile is the Our Work cut — 49.11px with a 1.5px stroke that does
     not scale down with the square — but this page is dark, so it needs the
     dark stroke. Tinted rather than duplicated as a second asset. */
  .page-services .services-top::before,
  .page-services .services-bottom::before {
    filter: invert(1) brightness(0.11);
  }

  /* The frame does not run these to a common gutter: it measures the method
     card at 298, the FAQ rules at 278, the tile block at 271 and the closing
     band at 285, each centred. They are set per section rather than forced onto
     one band. The padding is a floor for anything narrower than 392. */
  .page-services .services-hero__inner,
  .page-services .services-method,
  .page-services .services-ways,
  .page-services .services-faq {
    max-width: none;
    padding-inline: var(--page-gutter-sm);
  }

  .page-services .services-method {
    max-width: 298px;
    margin-inline: auto;
    padding-inline: 0;
  }

  .page-services .services-faq {
    max-width: 278px;
    margin-inline: auto;
    padding-inline: 0;
  }

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

  .page-services .services-hero {
    padding-top: 110px;
  }

  .page-services .services-hero__title {
    /* 280 reproduces the frame's five lines to the pixel: 265, 265, 257, 257,
       254 against its own 266, 265, 257, 257, 252. */
    max-width: 280px;
    font: 700 var(--text-display-s) / 1 var(--font-sans);
    letter-spacing: var(--ls-display);
  }

  /* 230: the frame's widest lead line is 216px and the next word would not fit
     under 244, so the paragraph is measured much narrower than the title. */
  .page-services .services-hero__lead {
    max-width: 230px;
    margin-top: 28px;
    font: inherit;
  }

  /* Back to two paragraphs, which is what the frame draws. */
  .page-services .services-hero__lead p {
    display: block;
  }

  .page-services .services-hero__lead p + p {
    margin-top: 20px;
  }

  .page-services .services-hero__lead p + p::before {
    content: none;
  }

  /* --- Method cards ------------------------------------------------------ */

  .page-services .services-method {
    grid-template-columns: 1fr;
    /* The frame draws both cards 299 and 298 tall although their copy differs
       by a line, so they are one height here too. */
    grid-auto-rows: 1fr;
    gap: 29px;
    margin-top: 138px;
  }

  /* 32 in from each edge, which is what the frame measures: its widest body
     line is 231px inside a 298px card. A tighter gutter takes the paragraph to
     seven lines instead of six. */
  .page-services .services-method__card {
    min-height: 0;
    padding: 32px 30px 43px;
  }

  .page-services .services-method__eyebrow {
    margin-bottom: 7px;
    font: var(--label-m);
    letter-spacing: var(--ls-label);
  }

  /* No fixed height on the wrapper. It used to carry one, sized for the
     lattice, and the taller hub diagram simply overflowed it and printed on top
     of the eyebrow and the first line of copy. Each diagram owns its own box. */
  /* The frame puts both diagrams' tops 33..36 below the card's, and both
     eyebrows on the same line — so the taller lattice sits tight against its
     eyebrow and the shorter hub carries the difference underneath. */
  .page-services .services-method__art--grid {
    margin: 0 10px 2px auto;
  }

  .page-services .services-method__art--hub {
    margin: 4px 0 41px auto;
  }

  .page-services .services-grid-art {
    --sv-art-cell: 18px;
    --sv-art-gap: 3px;
    --sv-piece-travel: 6px;
  }

  /* The frame sets the lead-in in the same weight as the rest here. */
  .page-services .services-method__body strong {
    font-weight: 400;
    color: var(--color-cream-dim);
  }

  /* 68 x 56 on the frame, against 110 x 94 at 1440. Every part is a variable on
     the component, so the whole diagram scales from these five. */
  .page-services .services-hub-art {
    --sv-hub-node: 12px;
    --sv-hub-pitch: 22px;
    --sv-hub-spine: 25px;
    --sv-hub-core: 30px;

    width: 68px;
    height: 56px;
  }

  .page-services .services-hub-art__core::before {
    width: 12px;
    height: 12px;
  }


  /* --- Marquee ----------------------------------------------------------- */

  .page-services .services-marquee {
    --marquee-row-gap: 17px;
    --marquee-gap: 32px;
    margin-top: 37px;
  }

  /* --- Services carousel -------------------------------------------------- */

  .page-services .services-offer {
    margin-top: 49px;
  }

  .page-services .services-offer__panel {
    max-width: none;
    min-height: 0;
    padding: 18px 0 10px;
    border-radius: 0;
  }

  .page-services .services-offer__eyebrow {
    margin: 0 0 17px;
    text-align: center;
  }

  .page-services .services-offer__track {
    gap: 16px;
    padding-inline: var(--page-gutter-sm);
    scroll-padding-left: var(--page-gutter-sm);
  }

  /* One black panel, not two: below 768 the frame draws the included box and
     the button inside the card rather than beside it, so the card itself takes
     the fill and the body stops painting one. */
  .page-services .services-card {
    width: calc(100vw - var(--page-gutter-sm) * 2);
    min-height: 0;
    grid-template-columns: 1fr;
    background: var(--color-black);
    border-radius: var(--radius-card-sm);
  }

  .page-services .services-card__body {
    padding: 20px 22px 20px;
    background: none;
    border-radius: 0;
  }

  .page-services .services-card__title {
    font-size: var(--text-xl);
  }

  .page-services .services-card__eyebrow {
    margin-top: 8px;
    font: var(--label-s);
    letter-spacing: var(--ls-label);
  }

  .page-services .services-card__lead {
    margin-top: 10px;
  }

  /* The frame keeps only the labels here, as an orange-square bulleted list. */
  .page-services .services-card__points {
    margin: 10px 0 0;
    gap: 0;
  }

  .page-services .services-card__points li {
    display: flex;
    align-items: baseline;
    gap: 10px;
    color: var(--color-white);
  }

  .page-services .services-card__points li::before {
    content: "";
    flex: none;
    width: 8px;
    height: 8px;
    background: var(--color-brand);
  }

  .page-services .services-card__point-text {
    display: none;
  }

  /* The border moves inward: the frame draws it round the list alone and puts
     the button below it on the card's own black. */
  .page-services .services-card__aside {
    padding: 0 22px 16px;
    gap: 18px;
    background: none;
    border: none;
    border-radius: 0;
  }

  .page-services .services-card__included {
    align-self: stretch;
    padding: 14px 16px 16px;
    background: var(--color-black);
    border: 1.5px solid var(--color-sand);
    border-radius: var(--radius-lg);
  }

  .page-services .services-card__included-title {
    margin-bottom: 16px;
    text-align: center;
  }

  .page-services .services-card__included-list {
    margin: 0;
    gap: 0;
    text-align: center;
  }

  /* The frame alternates the weight down the list. */
  .page-services .services-card__included-list li:nth-child(even) {
    font-weight: 700;
  }

  .page-services .services-card__included-list li:nth-child(odd) {
    font-weight: 400;
  }

  .page-services .services-card__cta {
    width: 160px;
    height: 34px;
  }

  .page-services .services-offer__step {
    display: none;
  }

  .page-services .services-offer__nav {
    display: block;
    margin-top: 28px;
  }

  /* --- Ways we work together --------------------------------------------- */

  .page-services .services-ways {
    margin-top: 45px;
  }

  /* 240 is what breaks the eyebrow after "WORK", which is what the frame
     draws; the string fits on one line at any width over ~325. */
  .page-services .services-ways__eyebrow {
    max-width: 240px;
    margin-inline: auto;
  }

  .page-services .services-ways__lead {
    max-width: 235px;
    margin-top: 11px;
  }

  .page-services .services-ways__lead strong {
    font-weight: 700;
    color: var(--color-white);
  }

  .page-services .services-ways__sub {
    margin-top: 18px;
    font: 700 var(--text-base) / 1.2 var(--font-sans);
  }

  /* Two columns of three rows, and the panel drops into the flow directly under
     the row its open tile belongs to — "se despliega la información
     correspondiente debajo de la fila a la que pertenece la card". `order` is
     what places it; services.js sets --sv-panel-order to twice the row index
     plus two, so the panel follows the second tile of that row. */
  .page-services .services-ways__board {
    width: 271px;
    margin-top: 34px;
    grid-template-columns: repeat(2, 130px);
    grid-template-rows: none;
    gap: 11px;
  }

  .page-services .services-way {
    order: var(--sv-order);
    height: 93px;
    grid-template-rows: 1fr auto;
    gap: 6px;
    padding: 12px 8px 10px;
    font: 700 var(--text-base) / 1.15 var(--font-sans);
    letter-spacing: var(--ls-display);
  }

  /* The chevron the frame draws: orange pointing down, black and flipped when
     the card is open. */
  .page-services .services-way::after {
    content: "";
    width: 12px;
    height: 12px;
    border-right: 2px solid var(--color-brand);
    border-bottom: 2px solid var(--color-brand);
    transform: translateY(-4px) rotate(45deg);
    transition:
      transform 0.25s ease,
      border-color var(--transition);
  }

  .page-services .services-way.is-active::after {
    border-color: var(--color-black);
    transform: translateY(2px) rotate(225deg);
  }

  .page-services .services-ways__panel {
    order: var(--sv-panel-order, 7);
    grid-column: 1 / -1;
    grid-row: auto;
    margin-left: 0;
    padding: 6px 0 4px;
    background: none;
    border: none;
    border-radius: 0;
    /* Left-aligned here, unlike everything else in this centred section: the
       frame sets the open card's copy flush with the grid's left edge. */
    text-align: left;
  }

  /* No bulb on mobile: the frame prints the copy on the page itself. */
  .page-services .services-ways__bulb {
    display: none;
  }

  .page-services .services-ways__copy {
    margin: 0;
  }

  /* Closed accordion: nothing shows. services.js keeps one open at a time. */
  .page-services .services-ways__board.is-collapsed .services-ways__panel {
    display: none;
  }

  /* --- FAQ --------------------------------------------------------------- */

  .page-services .services-faq {
    margin-top: 59px;
  }

  .page-services .services-faq__eyebrow {
    margin-bottom: 30px;
  }

  .page-services .services-faq__q {
    padding: 0 26px 11px 0;
    gap: var(--space-4);
    font: 700 var(--text-base) / 1.2 var(--font-sans);
    letter-spacing: var(--ls-display);
  }

  .page-services .services-faq__icon {
    width: 24px;
    height: 12px;
    border-width: 2px;
  }

  .page-services .services-faq__item + .services-faq__item {
    margin-top: 10px;
  }

  .page-services .services-faq__a p {
    margin: 4px 0 0;
    font-size: var(--text-2xs);
  }

  /* --- Final CTA ---------------------------------------------------------- */

  .page-services .final-cta {
    padding-top: 110px;
  }

  /* --- Mr. Orange --------------------------------------------------------- */
  /* The frame draws the teaser as a 56px black disc with the face inside and no
     speech bubble, exactly as the Our Work mobile frame does. PAGE-SCOPED for
     the same reason it is there: components/chat-teaser.css is shared with the
     home page, whose own mobile frame nobody has checked. */
  .page-services .chat-teaser__avatar {
    box-sizing: border-box;
    width: 56px;
    height: 56px;
    padding: 12px 8px;
    margin: 0;
    object-fit: contain;
    background: var(--color-black);
    border-radius: var(--radius-pill);
  }

  .page-services .chat-teaser__bubble {
    display: none;
  }
}

/* =============================================================================
   Motion

   Handoff Notes, "Animaciones al hacer scroll": entrances fire as each element
   enters the viewport, not all at load. The method cards use the shared
   fade-up, components/reveal.css.

   The two diagram loops are the 2026-09-09 comments, followed literally: a
   3.6s cycle for the lattice piece and 3.2s / 1.6s for the hub.
   ========================================================================== */

/* "El cuadrado blanco se desliza en 45 grados hacia abajo a la izquierda hasta
   meterse en el hueco vacío de la grilla … y vuelve. Todo el ciclo dura 3,6
   segundos." The hole fades out as the piece arrives; the two filled squares
   dim, the second half a second later than the first. */
@keyframes services-piece-slide {
  0%,
  8% {
    transform: translate(
      var(--sv-piece-travel),
      calc(-1 * var(--sv-piece-travel))
    );
  }
  30%,
  58% {
    transform: translate(0, 0);
  }
  80%,
  100% {
    transform: translate(
      var(--sv-piece-travel),
      calc(-1 * var(--sv-piece-travel))
    );
  }
}

@keyframes services-ghost-fade {
  0%,
  8% {
    opacity: 0.45;
  }
  30%,
  58% {
    opacity: 0;
  }
  80%,
  100% {
    opacity: 0.45;
  }
}

@keyframes services-cell-dim {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}

.page-services .services-grid-art__piece {
  animation: services-piece-slide 3.6s ease-in-out infinite;
}

.page-services .services-grid-art > span.is-ghost {
  animation: services-ghost-fade 3.6s ease-in-out infinite;
}

.page-services .services-grid-art > span.is-filled {
  animation: services-cell-dim 3.6s ease-in-out infinite;
}

.page-services .services-grid-art > span.is-filled:last-of-type {
  animation-delay: 0.5s;
}

/* "Los tres cuadrados … pasan de opacidad 100% a 30% y vuelven, uno después del
   otro … El cuadrado grande crece un 7% y vuelve, latiendo desde su centro.
   Ciclos de 3,2 segundos. La corriente de la línea es más rápida: 1,6." */
@keyframes services-node-dim {
  0%,
  40%,
  100% {
    opacity: 1;
  }
  15% {
    opacity: 0.3;
  }
}

@keyframes services-core-pulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.07);
  }
}

/* One pattern period per keyframe pass, so every loop is seamless. The three
   durations are set from one speed rather than from one duration: the
   horizontals repeat every 4px and the spine every 6px, so 0.4s and 0.6s put
   both at 10px per second and the current does not change pace at a corner.

   The comment says "1,6 segundos por vuelta". Read as one dash period that is
   2.5px per second — invisible, which is how the first attempt at this ended up
   looking static. Read as one traversal of the longest route it is far too
   fast. 10px/s is the speed at which the thing the comment is actually
   describing — data leaving the three boxes and arriving at the core — reads. */
@keyframes services-wire-flow-x {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 4px 0;
  }
}

@keyframes services-wire-flow-down {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 0 6px;
  }
}

@keyframes services-wire-flow-up {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 0 -6px;
  }
}

.page-services .services-hub-art__wire--top,
.page-services .services-hub-art__wire--middle,
.page-services .services-hub-art__wire--bottom {
  animation: services-wire-flow-x 0.4s linear infinite;
}

.page-services .services-hub-art__wire--spine-down {
  animation: services-wire-flow-down 0.6s linear infinite;
}

.page-services .services-hub-art__wire--spine-up {
  animation: services-wire-flow-up 0.6s linear infinite;
}

.page-services .services-hub-art__node {
  animation: services-node-dim 3.2s ease-in-out infinite;
}

.page-services .services-hub-art__node:nth-child(2) {
  animation-delay: 0.35s;
}

.page-services .services-hub-art__node:nth-child(3) {
  animation-delay: 0.7s;
}

.page-services .services-hub-art__core {
  animation: services-core-pulse 3.2s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .page-services .services-grid-art__piece,
  .page-services .services-grid-art > span,
  .page-services .services-hub-art__node,
  .page-services .services-hub-art__core,
  .page-services .services-hub-art__wire {
    animation: none;
  }
}
