/* ============================================================
   Home page layer — services masonry link, pillar row, hub grid
   (system styles live in playground.css)
   ============================================================ */

.services-more { margin-top: var(--gap); }

/* mobile: numbered services read 01→06 top-to-bottom */
@media (max-width: 899px) {
  .masonry .masonry-col { display: contents; }
  .masonry .card--purple { order: 1; }
  .masonry .card--amber { order: 2; }
  .masonry .card--green { order: 3; }
  .masonry .card--blue { order: 4; }
  .masonry .card--white { order: 5; }
  .masonry .card--orange { order: 6; }
}

/* AR mode: card content is English — tag pill stays on the text's side */
html[dir="rtl"] .masonry .block-card,
html[dir="rtl"] .pillar-row .block-card,
html[dir="rtl"] .hub-card { direction: ltr; }

/* why-us: four pillars in one row */
.pillar-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
  align-items: stretch;
}
.pillar-row .block-card { display: flex; flex-direction: column; align-items: flex-start; }
@media (max-width: 1099px) { .pillar-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px) { .pillar-row { grid-template-columns: 1fr; } }

/* (hub-grid/hub-card moved to playground.css — used on multiple pages) */

/* ---- trusted by: continuous logo marquee (left → right, edge fades) ---- */
.clients-strip {
  overflow: hidden;
  /* extra room at the BOTTOM: the country pill hangs below the tiles now, and
     the strip's own clip must never cut it to an orange sliver — that exact
     sliver is what the owner screenshotted */
  padding-block: 6px 10px;
  mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
}
.clients-track {
  display: flex; width: max-content;
  position: relative; /* anchors the route line (::before) to the full track */
  animation: clients-scroll var(--dur, 36s) linear infinite;
}
.clients-strip:hover .clients-track,
.clients-strip:focus-within .clients-track { animation-play-state: paused; }
/* the strip carries tabindex="0" so :focus-within is reachable — without it
   a keyboard-only visitor had no way at all to stop the scroll (WCAG 2.2.2),
   because nothing inside the ribbon can take focus */
