/* ═══════════════════════════════════════════════════════════════════════════
   rzv.studio — services.css  (#services only)

   Loads after work.css, before glass.css. Declares NO tokens and invents NO
   colours: everything below reads base.css's :root, so the section is correct
   in both phases with nothing extra to maintain.

   PREFIX `svc-`, NOT `sv-`. work.css owns `sv-` for the /services/ SUBPAGE
   (.sv-pillar, .sv-head, .sv-name, .sv-body, .sv-desc, .sv-subs, .sv-close,
   .sv-step*, …) and BOTH sheets load on index.html — work.css first, so this
   file only won where it happened to declare the same property. It did not:
   .sv-name inherited the subpage's DISPLAY scale (clamp(1.5rem,3vw,2.5rem)/300
   /-0.04em) instead of the card scale base.css `.t-lg` gives it
   (clamp(1.3rem,2.2vw,1.95rem)/400/-0.03em); .sv-subs inherited
   flex-wrap:wrap, which quietly un-stacked the pills at narrow columns; and
   worst, the modal's 34px round close button inherited .sv-close's
   `flex-direction:column; padding:clamp(60px,8vw,130px) 0 0`. Scoping under
   `#services` was the rejected alternative — it fixes the cascade but leaves
   two unrelated components answering to one name, so the next `.sv-` rule
   written on the subpage is the same bug again. A separate prefix cannot
   collide by construction. Nothing on /services/ was renamed.

   THE SECTION IS COMPOSED BY CSS ALONE. Nothing here starts hidden waiting for
   services.js — that is the house rule. JS adds the pill physics and the
   tile→modal morph; with JS dead the tiles are still six readable cards with
   their sub-services listed.

   Surface reuse: the tile wears base.css `.tilt` for its material (card
   background, cursor sheen, glow ring, and the ≤760px de-blur that keeps it
   off the iOS backdrop-filter × mix-blend-mode trap). .svc-tile below adds
   layout and nothing else. `transform` is deliberately ABSENT from every
   transition list in this file — GSAP owns transform on the pills and on the
   morphing modal (trap 2).
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1 ── THE GRID ───────────────────────────────────────────────────────
   3 → 2 → 1. minmax(0,1fr) rather than 1fr so a long sub-service pill can
   never push a column wider than its share. ── */
.svc-grid{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,1.6vw,24px);
}
@media(max-width:1080px){ .svc-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media(max-width:680px){  .svc-grid{grid-template-columns:1fr} }

/* ── 2 ── THE TILE ───────────────────────────────────────────────────────
   A real <button> (keyboard-operable, announced as a control) reset to a
   block-level card: `text-align:left` and `font:inherit` are the two things a
   UA stylesheet gets wrong on a button used as a surface.
   min-height keeps the six boxes on an even baseline whatever the description
   length; .svc-subs takes `margin-top:auto` so the pills sit on the floor of
   every tile regardless (Haseeb: pills at the bottom). ── */
.svc-tile{
  display:flex;flex-direction:column;align-items:flex-start;
  gap:clamp(16px,1.8vw,22px);
  width:100%;min-height:clamp(250px,25vw,310px);
  padding:clamp(22px,2.2vw,30px);
  text-align:left;font:inherit;color:var(--text);
  cursor:pointer;
}
.svc-ico{display:block;color:var(--muted);transition:color .35s var(--ease)}
.svc-ico svg{width:22px;height:22px;display:block}
.svc-tile:hover .svc-ico,.svc-tile:focus-visible .svc-ico{color:var(--text)}

.svc-body{display:flex;flex-direction:column;gap:9px}
.svc-name{display:block}
.svc-desc{display:block;max-width:34ch}

/* ── 3 ── THE PILLS ──────────────────────────────────────────────────────
   STACKED, one per line — his word. Each is its own inline-flex box so it has
   a real, independent bounding box for the physics to push around, and so it
   never stretches to the tile's width.

   --r-pill (100px) per the house radius scale. `transform` is NOT in the
   transition list: services.js writes x / y / rotation on these every frame
   while the cursor is over the tile (trap 2). ── */
.svc-subs{
  margin-top:auto;
  display:flex;flex-direction:column;align-items:flex-start;
  gap:7px;
}
.svc-pill{
  display:inline-flex;align-items:center;
  padding:6px 14px;border-radius:var(--r-pill);
  border:1px solid var(--border);background:var(--surface);
  font-size:12.5px;font-weight:400;letter-spacing:-0.005em;line-height:1.35;
  color:var(--muted);
  transition:border-color .35s var(--ease),background-color .35s var(--ease),color .35s var(--ease);
}
.svc-tile:hover .svc-pill,.svc-tile:focus-visible .svc-pill{
  border-color:var(--border-hi);background:var(--surface-2);color:var(--text);
}

/* ═══ 4 ── THE MODAL ════════════════════════════════════════════════════
   Built by services.js at first open and appended to <body> — it is behaviour,
   not content: every word it shows already exists in the tile above. With JS
   dead none of this is ever instantiated and nothing is lost.

   Geometry contract with the FLIP (adapted from ~/rzv-dashboard/task-modal.js
   MORPH): at rest the modal is centred with top/left 50% + translate(-50%,-50%);
   while `.morphing` GSAP writes top/left/width/height/borderRadius inline and
   clears them again on land. No `transition` on this element at all — a CSS
   transition on a property GSAP writes per frame smears every frame into the
   next. The no-morph path (motion off / no GSAP) fades the BACKDROP only. ═══ */
.svc-backdrop{
  position:fixed;inset:0;z-index:9000;
  background:color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .4s var(--ease),visibility 0s linear .4s;
}
.svc-backdrop.open{opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s}

.svc-modal{
  position:fixed;z-index:9010;
  top:50%;left:50%;transform:translate(-50%,-50%);
  width:min(560px,calc(100vw - 32px));
  max-height:min(78vh,720px);
  display:none;flex-direction:column;overflow:hidden;
  border-radius:var(--r-modal);
  border:1px solid var(--border-hi);
  background:var(--card-hi);
  backdrop-filter:blur(24px) saturate(1.4);-webkit-backdrop-filter:blur(24px) saturate(1.4);
  box-shadow:inset 0 1px 0 rgba(255,255,255,0.10), 0 30px 80px -40px rgba(0,0,0,0.75);
}
.svc-modal.open{display:flex}
/* GSAP is writing an explicit rect for the duration of the morph, so the
   centring transform and the max-height cap must both stand down. */
.svc-modal.morphing{max-height:none}
.svc-modal.morphing .svc-modal-scroll{overflow:hidden}

.svc-modal-scroll{overflow:auto;-webkit-overflow-scrolling:touch;
  padding:clamp(26px,3vw,38px);display:flex;flex-direction:column;gap:20px}

.svc-close{
  position:absolute;top:14px;right:14px;z-index:2;
  width:34px;height:34px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--border);background:var(--surface);color:var(--muted);
  transition:color .25s var(--ease),border-color .25s var(--ease),background-color .25s var(--ease);
}
.svc-close:hover{color:var(--text);border-color:var(--border-hi);background:var(--surface-2)}
.svc-close svg{width:13px;height:13px}

