/* 02 services (2026-10-18, NOTES-services-section.md). Replaces the 02 logo strip (02-logos.css stays on disk, unreferenced).
   Reference: octopush.agency "Our services" (four numbered cards, illustration + name, cards assemble on scroll).
   Owner round 2: no visible heading (the cards carry the section; an sr-only h2 names it), a scroll-driven horizontal row on phone
   like why-us and the offer, and every illustration animates by itself in a calm loop.
   Desktop: four in a row, dealt into place by page scroll. 769-1279: 2 x 2. 541-768: one column of wide rows.
   Phone (<= 540, motion allowed): .sv--pin, a sticky 100svh viewport whose row is moved by page scroll (02-services.js).
   Phone reduced motion / no JS: the cards stack as rows. */
[data-section="02-logos"] { --sv-card: #f6f3ed; --sv-ink: var(--night, #0a0d12); --sv-body: #535862; --sv-num: #9a9ea6; --sv-coral: var(--orange, #f26b6c);
  display: block; padding: 32px 32px 88px;   /* round 6: the tail of the coral section, 32 px under it */ background: #fff; overflow: clip; }
[data-section="02-logos"] .sv { max-width: 1240px; margin: 0 auto; }

/* cards */
[data-section="02-logos"] .sv__grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; }
[data-section="02-logos"] .sv__card { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: "num" "art" "name" "desc";
  grid-template-rows: auto 1fr auto auto; min-height: 420px; padding: 28px; border-radius: 24px; background: var(--sv-card); }
[data-section="02-logos"] .sv__card::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1.5px var(--sv-coral); opacity: 0; transition: opacity .35s ease; pointer-events: none; }
@media (hover: hover) and (pointer: fine) { [data-section="02-logos"] .sv__card:hover::after { opacity: 1; } }
[data-section="02-logos"] .sv__top { display: contents; }
[data-section="02-logos"] .sv__num { grid-area: num; font: 600 13px/18px Inter, var(--f-ui); color: var(--sv-num); font-variant-numeric: tabular-nums; letter-spacing: .04em; }
[data-section="02-logos"] .sv__art { grid-area: art; display: flex; align-items: center; justify-content: center; padding: 20px 0 28px; }
[data-section="02-logos"] .sv__art svg { display: block; width: 168px; height: auto; overflow: visible; }
[data-section="02-logos"] .sv__name { grid-area: name; margin: 0 0 10px; font: 700 28px/36px var(--f-display); color: var(--sv-ink); letter-spacing: 0; white-space: nowrap; }
[data-section="02-logos"] .sv__desc { grid-area: desc; margin: 0; font: 400 17px/28px var(--f-ui); color: var(--sv-body); min-height: 84px; }
/* line balance: every sentence is two clauses and breaks at its comma */
[data-section="02-logos"] .sv__desc .sv__cl { display: block; text-wrap: balance; }

/* ---------- illustration loops (owner: "automatically animate each service illustration in a loop") ----------
   Transform + opacity on SVG groups only, 5-8 s periods, each card on its own period and phase so they never pulse together.
   They run under html.full-motion and only while the section is within one screen of the viewport (.sv--run, set by the JS
   observer); otherwise paused. Reduced motion: no animation, the still frame below is the drawing at rest. */
[data-section="02-logos"] .sv__art svg *, .pq__art svg * { transform-box: fill-box; }
:is([data-section="02-logos"], .pq__art) .sv-dot, :is([data-section="02-logos"], .pq__art) .sv-ring { transform-origin: center; }
:is([data-section="02-logos"], .pq__art) .sv-flag { transform-origin: 100% 0; }
:is([data-section="02-logos"], .pq__art) .sv-sheet, :is([data-section="02-logos"], .pq__art) .sv-phone, :is([data-section="02-logos"], .pq__art) .sv-bar { transform-origin: 50% 100%; }
:is([data-section="02-logos"], .pq__art) .sv-spark { transform-origin: center; }
/* still frame */
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-ring { opacity: .35; }
:is([data-section="02-logos"], .pq__art) .sv-i2 .sv-back { transform: translate(-3px, 0) rotate(-5deg); }
:is([data-section="02-logos"], .pq__art) .sv-i2 .sv-mid { transform: rotate(-2deg); }

