/* ==========================================================================
   Services page — Figma 6528:7938 "Services_Update"
   ==========================================================================
   Four stacked bands on one shared 4-column grid: hero, then Strategy /
   Concept / Execution.

   This file owns GEOMETRY ONLY — where things sit, not how they look:
     · vertical rhythm  → .section (tokens.css, --section-py 120/80/60)
     · dark ground      → .section--dark (tokens.css)
     · heading + intro  → the text widget, via its --tw-* knobs
     · eyebrow          → .title-xs  (tokens.css)
     · service list     → .text-lg   (tokens.css)
   No font-size, line-height or colour is restated here; the tokens already
   scale themselves per breakpoint, so this file never repeats that either. */

/* ── Hero ──────────────────────────────────────────────────────────
   The header is fixed and overlays the page. Figma puts the title's box at
   y 186 with the nav at 0, so clear the header and add Figma's own 100px. */
.services-hero {
  /* One number owns the widget row gap, the gap down to the media when there
     is any, AND the space under the whole band. The hero has a single
     .container, which no longer carries a bottom padding of its own (that
     site-wide default was removed from tokens.css), so this IS the gap --
     nothing else adds to it. 100 / 60 / 40 across the breakpoints. */
  --services-hero-gap: var(--space-100);
  padding-top: calc(var(--header-h) + var(--space-100));
  padding-bottom: var(--services-hero-gap);
}

/* Only the widget's knobs. Figma 6528:8232 — heading Title/XL, lead
   Body/Large. Body/Large IS the widget's default, so the body is left alone
   rather than restated: no px size is named anywhere in this file. */
.services-hero .text-grid {
  --tw-heading-size: var(--title-xl);
  --tw-heading-lh: var(--title-xl-lh);
  --tw-row-gap: var(--services-hero-gap);
  --tw-pad-bottom: 0;
}

/* Figma: 1329×704 at content width, 20px corners, with a bottom-up scrim.
   The ratio is kept so the crop is the designed one at every width. */
.services-hero__media {
  position: relative;
  margin: var(--services-hero-gap) 0 0;
  aspect-ratio: 1329 / 704;
  border-radius: var(--radius-image);
  overflow: hidden;
}
.services-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.services-hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/* ── Service block ──────────────────────────────────────────────────
   The section's own padding comes from .section on the element. Only the
   grid's internal rhythm is Figma-specific, so that is the one knob here.
   The text widget spans the full width and keeps its own 4-column split
   inside; because both grids are `repeat(4, minmax(0, 1fr))` with the same
   20px column gap inside the same container, the widget's cols 3–4 land
   exactly under this grid's cols 3–4 — which is what lets the list sit
   flush beneath the intro. */
.services-block { --services-row-gap: var(--space-100); }

.services-block__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 20px;
  row-gap: var(--services-row-gap);
  padding-bottom: 0;   /* .container's own bottom padding would double the rhythm */
}

/* The grid's row-gap owns the spacing below the widget, so its trailing
   padding is switched off and its heading→body gap matches the block. */
.services-block__grid > .text-grid {
  grid-column: 1 / -1;
  --tw-row-gap: var(--services-row-gap);
  --tw-pad-bottom: 0;
}

/* Placement only — the look is .title-xs / .text-lg in the markup. */
.services-block__eyebrow {
  grid-column: 1 / span 2;
  margin: 0;
}