.svc-modal-head{display:flex;flex-direction:column;gap:12px;padding-right:40px}
.svc-modal-ico{display:block;color:var(--muted)}
.svc-modal-ico svg{width:24px;height:24px;display:block}
.svc-modal-title{font-size:clamp(1.8rem,3.4vw,2.6rem)}
.svc-modal-lede{color:var(--muted)}
.svc-modal-subs{display:flex;flex-wrap:wrap;gap:8px}
.svc-modal-subs .svc-pill{color:var(--text);border-color:var(--border-hi);background:var(--surface-2)}
.svc-modal-covers{display:flex;flex-direction:column;gap:12px}

/* Scroll lock while a modal is open. Named so it cannot collide with
   base.css's body.loading, which is the loader's lock and has its own life.

   THE GUTTER IS PAID BACK IN JS, not here. overflow:hidden retires the
   classic scrollbar, which on desktop Chrome/Firefox widens the layout
   viewport by ~15px: the whole page behind the dialog jumps left, the
   left:50%-centred modal jumps WITH it mid-morph, and the resize propagates
   into intro.js's debounced layout() + ScrollTrigger.refresh() and glass's
   resize path while a dialog is open. So services.js measures
   `innerWidth - documentElement.clientWidth` BEFORE adding this class and
   writes it back as an inline body padding-right, removing both together.
   `scrollbar-gutter:stable` on :root was the rejected alternative — it is the
   tidier fix but it reserves the gutter on every page of the site all the
   time, which is a global layout change to buy one section a modal. */
body.svc-lock{overflow:hidden}

/* ── 5 ── MOBILE (≤760px) ────────────────────────────────────────────────
   Same trap-1 discipline as base.css: no backdrop-filter left anywhere near
   this section on a phone, and the surfaces go genuinely opaque instead. The
   modal also stops pretending to be a centred dialog and becomes a sheet with
   room to breathe. .tilt is already de-blurred by base.css's own ≤760 block. ── */
@media(max-width:760px){
  .svc-tile{min-height:0}
  .svc-backdrop{backdrop-filter:none;-webkit-backdrop-filter:none;
    background:color-mix(in srgb, var(--bg) 88%, transparent)}
  .svc-modal{
    backdrop-filter:none;-webkit-backdrop-filter:none;
    background:var(--bg-2);
    width:calc(100vw - 24px);max-height:86vh;
  }
}
