/* =============================================================================
 * synqa-backgrounds.css  -  Synqa sub-page ambient backgrounds
 * Pairs with synqa-backgrounds.js. Styles the fixed full-bleed field layer and
 * lets it read through the otherwise-opaque Bone sections.
 *
 * Home (no [data-subpage]) and Our Work (its own /work map) are untouched.
 * ============================================================================= */

/* The field sits at z-index:-1 - above the html Bone canvas, below page content.
 * body's opaque Bone would paint over it, so make body transparent on these
 * pages; html keeps the Bone canvas behind. (Mirror of the existing /work map.) */
body[data-subpage]:not([data-subpage="work"]) { background: transparent; }

.synqa-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* v11: bound the field's layout + repaint to its own box. The layer is fixed,
   * full-viewport, z-index:-1, pointer-events:none, so containment changes no
   * pixels (it cannot affect anything outside its box, and nothing outside reads
   * into it). Mirrors the homepage .ch-ambient-field, which already uses this. */
  contain: layout paint;
  /* v9.2: the redesigned animated fields ship one gentle corner-fade. The old
   * per-field "waveform/redaction" extra-fade overrides retired with the old
   * static fields. Content rides on opaque cards regardless, so the field reads
   * in the gutters and between cards rather than washing through copy. */
  -webkit-mask-image: linear-gradient(100deg, rgba(0,0,0,0.4), #000 46%);
          mask-image: linear-gradient(100deg, rgba(0,0,0,0.4), #000 46%);
}
.synqa-bg svg { width: 100%; height: 100%; display: block; }

/* Perf (v9.3): the theta field pulses ~757 SVG circles via transform:scale.
 * Without a dedicated compositor layer the SVG shares the page paint layer, so
 * every frame re-rasters the full-bleed layer and pins the page at ~37fps. With
 * the SVG promoted to its OWN GPU layer (will-change:transform) that raster is
 * isolated and the page holds ~145fps with ALL 757 dots animating as designed
 * (measured). This is scoped to theta only: the other seven fields are light
 * (opacity / rotate / translate / offset-path) and run at ~180fps without
 * promotion, so leaving them un-promoted avoids a per-page full-viewport GPU
 * buffer for no gain. If a future field adds many transform:scale nodes, add its
 * data-bg to this selector. (Replaces the v9.2 ripple-thinning patch.) */
.synqa-bg[data-bg="theta"] svg { will-change: transform; }

/* v11: when the tab is hidden, synqa-backgrounds.js adds .is-bg-paused to the
 * field, which freezes its CSS animations so a backgrounded page stops re-
 * rastering its layer. Resumed (class removed) when the tab is visible again.
 * Behaviourally invisible: only a tab the user cannot see is ever paused. */
.synqa-bg.is-bg-paused,
.synqa-bg.is-bg-paused * { animation-play-state: paused !important; }

/* -----------------------------------------------------------------------------
 * Field MOTION (v9.2). The eight sub-page fields are now ANIMATED ambient
 * texture, driven entirely by CSS keyframes on inline-SVG elements: no rAF, no
 * SMIL, no canvas, no timers. transform / opacity / stroke-dashoffset /
 * offset-distance only (compositor-friendly, same family as the M31 living
 * marks). Honors dec-no-reduced-motion: there is deliberately NO
 * prefers-reduced-motion gate (full motion for every user). Per-element timings
 * are inline in synqa-backgrounds.js; the shared keyframes live here. The
 * helper classes pair with class="sq-ping" / "sq-scan" in the field markup. */
.synqa-bg .sq-ping { transform-box: fill-box; transform-origin: center; animation: sqPing 4.8s ease-out infinite; }
.synqa-bg .sq-scan { transform-box: fill-box; }
@keyframes sqTwinkleSoft{0%,100%{opacity:.3}50%{opacity:.78}}
@keyframes sqGlint3{0%,68%{opacity:0}80%{opacity:.85}90%{opacity:.4}100%{opacity:0}}
@keyframes sqCorePulse{0%,100%{transform:scale(1)}50%{transform:scale(1.16)}}
@keyframes sqPing{0%{transform:scale(.55);opacity:.6}70%{opacity:0}100%{transform:scale(2.6);opacity:0}}
@keyframes sqNodeFade{0%,100%{opacity:.2}50%{opacity:.9}}
@keyframes sqDash{to{stroke-dashoffset:-240}}
@keyframes sqScan{0%{transform:translateY(0);opacity:0}8%{opacity:1}92%{opacity:1}100%{transform:translateY(var(--sq-scan-dist,200px));opacity:0}}
@keyframes sqGlowInset{0%,20%{opacity:0}30%{opacity:1}80%{opacity:1}88%,100%{opacity:0}}
@keyframes sqRotate{to{transform:rotate(360deg)}}
@keyframes sqRotateRev{to{transform:rotate(-360deg)}}
@keyframes sqWiggle{0%,100%{transform:rotate(0deg) translate(0,0)}25%{transform:rotate(3deg) translate(1.5px,-1px)}50%{transform:rotate(0deg) translate(0,1.5px)}75%{transform:rotate(-3deg) translate(-1.5px,0)}}
@keyframes sqSwayTiny{0%,100%{transform:rotate(-2deg)}50%{transform:rotate(2deg)}}
@keyframes sqTravel{to{offset-distance:100%}}
@keyframes sqFadeIO{0%,100%{opacity:0}14%{opacity:1}86%{opacity:1}}
@keyframes sqFadeRare{0%,72%{opacity:0}82%{opacity:.5}90%{opacity:.5}100%{opacity:0}}
@keyframes sqRipple{0%,100%{opacity:.14;transform:scale(1)}50%{opacity:.6;transform:scale(2)}}
@keyframes sqSwap{0%{transform:translateX(0);opacity:1}40%{transform:translateX(0);opacity:1}47%{transform:translateX(60px);opacity:0}48%{transform:translateX(-60px);opacity:0}56%{transform:translateX(0);opacity:1}100%{transform:translateX(0);opacity:1}}
@keyframes sqDots{0%,24%{opacity:0}25%,99%{opacity:.42}100%{opacity:0}}

/* Let the field show THROUGH the Bone hero + Bone sections. Ink/Harbor surfaces
 * declare their own background at >= specificity (in each page's own <style> or
 * in subpages.css), so they stay solid - only plain Bone fills drop out.
 * Add any other full-bleed Bone section selectors your build uses here. */
body[data-subpage]:not([data-subpage="work"]) .ch-07,
body[data-subpage]:not([data-subpage="work"]) .subpage-hero,
body[data-subpage]:not([data-subpage="work"]) .case-section:not(.is-ink):not(.is-harbor):not([data-surface="ink"]),
body[data-subpage]:not([data-subpage="work"]) .theta-section:not(.is-ink):not(.is-harbor):not([data-surface="ink"]),
body[data-subpage]:not([data-subpage="work"]) .about-section:not([data-surface="ink"]),
body[data-subpage="services"] .case-cta-block,
body[data-subpage="about"] .case-cta-block {
  background: transparent;
}

/* v10 TEST: relax the "all info on OPAQUE cards" owner-rule to a HAIR of
 * translucency, so the animated field whispers through the bone cards. One token
 * drives every bone card face; alpha 1.0 = the original fully-opaque bone-100.
 * Scoped to the field pages (this file only loads there). Harbor/Ink bands and
 * solid component cards (.ch-07-card etc.) are excluded and stay opaque. */
body[data-subpage]:not([data-subpage="work"]) { --card-face: rgba(247, 243, 230, 0.93); }

/* -----------------------------------------------------------------------------
 * Content cards: lift the page INFORMATION onto opaque cards that float over the
 * field, so the field reads in the gutters + between cards rather than washing
 * through the copy. The full-bleed Bone SECTIONS stay transparent (above); their
 * inner centred content COLUMNS become the cards. Ink/Harbor bands, the footer,
 * the nav, and existing component cards (.ch-07-card, .theta-step, .theta-channel,
 * .about-team-card, .ch-09-form) stay solid.
 *
 * Card face: --bone-100 (#F7F3E6, a hair lighter than the Bone field) + a
 * --bone-400 hairline + --radius-lg + an explicit float shadow (the --shadow-md
 * token is malformed/unitless, so the shadow is spelled out here).
 * --------------------------------------------------------------------------- */
body[data-subpage]:not([data-subpage="work"]) .ch-07-meta,
body[data-subpage]:not([data-subpage="work"]) .subpage-hero-inner,
body[data-subpage]:not([data-subpage="work"]) .theta-section:not(.is-harbor):not(.is-ink):not([data-surface="ink"]) .theta-section-inner,
body[data-subpage]:not([data-subpage="work"]) .about-section:not([data-surface="ink"]) .about-section-inner,
body[data-subpage="services"] .case-cta-block-inner,
body[data-subpage="about"] .case-cta-block-inner,
body[data-subpage="contact"] .contact-meta,
body[data-subpage="contact"] .contact-form-col,
body[data-subpage="privacy-inquiry"] .contact-meta,
body[data-subpage="privacy-inquiry"] .contact-form-col {
  background: var(--card-face, var(--bone-100));
  border: 1px solid var(--bone-400);
  border-radius: var(--radius-lg);
  box-shadow: 0 6px 22px rgba(31, 24, 20, 0.09), 0 1.5px 4px rgba(31, 24, 20, 0.05);
}

/* .service-block is ALREADY a Bone card (hairline + radius-md + padding); lift it
 * onto the float shadow. NOTE (v10): the bone service listings stay FULLY OPAQUE
 * --bone-100, NOT the translucent --card-face: on /services only 2 of the 6
 * listings are bone (the other 4 are Harbor/Ink, already opaque), so a translucent
 * pair read as inconsistent against the rest. All six listings stay opaque. The
 * Harbor and Ink service blocks keep their own solid surface (excluded). */
body[data-subpage]:not([data-subpage="work"]) .service-block:not(.service-block--harbor):not(.service-block--ink) {
  background: var(--bone-100);
  box-shadow: 0 6px 22px rgba(31, 24, 20, 0.09), 0 1.5px 4px rgba(31, 24, 20, 0.05);
}

/* Columns that already had side padding (0 80px): add vertical breathing room. */
body[data-subpage]:not([data-subpage="work"]) .subpage-hero-inner,
body[data-subpage]:not([data-subpage="work"]) .theta-section:not(.is-harbor):not(.is-ink):not([data-surface="ink"]) .theta-section-inner,
body[data-subpage]:not([data-subpage="work"]) .about-section:not([data-surface="ink"]) .about-section-inner,
body[data-subpage="services"] .case-cta-block-inner,
body[data-subpage="about"] .case-cta-block-inner {
  padding-block: clamp(32px, 3.5vw, 52px);
}

/* Columns that had no padding of their own: a full inset. */
body[data-subpage]:not([data-subpage="work"]) .ch-07-meta,
body[data-subpage="contact"] .contact-meta,
body[data-subpage="contact"] .contact-form-col,
body[data-subpage="privacy-inquiry"] .contact-meta,
body[data-subpage="privacy-inquiry"] .contact-form-col {
  padding: clamp(28px, 3vw, 44px);
}

/* The contact/privacy form panel is itself a card; soften its border so it reads
 * as nested inside the column card, not doubled. Keep the two grid cards top-aligned. */
body[data-subpage="contact"] .contact-form-col .ch-09-form,
body[data-subpage="privacy-inquiry"] .contact-form-col .ch-09-form {
  border-color: var(--bone-400);
}
body[data-subpage="contact"] .contact-grid,
body[data-subpage="privacy-inquiry"] .contact-grid {
  align-items: start;
}

/* -----------------------------------------------------------------------------
 * Harbor "why we built it" band -> contained card (theta/holm/rewrite/patchpal).
 * The Harbor accent stays, but instead of bleeding edge to edge the band now
 * floats as a card over the field, consistent with the Bone content cards around
 * it. Drop the section's full-bleed fill + adjacency borders, move the Harbor
 * fill onto the inner column, round + float + inset it. Side padding (0 80px)
 * carries over from subpages.css; only the block padding is set here. */
body[data-subpage] .theta-section.is-harbor {
  background: transparent;
  border-top: none;
  border-bottom: none;
}
body[data-subpage] .theta-section.is-harbor .theta-section-inner {
  background: var(--harbor);
  border-radius: var(--radius-lg);
  box-shadow: 0 6px 22px rgba(31, 24, 20, 0.09), 0 1.5px 4px rgba(31, 24, 20, 0.05);
  padding-block: clamp(40px, 4vw, 60px);
}

/* The carded sub-page sections float with their own gaps + shadows, so the old
 * full-bleed hairline dividers between sections now just draw a stray line across
 * the field (most visible under the Harbor card). Drop them on the field pages -
 * the cards do the separating. */
body[data-subpage]:not([data-subpage="work"]) .theta-section + .theta-section,
body[data-subpage]:not([data-subpage="work"]) .about-section + .about-section,
body[data-subpage]:not([data-subpage="work"]) .case-section + .case-section {
  border-top: none;
}

/* The services/about CTA block goes transparent over the field too, so its own
 * full-bleed border-top drew a phantom line across the page. Drop it there. */
body[data-subpage="services"] .case-cta-block,
body[data-subpage="about"] .case-cta-block {
  border-top: none;
}

/* Product-page hero reuses the homepage `.ch-07` shell. Width matches the content
 * sections (--content-w). The app-preview column is held substantial so the phone
 * mock reads as a complement to the copy (and trims the copy card's blank right),
 * while staying narrower than the text. Homepage Ch7 (no data-subpage) is untouched. */
body[data-subpage] .ch-07-inner {
  max-width: var(--content-w);
  grid-template-columns: minmax(0, 1fr) clamp(400px, 38%, 480px);
  /* Each card hugs its OWN content and floats on the field (the page's card
     model) - never stretched past its content, which always left dead bone
     space. Top-align the two so their tops match (app-preview header level with
     the copy stamp); the shorter card just ends and the field shows below it. */
  align-items: start;
}
/* On mobile, mobile.css stacks the hero to a single column, but the rule above
   has higher specificity (body[data-subpage]) and would otherwise re-apply the
   2-column grid - whose clamp() min (400px) overflows a phone viewport. Re-assert
   the single column at the mobile breakpoint. */
@media (max-width: 1023px), (pointer: coarse) {
  body[data-subpage] .ch-07-inner { grid-template-columns: minmax(0, 1fr); }
  /* The 3-line roll runs a few px wide for the narrow mobile column at the
     inherited ch-07-head size; size it down a touch so it fits without clipping. */
  .ch-07-head.theta-slot {
    font-size: clamp(28px, 9vw, 34px);
    min-height: calc(3 * 1.02 * clamp(28px, 9vw, 34px));
  }
}
/* Let the browser balance the headline so it never breaks mid-phrase. */
body[data-subpage] .ch-07-head { text-wrap: balance; }

/* The carded sections were spaced for the old full-bleed Bone era (theta 12vh,
 * about/case 14vh top + bottom), so the field gap between cards differed per
 * page (24 vs 28vh) and read as a big empty band. Standardise every field page
 * to one 7vh block -> a uniform 14vh band between cards everywhere. */
body[data-subpage]:not([data-subpage="work"]) .theta-section,
body[data-subpage]:not([data-subpage="work"]) .about-section,
body[data-subpage]:not([data-subpage="work"]) .case-section {
  padding-block: 7vh;
}

/* -----------------------------------------------------------------------------
 * MOBILE COMPOSITION (Phase-4 visual QA). The desktop field-page rules above
 * (the translucent --card-face whisper, the uniform 7vh section padding) win
 * over mobile.css by specificity (body[data-subpage] ...), so the mobile
 * tightening + opacity fixes for the field pages must live here, at >= that
 * specificity. Desktop (>1023px, fine pointer) is untouched.
 * --------------------------------------------------------------------------- */
@media (max-width: 1023px), (pointer: coarse) {
  /* Direction-flip stability (pass 8, owner: "sub-page backgrounds jump around
   * on scroll direction change even if the content doesn't"). A fixed inset:0
   * layer tracks the DYNAMIC layout viewport on iOS: every toolbar show/hide on
   * a direction flip resizes the layer, and the 1360x720 slice-fit field art
   * then RESCALES (scale = max(w/1360, h/720), height-driven in portrait) and
   * RECENTERS (xMidYMid) - the exact "viewport-sized fixed layer" class fixed
   * on the homepage in pass 7.4. Anchor the box to the top and FREEZE its
   * height to the published px token. pass 8.1: raw 100lvh was tried first
   * and FALSIFIED on-device - in the frame-resize webview world lvh
   * re-resolves on every direction change (the whole frame swings), so the
   * raw-lvh box zoomed exactly like inset:0 did. The token re-lands only at
   * quiet adoption (site.js publishViewportTokens runs on EVERY page,
   * scroll-compensated; rotation re-publishes via the width path), so the box
   * is constant through churn in BOTH worlds. Any stale-height band hangs
   * invisibly below the small viewport (fixed, z-index:-1,
   * pointer-events:none, no scroll contribution). 1vh fallback keeps stock
   * behavior with JS absent. */
  .synqa-bg {
    inset: auto;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    height: calc(var(--lvh-px, 1vh) * 100);
  }

  /* Direction-flip stability, content half (pass 8, owner: "sub-page content
   * jumps a little on direction change"). The product heroes ship INLINE
   * style="padding-top:14vh; padding-bottom:7vh" (holm/theta/rewrite/patchpal),
   * which beats every stylesheet and re-resolves per chrome swing in frame-
   * resizing in-app webviews, shifting the whole page below the hero on each
   * flip. Identical 14/7 geometry on the FROZEN px tokens instead (seeded in
   * each page head, republished by subpages.js only at quiet adoption - pass 8
   * added that republish, so rotation stays correct). !important to beat the
   * inline style; homepage .ch-07 has no [data-subpage], untouched. */
  body[data-subpage] .ch-07 {
    padding-top: calc(var(--lvh-px, 1vh) * 14) !important;
    padding-bottom: calc(var(--lvh-px, 1vh) * 7) !important;
  }

  /* Item: the 7vh per-side section padding (set above to give desktop a uniform
   * 14vh field gap between cards) re-inflated the mobile sections OVER the
   * mobile.css reduction, leaving large empty bands - most visibly the
   * holm / rewrite Ink "Sit with us." CTA close and the trailing dark band on
   * /about. On a tall tablet 7vh is ~72px PER SIDE around a single CTA row. Pull
   * the field-page section padding to the mobile scale so the cards size to
   * their content and the field gap between them stays calm but tight. */
  body[data-subpage]:not([data-subpage="work"]) .theta-section,
  body[data-subpage]:not([data-subpage="work"]) .about-section,
  body[data-subpage]:not([data-subpage="work"]) .case-section {
    padding-block: var(--m-section-gap);
  }

  /* The holm / rewrite Ink "Sit with us." close is a one-row CTA in a full-bleed
   * Ink section. holm.html adds an inline padding-bottom:8vh (desktop intent) that
   * stacks another ~80px of dead Ink below the row on mobile. Override the inline
   * style (hence !important) and tighten both ends so the Ink band fits its
   * content with a small, deliberate breath. */
  body[data-subpage="holm"] .theta-section.is-ink,
  body[data-subpage="rewrite"] .theta-section.is-ink {
    padding-top: var(--m-section-gap) !important;
    padding-bottom: var(--m-section-gap) !important;
  }

  /* Item: the field's bright ambient shapes (e.g. the salmon rounded square on
   * /privacy-inquiry, the isometric wireframe on the app pages) read THROUGH the
   * 7%-translucent --card-face on the info/form cards, looking like a stray
   * shape mid-card. On mobile the card must read clean, so make the contact /
   * privacy info + form columns FULLY opaque (the desktop whisper is a large-
   * field nicety that does not survive a phone-sized card). */
  body[data-subpage="contact"] .contact-meta,
  body[data-subpage="contact"] .contact-form-col,
  body[data-subpage="privacy-inquiry"] .contact-meta,
  body[data-subpage="privacy-inquiry"] .contact-form-col {
    background: var(--bone-100);
  }

  /* Item: an isometric wireframe triangle from the field poked through the TOP
   * edge of the holm / rewrite dark Ink CTA section (the field shows in the
   * transparent Bone gutter just above it). Pin the Ink section as its own
   * stacking context above the z-index:-1 field and clip it, so nothing from the
   * field can read into the dark band. The Ink fill (from each page's <style>)
   * already paints the box opaque; this guarantees the occlusion at the seam. */
  body[data-subpage="holm"] .theta-section.is-ink,
  body[data-subpage="rewrite"] .theta-section.is-ink {
    position: relative;
    z-index: 1;
    overflow: hidden;
  }
}

/* Reduced-motion / print: nothing animates here, but hide it for print so it
 * never muddies a printed page. */
@media print { .synqa-bg { display: none; } }
