/* 4rooms. — 3D illustrations + motion layer (29.09.2026). Pairs with motion.js and platform/site3d/apply.py.
   Renders are generated on the SECTION'S OWN background colour (plan.json → bg), and the edges are faded with a mask,
   so the object sits on the page with no visible frame. Everything here is layout-safe: figures reserve their box via
   width/height attributes (no CLS), motion only touches transform/opacity/clip-path. */

/* ---- section head: text left, render right (desktop) ---- */
.shead{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);align-items:center;
  gap:calc(var(--index)*2.4)}
.shead__t > :last-child{margin-bottom:0}
.ill{margin:0;position:relative;pointer-events:none}
.ill img{display:block;width:100%;height:auto;
  -webkit-mask-image:radial-gradient(closest-side,#000 72%,transparent 100%);
          mask-image:radial-gradient(closest-side,#000 72%,transparent 100%)}
.shead .ill{max-width:560px;justify-self:end;width:100%}
@media (max-width:900px){
  .shead{grid-template-columns:1fr;gap:calc(var(--index)*1)}
  .shead .ill{justify-self:center;max-width:420px;order:-1;margin-bottom:calc(var(--index)*-.5)}
}

/* ---- hero render: right side, under the text, fading into the gradient ---- */
.hero .ill--hero{position:absolute;right:-4vw;bottom:4vh;width:min(62vw,1100px);z-index:1}
/* 30.09.2026 owner: "the background gives hard edges". Old mask radial-gradient(60% 62% at 55% 50%) left 20–50 %
   opacity ON the frame edges (ellipse wider than the box) → a visible rectangle, because the render bg is flat while the
   hero is a gradient. Two linear masks intersected reach 0 on every edge by construction; lighten drops the render's
   dark flat bg wherever the hero gradient is lighter (objects are brighter than both, so they stay). Blend sits on the
   figure, not the img: GSAP puts a transform on the figure → it is its own group, an img inside would blend with nothing. */
.hero .ill--hero{mix-blend-mode:lighten}
.hero .ill--hero img{
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 24%,#000 86%,transparent 100%),
                     linear-gradient(180deg,transparent 0,#000 20%,#000 74%,transparent 100%);
  -webkit-mask-composite:source-in;
          mask-image:linear-gradient(90deg,transparent 0,#000 24%,#000 86%,transparent 100%),
                     linear-gradient(180deg,transparent 0,#000 20%,#000 74%,transparent 100%);
          mask-composite:intersect}
.hero .hero__in{z-index:2}
.hero .hero__desc{position:relative}
@media (max-width:900px){
  /* opacity on the img, not the figure: motion.js tweens the figure to an inline opacity:1, which beat .55 here and
     left full-bright cubes under the H1 on packages.html (30.09.2026) */
  .hero .ill--hero{right:-10vw;top:9vh;bottom:auto;width:120vw}
  .hero .ill--hero img{opacity:.5}
}

/* ---- vertical label instead of an eyebrow above the H1 (owner rule: no eyebrows) ---- */
.vlabel{position:absolute;left:calc(var(--index)*-1.6);bottom:0;writing-mode:vertical-rl;rotate:180deg;
  font-size:11px;letter-spacing:.18em;text-transform:uppercase;opacity:.45;white-space:nowrap;margin:0}
.hero__in{position:relative}
/* long geo label is ~420px tall: on short screens it climbs under the fixed header (check_responsive 1440×600) */
@media (max-width:1100px),(max-height:760px){.vlabel{display:none}}

/* ---- heading word mask (motion.js splits words only when GSAP is present) ---- */
.mo .mo-w{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:.08em;margin-bottom:-.08em}
.mo .mo-wi{display:inline-block;will-change:transform}

@media (prefers-reduced-motion:reduce){.ill img{transform:none!important}}