html.full-motion :is([data-section="02-logos"], .pq__art) .sv-m { animation: var(--sv-a) var(--sv-t) cubic-bezier(.45, 0, .25, 1) var(--sv-dl, 0s) infinite both; animation-play-state: paused; }
html.full-motion [data-section="02-logos"].sv--run .sv-m { animation-play-state: running; }
html.full-motion .pq.is-open .pq__art .sv-m { animation-play-state: running; }   /* the same loops, in miniature, on the sheet's service cards */
/* each drawing on its own small layer, so a loop frame repaints 200 px of SVG, not the whole card row */
html.full-motion [data-section="02-logos"] .sv__art svg { will-change: transform; }
/* 1 plan, 6 s: the stops lift one after another toward the goal, the goal ring sends out a pulse, the flag waves */
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-m { --sv-t: 6s; }
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-dot { --sv-a: svHop; }
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-dot.sv-d1 { --sv-dl: .35s; }
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-dot.sv-d2 { --sv-dl: .7s; }
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-goal { --sv-dl: 1.05s; }
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-ring { --sv-a: svPulse; --sv-dl: 1.35s; }
:is([data-section="02-logos"], .pq__art) .sv-i1 .sv-flag { --sv-a: svWave; --sv-t: 3s; }
@keyframes svHop { 0%, 22%, 100% { transform: translateY(0); } 9% { transform: translateY(-7px); } }
@keyframes svPulse { 0% { transform: scale(.6); opacity: .7; } 30%, 100% { transform: scale(1.45); opacity: 0; } }
@keyframes svWave { 0%, 100% { transform: scaleX(1); } 50% { transform: scaleX(.78); } }
/* 2 design, 7 s: the three sheets fan open, hold, and close */
:is([data-section="02-logos"], .pq__art) .sv-i2 .sv-m { --sv-t: 7s; --sv-dl: -1.6s; }
:is([data-section="02-logos"], .pq__art) .sv-i2 .sv-back { --sv-a: svFanB; }
:is([data-section="02-logos"], .pq__art) .sv-i2 .sv-mid { --sv-a: svFanM; }
:is([data-section="02-logos"], .pq__art) .sv-i2 .sv-front { --sv-a: svFanF; }
@keyframes svFanB { 0%, 18%, 100% { transform: translate(-3px, 0) rotate(-5deg); } 40%, 70% { transform: translate(-9px, 2px) rotate(-12deg); } }
@keyframes svFanM { 0%, 18%, 100% { transform: rotate(-2deg); } 40%, 70% { transform: translate(-4px, 1px) rotate(-5deg); } }
@keyframes svFanF { 0%, 18%, 100% { transform: none; } 40%, 70% { transform: translate(6px, -5px) rotate(3deg); } }
/* 3 apps + web, 6.5 s: the phone steps out of the browser and back; the browser answers with a small shift */
:is([data-section="02-logos"], .pq__art) .sv-i3 .sv-m { --sv-t: 6.5s; --sv-dl: -3.2s; }
:is([data-section="02-logos"], .pq__art) .sv-i3 .sv-browser { --sv-a: svBrowser; }
:is([data-section="02-logos"], .pq__art) .sv-i3 .sv-phone { --sv-a: svPhone; }
:is([data-section="02-logos"], .pq__art) .sv-i3 .sv-blink { --sv-a: svBlink; --sv-t: 3.25s; }
@keyframes svBrowser { 0%, 20%, 100% { transform: none; } 42%, 68% { transform: translate(4px, 3px); } }
@keyframes svPhone { 0%, 20%, 100% { transform: none; } 42%, 68% { transform: translate(-6px, -10px) rotate(-6deg); } }
@keyframes svBlink { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
/* 4 campaigns, 5 s: the bars grow one after another, a spark lands on the top bar */
:is([data-section="02-logos"], .pq__art) .sv-i4 .sv-m { --sv-t: 5s; --sv-dl: -.8s; }
:is([data-section="02-logos"], .pq__art) .sv-i4 .sv-bar { --sv-a: svGrow; }
:is([data-section="02-logos"], .pq__art) .sv-i4 .sv-bar.sv-d1 { --sv-dl: -.6s; }
:is([data-section="02-logos"], .pq__art) .sv-i4 .sv-bar.sv-d2 { --sv-dl: -.4s; }
:is([data-section="02-logos"], .pq__art) .sv-i4 .sv-bar.sv-d3 { --sv-dl: -.2s; }
:is([data-section="02-logos"], .pq__art) .sv-i4 .sv-spark { --sv-a: svSpark; --sv-dl: -.2s; }
@keyframes svGrow { 0%, 30%, 100% { transform: scaleY(1); } 45%, 62% { transform: scaleY(1.2); } }
@keyframes svSpark { 0%, 40%, 82%, 100% { transform: scale(.4); opacity: 0; } 52%, 70% { transform: scale(1) rotate(45deg); opacity: 1; } }
html.reduce-motion :is([data-section="02-logos"], .pq__art) .sv-m { animation: none !important; }

/* desktop deal: 02-services.js writes transform/opacity per card while the grid scrolls in; will-change only while it runs */
[data-section="02-logos"].sv--deal .sv__card { will-change: transform, opacity; }

/* small laptop / tablet landscape: 2 x 2 (four columns need 1280: below it the names outgrow the card) */
@media (max-width: 1279px) {
  [data-section="02-logos"] .sv__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  [data-section="02-logos"] .sv__card { min-height: 380px; }
}
/* tablet portrait (541-768): one column of wide rows */
@media (max-width: 768px) and (min-width: 541px) {
  [data-section="02-logos"] { padding: 28px 24px 80px; }
  [data-section="02-logos"] .sv__grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  [data-section="02-logos"] .sv__card { min-height: 0; padding: 24px 28px; column-gap: 16px;
    grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto auto; grid-template-areas: "num art" "name art" "desc desc"; }
  [data-section="02-logos"] .sv__num { align-self: end; }
  [data-section="02-logos"] .sv__name { margin: 4px 0 0; font-size: 24px; line-height: 32px; align-self: start; }
  [data-section="02-logos"] .sv__art { padding: 0; width: 104px; height: 78px; }
  [data-section="02-logos"] .sv__art svg { width: 104px; }
  [data-section="02-logos"] .sv__desc { margin-top: 16px; font-size: 17px; line-height: 28px; min-height: 56px; }
}

/* phone (<= 540), round 3 (owner: "the text container is too small on mobile, too much empty space. also squeeze 2 cards on
   screen on mobile"). Card anatomy: number and a 64 px looping drawing share the top row, then the name across the full
   width, then the sentence; the card is exactly as tall as its content, so there is no empty band. Name 24/32, sentence
   17/28 (16.4 at 360 phones and up, the fluid floor) in a fixed 15.5em measure, each clause one line on every phone.
   Fallback (reduced motion, no JS): the four cards stack on the 16 px edge line. */
@media (max-width: 540px) {
  [data-section="02-logos"] { padding: 24px 16px 56px; }
  [data-section="02-logos"] .sv__grid { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  [data-section="02-logos"] .sv__card { min-height: 0; padding: 20px; border-radius: 20px; column-gap: 12px;
    grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto auto; grid-template-areas: "num art" "name name" "desc desc"; }
  [data-section="02-logos"] .sv__num { font-size: 13px; line-height: 18px; align-self: center; }
  [data-section="02-logos"] .sv__art { padding: 0; align-self: center; width: 64px; height: 48px; }
  [data-section="02-logos"] .sv__art svg { width: 64px; }
  [data-section="02-logos"] .sv__name { margin: 10px 0 0; font-size: 24px; line-height: 32px; }
  [data-section="02-logos"] .sv__desc { margin-top: 6px; font-size: min(17px, calc((var(--sv-cw, calc(100vw - 32px)) - 40px) / 15.5));
    line-height: 1.65; max-inline-size: 15.5em; min-height: 3.3em; }

  /* scroll-driven row (owner: "scroll horizontally like the cards and packages"), the offer section's mechanism with the
     cards in pairs: cards 1 + 2 stacked in the first column, 3 + 4 in the second, so two cards are on screen at every rest.
     Column width min(440px, 100vw - 64px), 16 px column gap, 12 px between the two cards of a pair, 20 px edge inset:
     pair 1 rests with pair 2 peeking 28 px, pair 2 rests with pair 1 peeking 28 px (two rests). The pair is centred between
     the nav pill and a 96 px bottom band kept clear for the dock. */
  /* round 6 (owner: "on mobile the services sit at the bottom of the screen ... almost the footer of the coral section"):
     the card row is bottom-sticky. #logos is a column whose content sits at its end; .sv sticks to the screen bottom above the
     dock band, so the cards rise in right under the coral hero and stay at the bottom while page scroll moves the row; the
     section's empty upper part scrolls past above them. Height and progress are set by 02-services.js. */
  .sv--pin[data-section="02-logos"] { padding: 0; display: flex; flex-direction: column; justify-content: flex-end; }
  .sv--pin[data-section="02-logos"] .sv { position: sticky; bottom: 0; width: 100%; max-width: none; box-sizing: border-box;
    padding: 12px 0 var(--sv-bot, 96px); overflow: hidden; overflow: clip; }
  /* round 5 (owner: "one row but show 2 columns" + "I want the illustration to be dominant").
     One row of four cards, two side by side at every rest: card width (100vw - 32 - 12) / 2, 12 px between the cards of a
     pair, 20 px between pairs (card 3 has 8 px extra start margin, so no sliver of the hidden pair shows), 16 px edge inset.
     Card: a full-bleed square drawing stage on top (number small in its corner), then the name on two fixed lines (.sv__nl),
     then the sentence. Type is tied to the card width inside fixed em measures (NOTES-card-subtext-lines.md method), so every
     phone width wraps identically: name (card - 32) / 7, sentence (card - 32) / 7.8 capped at 18 px, 7.8em measure. */
  .sv--pin[data-section="02-logos"] .sv__grid { display: flex; gap: 12px; padding-inline: 16px; align-items: stretch; width: max-content; will-change: transform; }
  .sv--pin[data-section="02-logos"] .sv__card { flex: 0 0 var(--sv-cw, calc((100vw - 44px) / 2)); padding: 0 0 16px; border-radius: 18px; overflow: hidden;
    grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto 1fr; grid-template-areas: "art" "name" "desc"; }
  .sv--pin[data-section="02-logos"] .sv__card:nth-child(3) { margin-inline-start: 8px; }
  .sv--pin[data-section="02-logos"] .sv__num { position: absolute; top: 10px; inset-inline-start: 12px; z-index: 1; font-size: 12px; line-height: 16px; }
  /* ios-services-fix2: the stage height is a fixed px value (card width / .85) and the drawing sizes from its width only.
     Before, the stage was aspect-ratio + stretch with an svg at height:100% inside it: a percentage height against a
     content-sized box, which iOS WebKit grew without bound (stage taller than the screen, drawing and text pushed off it). */
  .sv--pin[data-section="02-logos"] .sv__art { width: auto; height: calc(var(--sv-cw, calc((100vw - 44px) / 2)) / .85); min-height: 0; max-height: 80vw;
    aspect-ratio: auto; padding: 10px; background: #ede7db; align-self: start; box-sizing: border-box; }
  .sv--pin[data-section="02-logos"] .sv__art svg { width: 100%; height: auto; }
  .sv--pin[data-section="02-logos"] .sv__name { margin: 12px 16px 0; font-size: min(20px, calc((var(--sv-cw, 158px) - 32px) / 7)); line-height: 1.3; white-space: nowrap; }
  .sv--pin[data-section="02-logos"] .sv__nl { display: block; }
  .sv--pin[data-section="02-logos"] .sv__desc { margin: 6px 16px 0; font-size: min(18px, calc((var(--sv-cw, 158px) - 32px) / 7.8)); line-height: 1.6;
    max-inline-size: 7.8em; min-height: 0; }
}

/* round 5, desktop and laptop (769 px and up): the drawing stage is the largest part of the card. Full-bleed 4:3 stage in
   cream on top (16:9 in the wide 2 x 2 cards), number in its corner, then name and sentence. Card width, type and line
   counts are unchanged. */
@media (min-width: 769px) {
  [data-section="02-logos"] .sv__card { min-height: 0; padding: 0 0 28px; overflow: hidden; grid-template-rows: auto auto auto; grid-template-areas: "art" "name" "desc"; }
  [data-section="02-logos"] .sv__num { position: absolute; top: 18px; inset-inline-start: 22px; z-index: 1; }
  [data-section="02-logos"] .sv__art { aspect-ratio: 4 / 3; padding: 12px; margin-bottom: 24px; background: #ede7db; box-sizing: border-box; }
  [data-section="02-logos"] .sv__art svg { width: 100%; height: 100%; }
  [data-section="02-logos"] .sv__name, [data-section="02-logos"] .sv__desc { margin-inline: 28px; }
}
@media (min-width: 769px) and (max-width: 1279px) { [data-section="02-logos"] .sv__art { aspect-ratio: 16 / 9; } }

/* ---------- services select (2026-10-19, NOTES-services-select.md) ----------
   Owner: "make the services a way to select what you want multi select on the page but without checkbox just border selected
   and button appears below to cta which skips the first step on the form". Each card is a toggle (role=button, aria-pressed).
   Selected = a 2 px coral ring, a coral tint on the card and its stage, and a 4 px lift; no checkbox, no tick. The lift and
   the press use the separate translate / scale properties, so they never fight the desktop deal's inline transform.
   The same selected state (ring, tint, stage tint) is used by the sheet's cards (09-prequal.css): one component family.
   The CTA sits in space the section already had (desktop: the bottom padding; phone pin: the 96 px dock band), so showing it
   moves nothing and the scroll pin keeps its numbers. */
[data-section="02-logos"] { --sv-sel: #fdeeea; --sv-sel-stage: #f9e0d8; --sv-spring: cubic-bezier(.34, 1.56, .64, 1); }
[data-section="02-logos"] .sv { position: relative; }
[data-section="02-logos"] .sv__card { cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation; -webkit-user-select: none; user-select: none;
  transition: translate .5s var(--sv-spring), scale .45s var(--sv-spring), background-color .25s ease; }
[data-section="02-logos"] .sv__art { transition: background-color .25s ease; }
[data-section="02-logos"] .sv__card:active { scale: .97; transition-duration: .12s, .12s, .25s; }
[data-section="02-logos"] .sv__card.is-sel { background: var(--sv-sel); translate: 0 -4px; }
[data-section="02-logos"] .sv__card.is-sel .sv__art { background: var(--sv-sel-stage); }
[data-section="02-logos"] .sv__card.is-sel::after { opacity: 1; box-shadow: inset 0 0 0 2px var(--sv-coral); }
[data-section="02-logos"] .sv__card.is-sel .sv__num { color: var(--sv-coral); }
[data-section="02-logos"] .sv__card:focus { outline: none; }
[data-section="02-logos"] .sv__card:focus-visible { outline: 2px solid var(--sv-ink); outline-offset: 3px; }
html.reduce-motion [data-section="02-logos"] .sv__card { transition: none; translate: none; scale: none; }

/* the CTA and the quiet line share one slot under the row: the line shows until something is picked, then the CTA takes over */
[data-section="02-logos"] .sv__hint, [data-section="02-logos"] .sv__cta { position: absolute; top: 100%; inset-inline: 0; margin: 20px auto 0; width: max-content; height: 56px; }
[data-section="02-logos"] .sv__hint { display: flex; align-items: center; font: 400 15px/24px var(--f-ui); color: var(--sv-num); transition: opacity .3s ease; pointer-events: none; }
[data-section="02-logos"].sv--sel .sv__hint { opacity: 0; }
[data-section="02-logos"] .sv__cta { display: flex; align-items: center; justify-content: center; gap: 0; min-width: 260px; padding: 0 32px; box-sizing: border-box; border-radius: 999px;
  background: var(--sv-coral); color: #fff; font: 700 17px/1 var(--f-ui); text-decoration: none; white-space: nowrap; font-variant-numeric: tabular-nums;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  opacity: 0; transform: translateY(12px); visibility: hidden; pointer-events: none;
  transition: opacity .14s ease, transform .14s ease, visibility 0s linear .14s, background-color .25s ease, scale .4s var(--sv-spring); }
[data-section="02-logos"].sv--sel .sv__cta { opacity: 1; transform: none; visibility: visible; pointer-events: auto;
  transition: opacity .3s var(--e-out) .2s, transform .45s var(--e-out) .2s, visibility 0s linear 0s, background-color .25s ease, scale .4s var(--sv-spring); }
[data-section="02-logos"] .sv__cta-n { unicode-bidi: isolate; }
@media (hover: hover) and (pointer: fine) { [data-section="02-logos"] .sv__cta:hover { background: var(--sv-ink); } }
[data-section="02-logos"] .sv__cta:active { scale: .97; }
[data-section="02-logos"] .sv__cta:focus-visible { outline: 2px solid var(--sv-ink); outline-offset: 3px; }
html.reduce-motion [data-section="02-logos"] .sv__cta, html.reduce-motion [data-section="02-logos"] .sv__hint { transition: none; transform: none; scale: none; }
@media (max-width: 768px) and (min-width: 541px) { [data-section="02-logos"] .sv__hint, [data-section="02-logos"] .sv__cta { margin-top: 12px; } }

/* phone, not pinned (reduced motion / no JS stacking): the slot is in the flow, reserved even while empty */
@media (max-width: 540px) {
  [data-section="02-logos"] .sv__hint { display: none; }
  [data-section="02-logos"] .sv__cta { position: relative; top: auto; margin: 16px auto 0; width: min(340px, calc(100vw - 40px)); font-size: 18px; }
  /* pinned: the CTA takes the dock's exact place and size in the 96 px band under the visible pair (thumb reach, inside the
     sticky viewport); the dock steps aside while it is there (html.sv-cta-on, below) */
  .sv--pin[data-section="02-logos"] .sv { padding-bottom: calc(var(--sv-bot, 96px) + env(safe-area-inset-bottom, 0px)); }
  .sv--pin[data-section="02-logos"] .sv__cta { position: absolute; top: auto; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); margin: 0 auto; }
}
/* the dock and the services CTA never share the screen: the dock leaves fast, the CTA arrives after it (.2 s delay above) */
@media (max-width: 700px) {
  html.sv-cta-on .wa-dock.is-on { opacity: 0; transform: translateY(24px); visibility: hidden; pointer-events: none;
    transition: opacity .16s ease, transform .16s ease, visibility 0s linear .16s; }
}