/* The service list sits in the body column, one line per item, no bullets. */
.services-block__list {
  grid-column: 3 / span 2;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Dark ground: same approach as the text widget's own rule in
   components/text-widget.css — keyed on [data-theme="dark"], and only ever
   touching this page's own elements, never the shared utility classes. */
[data-theme="dark"] .services-block__eyebrow,
[data-theme="dark"] .services-block__list { color: var(--text-light); }

.services-block__cases {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}

/* ── Tablet ─────────────────────────────────────────────────────── */
@media (max-width: 1024px) {
  .services-block { --services-row-gap: 60px; }
  .services-hero { --services-hero-gap: 60px; }
}

/* ── Mobile ──────────────────────────────────────────────────────
   Everything goes full width — the widget already stacks at this breakpoint,
   so the eyebrow, the list and the case pair follow it. */
@media (max-width: 767px) {
  .services-block { --services-row-gap: 40px; }
  .services-hero {
    --services-hero-gap: 40px;
    padding-top: calc(var(--header-h) + 40px);
  }
  .services-hero__media {
    aspect-ratio: 4 / 3;
    border-radius: 16px;
  }
  .services-block__eyebrow,
  .services-block__list { grid-column: 1 / -1; }
  .services-block__cases { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}

/* ── Block label: "Think \\ Strategy" ──────────────────────────────────────
   Two ACF fields (prefix + label) with this span as the separator, so the
   editor never has to type a magic character. Blue slashes are Figma's. */
.services-block__slash {
  color: var(--dragster-blue);
  margin-left: 0.3em;   /* the space AFTER comes from the markup's own newline */
}

/* ── Third ground ─────────────────────────────────────────────────────────
   Figma's Make \\ Execution block sits on --bg-dark (#1C1B1F), not the
   --bg-secondary (#1C1024) that the Think block uses. Sampled straight out of
   the render - the two darks really are different colours, so one
   .section--dark cannot serve both.
   Only the BACKGROUND is repainted here; the light text, the header flip and
   the widget's dark mode all ride on .section--dark + data-theme="dark",
   which the template still adds. Equal specificity to .section--dark, so this
   wins purely on source order (page-services.css is enqueued after tokens). */
.services-block--deep { background: var(--bg-dark); }


/* -- Service list: expandable rows -------------------------------------
   Figma 6528:7938 puts a hairline UNDER every row (none above the first --
   checked by scanning the render) and a + on the right, on a 42px pitch
   with Body/Large text. A row only becomes a <details> when it actually
   HAS a description (page-services.php), so the + can never sit on a
   control that does not open.
   The rule colour is ONE knob so the dark blocks flip it instead of
   restating the rules -- the same variable flip the text widget uses. */
.services-block { --services-rule: rgba(28, 27, 31, 0.15); }
[data-theme="dark"].services-block { --services-rule: rgba(255, 255, 255, 0.22); }

.services-block__item {
  border-bottom: 1px solid var(--services-rule);
  padding-block: 3px;   /* 36px line box + 6 = the 42px pitch Figma uses */
}

/* The rows ARE the case accordion (.case-about), so nothing about the open
   animation, the staggered rise or the text sweep is restated here -- only
   the context differs. The summary becomes a full-width row with the icon
   pushed right, at the list own size instead of the accordion Body/Small,
   and the plus turns into a cross because .case-about[open] rotates the
   whole icon 45deg (case-content.css). */
.services-item { border: none; }
.services-item > .case-about__summary {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 0;
  font-size: inherit;     /* the <ul> carries .text-lg -- no size named here */
  line-height: inherit;
  color: inherit;
}
.services-item .case-about__icon { width: 14px; height: 14px; color: inherit; }
.services-item .case-about__icon::before,
.services-item .case-about__icon::after { width: 12px; height: 1px; }

.services-item .case-about__body { padding-bottom: 14px; max-width: 46ch; }

/* The sweep is a background-clip gradient, so on the dark blocks it has to
   be built from white or the text is revealed in near-black on near-black.
   Two variables, no copy of the gradient. */
[data-theme="dark"] .services-item {
  --sweep-ink: var(--text-light);
  --sweep-body: var(--text-light);
}


/* -- Word-cycle band ---------------------------------------------------
   A muted opening line with a phrase under it that swaps as you scroll.
   The band is TALLER than a screen and its content is sticky: the extra
   height IS the scroll distance the swapping is mapped onto, which is why
   it scales with the phrase count (set inline by the template). One phrase
   and the band is a single ordinary screen with nothing to cycle. */
.services-words {
  /* The band's own rhythm, NOT derived from the header. Figma measures 103px
     from the SECTION top; tying it to --header-h only looked right by
     coincidence (86 + 20 = 106) and would drift the moment the header
     changed height. 100 / 60 / 40 like the hero and the Sweet Spot band. */
  --services-words-gap: var(--space-100);
  background: var(--bg-secondary);
  /* Scroll distance per phrase. Was 70svh: a single wheel flick crossed a whole
     phrase (~470px of 1440x900) while the next one needs ~1.75s to finish
     revealing, so swaps stacked on top of each other (Kajsa 2026-10-13). */
  /* 80svh per phrase (120 originally, 40 for a day, then 80 - Kajsa 2026-10-15: 40 scrolled too fast for four sentences). The swaps are still paced by TIME in services.js, so this only sets how far the visitor travels per phrase. */
  /* Time-driven since 2026-10-26 (Kajsa, alternative C): the band is pinned for a SHORT
     stretch and services.js plays the phrases on a timer, so this is just how long it holds -
     no longer scaled by the number of phrases. */
  --words-travel: 150svh;
  min-height: calc(100svh + var(--words-travel));
}
/* Figma 6982:5615 measured in its 1440x900 frame: the opening line starts
   at y~103 (high up, just clear of the fixed header) and the cycling
   phrase sits around y~389-545, i.e. through the middle of the band.
   So the two are NOT centred together - the heading is pinned near the
   top and the phrase is centred in the space under it. */
.services-words__inner {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  /* The gap owns the RHYTHM; the header is only a FLOOR. The band is sticky
     at top: 0, so its content sits under the fixed header - at the 40px
     phone gap, 47px of the opening line was hidden behind it (measured).
     max() keeps Figmas 100 on desktop, where the gap is the larger of the
     two, and guarantees clearance below that. Not the same thing as
     deriving the position from the header, which is what this used to do. */
  padding-block: max(var(--services-words-gap), calc(var(--header-h) + 8px)) var(--space-60);
}
.services-words__inner > .container {
  flex: 1;
  display: grid;
  /* ONE row, both children in it: the heading pins to the top and the
     phrase centres on the WHOLE band. Centring it in the space under the
     heading instead put it 49px below where Figma has it. They share a
     cell but never collide - the heading is ~70px tall at the top and two
     lines of phrase span roughly the middle third. */
  grid-template-rows: 1fr;
  grid-template-columns: minmax(0, 1fr);
}
/* grid-AREA, not grid-row. Placing both children in row 1 alone still lets
   auto-placement give them separate COLUMNS - which it did: the container
   resolved to "343px 0px" and the phrase, being absolutely positioned
   inside a 0px-wide stack, wrapped to 42 lines. They have to share one
   cell explicitly. */
.services-words__lead  { grid-area: 1 / 1; align-self: start; }
/* ~40% white over #1C1024 -- measured off the Figma render, not guessed. */
.services-words__lead {
  margin: 0;   /* the grid row owns the gap, not a magic number */
  color: rgba(255, 255, 255, 0.4);   /* ~40% white over #1C1024, measured */
}
/* All phrases occupy the SAME box, so the band never reflows as they swap.
   min-height holds two lines, the longest Figma shows. */
.services-words__stack {
  position: relative;
  /* Two lines at the PHRASE's own line-height. 2.1em resolved against the
     inherited 14px instead, so the box reserved 29px for 172px of text. */
  min-height: calc(var(--title-lg-lh) * 2);
  grid-area: 1 / 1;
  align-self: center;  /* centred on the whole band, as Figma has it */
}
.services-words__phrase {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  margin: 0;
  max-width: 85%;   /* Figma wraps this to TWO lines; measured 1030 of 1220 */
  color: var(--text-light);
  opacity: 0;
  /* Leaves slower than before (0.3s) so the outgoing phrase is gone before the
     incoming one starts, instead of the two overlapping. */
  transition: opacity 0.6s ease;
}

/* The incoming phrase waits for the outgoing one to clear: the same per-letter
   delay as the shared reveal in case-content.css, plus 0.3s. */
.services-words .services-words__phrase.is-lit .rv__c {
  transition: opacity 1.2s calc(var(--d) + 0.3s), transform 0.5s calc(var(--d) + 0.3s);
}
/* Without JS the first phrase is the one the template marked current, so
   the band still reads as a finished sentence. */
.services-words__phrase.is-current { opacity: 1; }

@media (max-width: 1024px) {
  .services-words { --services-words-gap: 60px; }
  .services-words__phrase { max-width: 100%; }
}
@media (max-width: 767px) {
  .services-words { --services-words-gap: 40px; }
}

/* -- Sweet Spot band ---------------------------------------------------
   Same 4-column grid as the service blocks, so the button and the caption
   land under the intro column. The collage rows sit OUTSIDE the text
   container (they bring their own, or go full-bleed). */
/* .section gives this band --section-py (120/80/60) top and bottom. Figma
   asks for 100 on top, so the band's OWN gap variable owns that edge; the
   bottom is left on the section rhythm. Equal specificity to .section, so it
   wins on source order - this file is enqueued after tokens.css. */
.services-sweet {
  --services-sweet-gap: var(--space-100);
  padding-top: var(--services-sweet-gap);
}
.services-sweet__text,
.services-sweet__caption {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 20px;
  row-gap: 40px;
}
.services-sweet__text > .text-grid { grid-column: 1 / -1; --tw-pad-bottom: 0; }
.services-sweet__cta { grid-column: 3 / span 2; }
.services-sweet__caption p { grid-column: 3 / span 2; margin: 0; }
/* Overrides .case-media base 60/60 (case-content.css) -- equal specificity,
   wins on source order because this file is enqueued after it. */
.services-sweet__media { padding-block: var(--services-sweet-gap) 40px; }
/* Until the media builder has been filled in there is nothing between the
   button and the caption, so they would touch. Only applies in that state. */
.services-sweet__text + .services-sweet__caption { margin-top: 40px; }

@media (max-width: 1024px) { .services-sweet { --services-sweet-gap: 60px; } }
@media (max-width: 767px) {
  .services-sweet { --services-sweet-gap: 40px; }
  .services-sweet__cta,
  .services-sweet__caption p { grid-column: 1 / -1; }
}