.clients-strip:focus-visible { outline: 2px solid var(--orange, #ff6b00); outline-offset: 4px; }
@keyframes clients-scroll {
  from { transform: translateX(-50%); }
  to { transform: translateX(0); }
}
.clients-seq { display: flex; align-items: center; flex-shrink: 0; }
/* client marks arrive with mixed backgrounds (some white, some black), so each
   sits in an identical white tile — uniform size, one hairline, one radius.
   The CELL is a transparent column: tile on top, country pill BELOW it —
   the owner wants the country outside the logo box, plainly visible on the
   dark page background, not tucked inside the tile. */
.client-cell {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  width: 190px; flex-shrink: 0;
  margin-inline-end: var(--gap);
}
.client-tile {
  display: flex; align-items: center; justify-content: center;
  width: 190px; height: 104px;
  padding: 16px;
  border-radius: 16px;
  background: #fff;
  border: 1px solid var(--line);
  overflow: hidden;
  transition: border-color 0.25s, box-shadow 0.25s, transform 0.25s;
}
.client-tile:hover {
  border-color: rgba(255, 107, 0, 0.5);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  transform: translateY(-2px);
}
/* the flight route — variant E from the picture menu, owner-picked. One
   dashed path spans the whole track; every tagged client is a stop on it:
   an orange pin ringed so it punches out of the line, country beneath.
   The line lives on the TRACK (width: max-content) so it spans the full
   marquee in both the desktop animation and the mobile swipe mode — the
   strip's edge mask still fades it out at both ends. */
.clients-track.has-route::before {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 26px;
  border-top: 2px dashed rgba(241, 239, 232, 0.22);
  pointer-events: none;
}
/* every cell carries this container — empty when untagged — so all cells
   share one height and the route crosses every pin centre */
.client-stop {
  height: 34px;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.client-pin {
  width: 9px; height: 9px; border-radius: 50%;
  box-sizing: content-box;
  background: var(--orange, #ff6b00);
  border: 2px solid var(--noir-ground, #0c0c0c);
  box-shadow: 0 0 0 2px rgba(241, 239, 232, 0.28);
  animation: pinPulse 3.6s ease-in-out infinite;
}
.client-place {
  color: var(--orange, #ff6b00);
  font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  white-space: nowrap;
  line-height: 1.4;
}
@keyframes pinPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.35); }
}
html.motion-off .client-pin { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .client-pin { animation: none; }
}
/* motion-off wraps the strip into a grid — a single route line through a
   multi-row grid is nonsense, so it goes */
html.motion-off .clients-track::before { content: none; }
/* The plane that flies the route. The span is as wide as the track, carrying
   TWO glyphs one sequence (50% of the track) apart, and travels exactly one
   sequence per loop — so the marquee's own wrap, which snaps the track back
   by one sequence, lands the trailing plane precisely where the leading one
   was. One plane teleported across the strip on every wrap. Translating the
   span (never `left`) keeps the whole flight on the compositor.
   Centred on the line: line centre 27px, plane 16px. */
.route-flight {
  /* 22px plane centred on the line: line centre is 27px up from the track
     foot, so the box sits at 27 − 11 */
  position: absolute; left: 0; bottom: 16px; width: 100%; height: 22px;
  pointer-events: none;
  animation: routeFly var(--fly, 40s) linear infinite;
}
/* social-proof.js sets each plane's `left` and the --hop it travels: enough
   planes, evenly spaced, that one is always on the visible strip */
.route-plane { position: absolute; top: 0; left: 0; width: 22px; height: 22px; }
.route-plane svg {
  display: block; width: 22px; height: 22px;
  fill: var(--orange, #ff6b00);
  transform: rotate(90deg); /* the glyph points up; this flight goes right */
}
/* the vapour trail — a short fade laid along the dashes behind the plane.
   This is what makes a 22px icon read as a flight instead of a dot; it is
   the trail, not brightness, doing the work (no glow anywhere). */
.route-plane::before {
  content: '';
  position: absolute; top: 50%; right: 100%;
  width: 54px; height: 2px; margin-top: -1px;
  background: linear-gradient(to left, rgba(255, 107, 0, 0.65), rgba(255, 107, 0, 0));
}
@keyframes routeFly {
  from { transform: translateX(0); }
  to   { transform: translateX(var(--hop, 50%)); }
}
/* the strip's hover/focus pause must ground the plane too — moving content
   the visitor cannot stop is the exact WCAG trap the swipe mode fixed */
.clients-strip:hover .route-flight,
.clients-strip:focus-within .route-flight { animation-play-state: paused; }
html.motion-off .route-flight { display: none; }
@media (prefers-reduced-motion: reduce) {
  .route-flight { display: none; }
}
.client-tile img {
  max-width: 100%; max-height: 72px; width: auto; height: auto;
  object-fit: contain; border-radius: 8px;
  /* client marks stay in full brand colour — the uniform white tile is what
     holds the row together, not desaturation */
  transition: transform 0.28s ease;
}
.client-cell:hover img { transform: scale(1.04); }
/* reduced motion: a calm wrapped grid, no duplicate set */
html.motion-off .clients-track { animation: none; width: auto; flex-wrap: wrap; gap: var(--gap); }
html.motion-off .clients-seq { flex-wrap: wrap; gap: var(--gap); }
html.motion-off .clients-seq[aria-hidden="true"] { display: none; }
/* the ribbon's filler copies are for the scroll only — a static grid must
   show each client once */
html.motion-off .client-cell--dup { display: none; }
@media (max-width: 599px) {
  .client-cell { width: 148px; }
  .client-tile { width: 148px; height: 86px; padding: 12px; }
}

/* ---- what clients say: the thread ----
   These words arrived on WhatsApp, so they are shown as WhatsApp — incoming
   messages from the client, not quote cards. Built mobile-first, because a
   chat is a phone shape and that is where most of this traffic reads it. */
.testimonials-grid { max-width: 640px; }
.chat-thread { list-style: none; margin: 0; padding: 0; }
.chat-msg {
  display: flex; align-items: flex-end; gap: 10px;
  margin-bottom: 16px;
}
.chat-msg:last-child { margin-bottom: 0; }

.chat-av {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%;
  background: #fff; overflow: hidden;
  display: grid; place-items: center; padding: 5px;
}
.chat-av img { max-width: 100%; max-height: 100%; object-fit: contain; }
.chat-av--text {
  background: rgba(255, 107, 0, 0.16); color: var(--orange, #ff6b00);
  padding: 0; font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em;
}

/* the bubble: one squared corner at the bottom-left, the way an incoming
   message sits against its avatar */
.chat-bub {
  position: relative; min-width: 0;
  max-width: min(520px, 86%);
  padding: 12px 14px 8px;
  border-radius: 16px 16px 16px 4px;
  background: var(--noir-panel, #151513);
  border: 1px solid var(--line);
}
.chat-from {
  display: block; margin-bottom: 4px;
  font-size: 0.76rem; font-weight: 700; letter-spacing: 0.01em;
  color: var(--orange, #ff6b00);
}
.chat-text {
  margin: 0; font-size: 0.95rem; line-height: 1.6;
  /* a pasted quote can carry a long unbroken string; it must never push the
     page sideways on a phone */
  overflow-wrap: anywhere;
}
.chat-meta { display: flex; justify-content: flex-end; margin-top: 4px; }

/* ---- the message being written ----
   Three dots while it is "thinking", then the words arrive. social-proof.js
   pins the bubble's final height before the first character, so the row never
   grows as it types — on a wide screen the three bubbles are one matched-height
   band, and any one of them stretching would drag the other two. */
.chat-dots { display: none; gap: 4px; align-items: center; height: 1.6em; }
.chat-msg.is-typing .chat-dots { display: inline-flex; }
.chat-msg.is-writing .chat-dots { display: none; }
.chat-dots i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--muted);
  animation: chatDot 1.25s ease-in-out infinite;
}
.chat-dots i:nth-child(2) { animation-delay: 0.18s; }
.chat-dots i:nth-child(3) { animation-delay: 0.36s; }
@keyframes chatDot {
  0%, 60%, 100% { opacity: 0.35; transform: translateY(0); }
  30%           { opacity: 1;    transform: translateY(-3px); }
}
/* the cursor, only while words are actually landing */
.chat-msg.is-writing .chat-text::after {
  content: '';
  display: inline-block; width: 2px; height: 1.05em;
  margin-inline-start: 2px; vertical-align: -0.16em;
  background: var(--orange, #ff6b00);
  animation: chatCaret 0.9s steps(1) infinite;
}
@keyframes chatCaret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* the read receipt — one grey tick, then two blue */
.chat-meta svg { width: 20px; height: 12px; overflow: visible; }
.chat-meta path {
  fill: none; stroke: var(--muted); stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
  transition: stroke 0.5s ease, opacity 0.4s ease, transform 0.4s ease;
}
/* the second tick waits behind the first until the message is read */
.chat-meta .tick-b { opacity: 0; transform: translateX(-7px); }
.chat-msg.is-read .chat-meta .tick-b { opacity: 1; transform: none; }
.chat-msg.is-read .chat-meta path { stroke: #4bb3ff; }

.chat-note {
  margin: 18px 0 0; padding-inline-start: 46px;
  font-size: 0.78rem; color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  .chat-meta path { transition: none; }
}

@media (min-width: 700px) {
  .chat-msg { gap: 12px; margin-bottom: 18px; }
  .chat-av { width: 42px; height: 42px; }
  .chat-av--text { font-size: 0.78rem; }
  .chat-bub { padding: 14px 16px 9px; }
  .chat-text { font-size: 1rem; }
  .chat-note { padding-inline-start: 54px; }
}

/* Wide screens read it side by side — a stacked thread in a 1200px column
   wastes the right half of the page. The message keeps its shape: avatar at
   the foot, bubble squared into it, ticks bottom-right. Bubbles stretch to a
   common height so the row reads as one band rather than three ragged boxes;
   the phone keeps the true vertical thread. */
@media (min-width: 1000px) {
  .testimonials-grid { max-width: none; }
  .chat-thread {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--gap);
    align-items: stretch;
  }
  .chat-msg { margin-bottom: 0; }
  .chat-bub {
    max-width: none; width: 100%;
    align-self: stretch;
    display: flex; flex-direction: column;
  }
  /* the ticks sit at the foot of the tallest bubble, not under the shortest
     quote */
  .chat-bub .chat-meta { margin-top: auto; padding-top: 8px; }
  .chat-note { padding-inline-start: 0; text-align: center; }
}

/* the marquee track is intentionally wider than the viewport; make sure the
   strip clips it on the narrowest phones so the page never scrolls sideways */
.clients-strip { overflow: hidden; max-width: 100%; }
#clients { overflow: hidden; }


/* THE DEAL deck removed — the hero is now the pinned conveyor (end of file) */

/* ============================================================
   presentation pass — verified findings from the homepage audit
   ============================================================ */

/* Touch has neither :hover nor :focus-within, so the client marquee's 32s
   loop can never be stopped, examined or steered — WCAG 2.2.2, Level A.
   Once it only moves on a swipe it is no longer auto-moving content.
   (main.js carries a 2.2.2 pause handler for .marquee/.marquee-toggle, but
   that markup exists in zero HTML files — it was never built.) */
@media (hover: none) {
  .clients-strip {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    /* proximity, not mandatory — tiles are 148px here and mandatory
       snapping makes a fast flick feel stuck */
    scroll-snap-type: x proximity;
    mask-image: none; -webkit-mask-image: none;
  }
  .clients-track { animation: none; width: max-content; }
  .client-cell { scroll-snap-align: start; }
  .clients-seq[aria-hidden="true"] { display: none; }
  /* The flight keeps looping here too — the owner asked for the continuous
     animation on the phone, which is where most of his visitors are. WCAG
     2.2.2 is satisfied by the strip's tabindex: a tap focuses it and the
     :focus-within rule above stops the ribbon and the flight together, and
     prefers-reduced-motion removes the flight outright. The plane spacing is
     recomputed against the track's real width, which is ONE sequence here
     because the duplicate is hidden — assuming two put six planes 108px
     apart on a phone. */
}

/* Mobile: the services masonry runs 3,225px at 375px — 31% of the whole
   page, immediately after the hero. The blueprint frame is 44% of each card
   while the copy that sells is 36%, so the frame hugs its drawing instead of
   stretching card-wide. .bp-art is already margin-inline:auto, so the
   narrower frame reads deliberate rather than truncated. */
@media (max-width: 599px) {
  .masonry .blueprint--sm { max-width: 212px; padding: 8px 10px 4px; margin-bottom: 12px; }
  .masonry .block-card .card-tag { margin-bottom: 14px; }
}

/* "Why clients come back" is the flattest band on the page: no blueprint, no
   hue glow, and .card--white is the only hue with no paired .card-tag rule,
   so its numerals fell back to an outline 3.5x fainter than every other card.
   An ember rule gives the band the same "these are the studio's terms" signal
   the trust bar uses. Scoped to .pillar-row, so service card 05, the projects
   grid and the quote tool's card--white tiles are untouched. */
.pillar-row .block-card::before {
  content: ""; display: block;
  width: 22px; height: 2px; border-radius: 2px;
  background: var(--ember); margin-bottom: 14px;
}
.pillar-row .card-tag {
  color: var(--amber);
  border-color: rgba(255, 178, 30, 0.5);
  margin-bottom: 18px;
}
/* a quarter column cannot hold 15.2px body copy at 22 characters a line —
   step the type down with it. min-height on the h3 is load-bearing: it keeps
   all four paragraph tops aligned instead of splitting into two groups when
   a title wraps at ~1200px. */
.pillar-row .block-card { padding: clamp(20px, 2vw, 26px); }
.pillar-row .block-card h3 { font-size: clamp(1.1rem, 1.45vw, 1.35rem); min-height: 2.4em; }
.pillar-row .block-card p { font-size: 0.9rem; line-height: 1.6; }

/* ============================================================
   THE PAPER HERO — a white sheet on the dark canvas.
   Composition: the Monument (typography is the design). Motion:
   the interactive 3D field (hero-3d.js) floating over the sheet —
   the scroll dive was retired with it. The sheet itself gets one
   quiet entrance rise and then holds still.
   ============================================================ */
.paper-hero { position: relative; }
.ph-stage {
  min-height: calc(100dvh - 90px);
  display: grid; place-items: center; position: relative;
  padding: clamp(18px, 3vh, 40px) 0;
}

/* the sheet: local light-canvas tokens so nothing dark-side leaks in */
.ph-sheet {
  --ph-ink: #17120c;
  --ph-muted: #6d6457;
  --ph-line: rgba(23, 18, 12, 0.14);
  width: 100%;
  box-sizing: border-box;
  background: #faf8f4;
  border-radius: var(--radius);
  padding: clamp(34px, 6vh, 76px) clamp(26px, 5vw, 84px);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
  color: var(--ph-ink);
}
.ph-kicker {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--ph-muted);
}
.ph-kicker::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--ember);
}
.ph-title {
  font-family: var(--font-display);
  font-size: clamp(2.9rem, 7.6vw, 6.4rem);
  line-height: 0.98; letter-spacing: -0.035em; font-weight: 800;
  color: var(--ph-ink); margin-top: 26px;
}
.ph-line { display: block; }
.ph-accent { font-style: normal; color: var(--orange); }
.ph-sub {
  margin-top: 24px; color: var(--ph-muted);
  font-size: 1.05rem; line-height: 1.7; max-width: 56ch; font-weight: 480;
}
/* the typed rotator inherits dark-canvas colours — re-ink it for paper */
.ph-sheet .ph-type { margin-top: 14px; color: var(--ph-ink); }
.ph-sheet .ph-type .type-word { color: var(--orange); }
.ph-sheet .ph-type .type-caret { background: var(--orange); }
.ph-ctas { display: flex; flex-wrap: wrap; gap: 12px 16px; margin-top: 30px; }
.ph-ghost {
  display: inline-flex; align-items: center; min-height: 48px;
  padding: 0 26px; border-radius: var(--pill);
  border: 1.5px solid rgba(23, 18, 12, 0.3);
  color: var(--ph-ink); font-weight: 700; font-size: 0.92rem;
  text-decoration: none;
  transition: border-color var(--dur-ui) var(--ease-fluid), color var(--dur-ui) var(--ease-fluid);
}
.ph-ghost:hover { border-color: var(--orange); color: var(--orange); }
.ph-facts {
  list-style: none; display: flex; flex-wrap: wrap; gap: 10px 34px;
  margin-top: clamp(26px, 4.5vh, 46px); padding: 18px 0 0;
  border-top: 1px solid var(--ph-line);
}
.ph-facts li { font-size: 0.86rem; color: var(--ph-muted); }
.ph-facts b {
  font-family: var(--font-display); font-weight: 800;
  color: var(--ph-ink); margin-inline-end: 6px;
}
/* entrance: the sheet rises once on load — same beat as the ledger heroes */
/* no entrance motion on the sheet — the owner asked the home hero to hold
   still on load; the 3D field supplies the life */
