/* GT Business Consulting — standalone landing page.
   Not part of the Gravity Team design system.
   Designed on a 1920 grid: 1rem = 10px at 1920, so every value below maps 1:1
   to the Illustrator measurement. Scales fluidly with the viewport, but not at a
   constant rate above 1440 — see the root font-size, which is the page's single
   scale control and carries the reasoning. A handful of values that have to span
   the page are deliberately in vw or % instead of rem; each says so. */

:root{
  --ink:#000;
  --paper:#fff;
  --salmon:#fda791;
  --blue:#6d98d6;
  --pale:#effaff;
  --serif:"farnham-display",Georgia,"Times New Roman",serif;
  --sans:"termina",-apple-system,system-ui,sans-serif;
  /* Page gutter, measured 85px at 1920. In vw, not rem: it is an inset from the
     edge of the screen, so it has to track the screen. 4.427vw is exactly the
     8.5rem it replaces at every width below the root scale's knee. */
  --gut:4.427vw;
}

*{box-sizing:border-box}
/* 1rem = 10px at 1920 and scales with the viewport, so every measured value
   below stays in proportion at any desktop width. Capped so it stops growing
   past ~2110px. No lower floor: a floor would hold type at a fixed size while
   the percentage-based columns kept shrinking, breaking the proportions. */
/* 1rem = 10px at 1920 and scales with the viewport — but NOT at a constant rate.

   The design is drawn on a 1920 grid and every value in this file is in rem, so
   the root size is the page's one scale control: type, leading, text measures and
   the vertical rhythm all follow it together. A single slope (it was a flat
   0.5208vw) keeps the page identical at every width, which is why it read as
   "too big" full-screen and better, "more premium", in a smaller window — at
   1920 the body sat at 23px and the hero line at 62px.

   So the slope has a knee. Below 1440 nothing changes at all: 0.5208vw still
   wins, and the design is exactly as drawn. Above it the second term takes over
   and the page grows at a fifth of the rate — 6px of it is fixed, so a wider
   screen buys mostly margin rather than bigger type. At 1920 the root is 8px
   (body 18.4px, hero line 50px); at 2560, 8.7px; at 3840, 10px.

   The knee is at 1440 exactly, where the two terms are equal, so there is no step.

   WHAT THIS BREAKS, AND WHERE IT IS HANDLED: a rem length no longer tracks the
   viewport, so anything that has to *span* the page had to stop being rem. That
   is --gut and the Our Services grid, both below. Everything else is meant to
   scale with the type and is left alone. */
html{font-size:min(0.5208vw,calc(6px + 0.104vw),11px);-webkit-text-size-adjust:100%}
/* 13 Sep 2026: "make it 20% smaller on wider screen" — only the >=1440 behaviour above
   changes; below 1440 is untouched, still exactly as drawn. This multiplies the post-knee
   formula's own output by a single factor (below), so the reasoning only has to be redone
   once if the factor changes again: 6px+0.104vw becomes factor×6px + factor×0.104vw, and
   the 11px ceiling becomes factor×11px. This is a real step at 1440px itself, not a smooth
   blend through the knee — that is what "smaller on wider screens" was asked for.

   Same day, second pass: "scale up all texts 10% more back, as it is too small now" — the
   20% cut read as too aggressive, so the factor moved from 0.8 to 0.88 (0.8 × 1.1 — 10% back
   up from where it was, not 10% off the original). Reference points at 1920/2560/3840 move
   from 6.4/6.96/8px up to 7.04/7.66/8.8px — each exactly 10% more than the 20%-cut values,
   which nets out to 12% below the original (pre-13-Sep) 8/8.7/10px, not 20%. */
@media (min-width:1440px){
  html{font-size:min(calc(5.28px + 0.09152vw),9.68px)}
}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--serif);font-weight:400;font-size:2.3rem;line-height:3.5rem;text-wrap:pretty;overflow-x:clip}
img{display:block;max-width:100%}
p{margin:0}
h1,h2,h3{margin:0;font-weight:400}
a{color:inherit}
a:hover{color:var(--blue)}
.site-foot a:hover{color:var(--salmon)}

/* Type roles. farnham-display carries display + body; termina carries the
   sans display line, labels and interface text. */
.d-serif{display:block;font-family:var(--serif);font-weight:400;letter-spacing:-0.004em}
.d-sans{display:block;font-family:var(--sans);font-weight:600;letter-spacing:-0.012em}

/* Button — measured 339x60 with 2.1rem termina */
.btn{display:inline-flex;align-items:center;gap:1.6rem;height:6rem;padding:0 2.8rem;background:var(--ink);color:#fff;font-family:var(--sans);font-weight:400;font-size:2.1rem;line-height:1;letter-spacing:0.005em;text-decoration:none;transition:background .18s}
.btn:hover{background:var(--salmon);color:var(--ink)}
.btn .arw{width:1.9rem;height:1.9rem;flex:none}
/* The dark buttons go to the warm brand colour with black text on hover
   (13 Sep 2026, was --blue with white text). The light button on the Let's Talk
   room is the exception and is untouched — it inverts to black instead, because
   it sits on a warm lit photograph where salmon reads as the button dimming into
   its own background, and black is the one value the room cannot wash out. */
.btn--light{background:var(--paper);color:var(--ink)}
.btn--light:hover{background:var(--ink);color:#fff}

/* ── Header ─────────────────────────────────────────────── */
/* Centred on the page, not set in the left gutter as the layout export had it
   (changed 12 Sep 2026 at the user's ask). Stretching the header edge to edge and
   centring the link is what does it, rather than a transform, so the logo stays on
   the true centre at any width.

   The band that spans is now full-width and sits above the hero, so it is made
   click-through and only the link itself takes the pointer — otherwise an
   invisible strip across the top of the page would swallow clicks. Nothing is
   under it today; this is so nothing is broken by whatever is put there later. */
.site-head{position:absolute;top:5.4rem;left:0;right:0;z-index:5;display:flex;justify-content:center;pointer-events:none}
.site-head a{pointer-events:auto}
.site-head img{width:27.1rem}

/* ── 1 Hero ─────────────────────────────────────────────── */
.hero{position:relative;height:96rem}
.hero-copy{position:absolute;left:var(--gut);width:54rem}
.hero h1{position:absolute;top:40rem;left:0;white-space:nowrap}
.hero h1 .d-serif{font-size:6.2rem;line-height:5.6rem}
.hero h1 .d-sans{font-size:5.1rem;line-height:5.6rem}
.hero-body{position:absolute;top:57.4rem;left:0;width:54rem}
/* Gap to the button was 1.8rem (13px) and read as stuck to the paragraph;
   raised to the same 5.4rem the plan cards use between their list and their
   own button (.plan .btn), so the two match. The section grew from 91.8rem
   to 96rem to give the button room without pushing into .about, and
   .hero-art now anchors to the section's bottom edge instead of a fixed
   height so it still fills the same visual panel. */
.hero .btn{position:absolute;top:87.3rem;left:0}
/* image panel: measured x962→1920, y362→918; bottom:0 keeps it flush with
   .hero's height instead of a fixed 55.6rem that stopped matching once the
   section grew for the button spacing above. */
.hero-art{position:absolute;left:50.1%;right:0;top:36.2rem;bottom:0;overflow:hidden}
/* THE SPIRE IS CENTRED ON WHAT YOU CAN SEE, not on the panel box (14 Sep 2026:
   "lets center the image on the hero.. so that the vertical structure looks more
   optically in the center on horizontal axis"). The bite eats the panel's left
   36.2rem, so the visible picture runs from 36.2rem to the panel's right edge —
   its centre is 18.1rem right of the box's centre, and the spire, which sits at
   49.6% of the asset, was landing on the box's centre instead.

   Rather than nudging object-position (a percentage there is not a fixed
   distance — how far it moves the content depends on how much the cover crop
   overhangs, which changes with the viewport), the img gets a box 36.2rem wider
   than the panel, pinned at left:0 with the extra hanging off the right. Its
   centre — and so the asset's — is then exactly 50% + 18.1rem, at every width.
   The overhang is never seen, and never scrolled to: the mask paints nothing
   outside the element box, and .hero-art carries overflow:hidden so the wider
   img box is clipped out of the layout as well — a mask restricts painting but
   not scrollable overflow, and without that clip the page gained 36.2rem of
   empty sideways scroll. max-width:none is required too: the global img reset
   clamps at 100%, which would collapse the box back to the panel's width. */
.hero-art img{position:absolute;left:0;top:0;width:calc(100% + 36.2rem);max-width:none;height:100%;object-fit:cover}
/* Leading edge is cut by the supplied puzzle_mask_2, mirrored so the bite and its
   two tabs face left. Two mask layers: the asset pinned left at its own aspect
   ratio (72.69 tall → 44.001 wide), then a solid layer filling the rest.
   Stretching one layer to 100% width would flatten the bite into an ellipse, so
   the bite layer's WIDTH has to be derived from whatever height .hero-art
   actually has right now — 33.66rem was only ever correct for the panel's old
   55.6rem height (33.66/55.6 = 44.001/72.69, the asset's own ratio). Since #40
   made .hero-art 59.8rem tall (96rem section − 36.2rem top), the width was
   recalculated the same way: 59.8 × (44.001/72.69) = 36.2rem. Getting this
   out of sync is exactly what "stretched" the bite into visible steps —
   caught 13 Sep 2026 after #40 changed the height without updating this width
   to match, which is on this session, not the artwork. If `.hero-art`'s height
   changes again, this width must be recomputed the same way:
   new-width = new-height × 44.001 / 72.69. The solid layer is 8px wider than
   the gap it fills (see below), so the two layers overlap rather than abut —
   abutting mask layers leave a hairline seam where their antialiased edges meet.

   THE MASK IS INLINED AS A data: URI, not loaded from assets/. It has to be.
   A CSS mask image is a CORS-sensitive resource, and a page opened straight off
   disk has an opaque origin, so Chrome refuses to load assets/*.svg as a mask and
   fails silently — the mask layer stays empty, the left portion of the panel is
   masked out entirely, and the bite disappears. notes.md says to open index.html
   directly, so that is the case that has to work. A data: URI is not a separate
   fetch, so it loads under file:// and over http alike.

   The path below is the exact `d` of assets/puzzle_mask_2_mirror.svg, unedited —
   a same-day attempt (13 Sep 2026) to redraw its shoulders as smoother curves was
   wrong and is reverted; the shape was never the problem, the width above was.
   The 8kb the asset carries is C2PA metadata; only the path is needed. */
:root{--bite:url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2244.001%22%20height=%2272.69%22%20viewBox=%220%200%2044.001%2072.69%22%3E%3Cg%20transform=%22translate%2844.001%200%29%20scale%28-1%201%29%22%3E%3Cpath%20fill=%22%23000%22%20d=%22M44.001,70.524c0-2.022-1.808-3.556-3.805-3.239-1.615.256-3.271.39-4.958.39-17.533,0-31.747-14.214-31.747-31.747S17.705,4.181,35.238,4.181c1.687,0,3.343.134,4.958.39,1.997.317,3.805-1.217,3.805-3.239V0H0v72.69h44.001v-2.166Z%22/%3E%3C/g%3E%3C/svg%3E')}
.hero-art{-webkit-mask:var(--bite) left center/36.2rem 100% no-repeat,linear-gradient(#000,#000) right center/calc(100% - 36.2rem + 8px) 100% no-repeat;mask:var(--bite) left center/36.2rem 100% no-repeat,linear-gradient(#000,#000) right center/calc(100% - 36.2rem + 8px) 100% no-repeat}

/* ── 2 About ────────────────────────────────────────────── */
.about{position:relative;height:115.7rem}
.about-pale{position:absolute;left:50.1%;right:0;top:0;bottom:0;background:var(--pale)}
/* trapezoid: measured left edge 561 tall, right edge 432 tall, both centred
   on the same axis — a plane turned away from the viewer. Both edges are
   separately insettable (--pl, --pr) so motion.js can turn the plane either
   way; the defaults are the measured state, so with no JS the panel sits
   exactly as drawn. */
.about-art{position:absolute;left:31.3rem;top:27.4rem;width:34.1rem;height:56.1rem;margin:0;--pl:0%;--pr:11.5%;clip-path:polygon(0 var(--pl),100% var(--pr),100% calc(100% - var(--pr)),0 calc(100% - var(--pl)))}
.about-art img{width:100%;height:100%;object-fit:cover}
.about-copy{position:absolute;left:calc(50.1% + 8.1rem);top:27.2rem;width:66.3rem}
.about h2 .d-serif{font-size:5.4rem;line-height:5.6rem}
.about h2 .d-sans{font-size:5.1rem;line-height:5.6rem}
.about-body{margin-top:6.2rem}
.about-body p+p{margin-top:3.5rem}

/* ── 3 How We Work ──────────────────────────────────────── */
.hww{position:relative;height:95.9rem;background:var(--ink);color:#fff}
/* Steps and art swapped sides: the photo is now the left block, the text the
   right one. Art fills from 0 instead of ending at 0, same as before, mirrored.

   Steps used to hang off the right edge at a fixed --gut, which — now that it
   sits on the right — put "Understand" well right of where "Business
   Consultation" starts in the Plans column below it. The two are meant to
   read as one vertical line, so steps now use the SAME offset as the plan
   cards: .plan's own padding-left is 8.8rem in from the 50.1% column start
   (see below), so this is left:50.1% + 8.8rem too, not a right-anchor. */
.hww-steps{position:absolute;left:calc(50.1% + 8.8rem);top:0;width:46rem}
.hww-step{position:absolute;left:0;width:46rem}
.hww-step:nth-child(1){top:7.1rem}
.hww-step:nth-child(2){top:39.9rem}
.hww-step:nth-child(3){top:72.7rem}
.hww-step h3{font-size:6.2rem;line-height:1}
.hww-step p{margin-top:1rem;font-size:2.1rem;line-height:3.2rem}
/* Man_bg and Man_alpha share one frame — stacked at the same rect they align. */
.hww-art{position:absolute;left:0;right:49.9%;top:0;bottom:0;overflow:hidden}
.hww-art img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center}
/* Only the ground moves: the figure is cut out, so drifting what is behind it
   reads as the man holding still while the scene travels past. The 1.28 scale
   is headroom for the drift — 14% of overhang either side against a 12% travel —
   so no edge of the plate can come into frame. --bgx is driven by motion.js. */
.hww-bg{--bgx:0%;transform:translateX(var(--bgx)) scale(1.28);will-change:transform}
.hww-head{position:absolute;left:0;right:0;top:9.4rem;text-align:center}
.hww-head h2{font-size:8.4rem;line-height:1}
.hww-head p{margin-top:1.8rem;font-size:3rem;line-height:1}

/* ── 4 Consulting Plans ─────────────────────────────────────
   The rail is pinned; the four plans pass it on the right. Card 1 carries the
   orb, so it is taller than a screen — the rail simply stays pinned longer. */
/* ONE GRADIENT FOR THE WHOLE SECTION. .plans-rail spans both columns and is the
   only thing in here that carries the sunset; the white column lies on top of it
   and is cut away where the two puzzle tabs go, so they are holes onto the same
   plane rather than copies of it.

   That is what finally fixed this. Painting the sunset a second time inside each
   tab and keeping the two in step failed five different ways — measured offsets
   lagged the compositor, position:fixed lagged the mask instead, sampled stops
   only matched one window size, and the shared damped follower put the rail a few
   percent behind whatever it was supposed to meet. A hole cannot disagree with
   the thing behind it. */
.plans{position:relative;display:grid;grid-template-columns:50.1% 1fr;background:#4b7cc4}
.plans-rail{grid-column:1/-1;grid-row:1}
.plans-list{grid-column:2;grid-row:1;z-index:1}
/* Rail contents sit together at the top rather than being pushed to opposite
   ends of the pinned 100vh box (13 Sep 2026: "put the serif text closer after
   the main title"). space-between put the lede at the very bottom of the
   screen, most of a viewport below the heading; flex-start plus the paragraph's
   own margin keeps the two as one block. The bottom padding stays as the box's
   closing air. */
/* THE RAIL UNPINS HALF A SCREEN BEFORE THE SEAM and scrolls up with the page
   from there (13 Sep 2026): "when you scroll to this point the light gradient on
   top should not be seen, it should go up and dissapear". Sticking all the way
   to the section's end left a full viewport of sunset on screen at the moment
   .svc arrives — its light top included — so the band handed off with a light
   gradient above a dark seam. A margin-bottom shortens a sticky element's own
   travel, so the rail lets go early and the light end walks off the top of the
   screen; what it uncovers is .plans's background, set to #4b7cc4 above, which
   is both the rail's own cold end and .svc's first stop, so the strip it leaves
   behind is continuous with the section below and the rail's bottom edge cannot
   be seen. (The white column covers the whole of column 2, so this background is
   only ever visible in the strip the rail vacates.) */
.plans-rail{position:sticky;top:0;align-self:start;height:100vh;margin-bottom:50vh;overflow:hidden;background:var(--salmon);display:flex;flex-direction:column;justify-content:flex-start;padding:10rem 8rem 11rem var(--gut)}
/* Sunset on the rail: warm at the first plan, cold by the last. FOUR gradient
   layers cross-fade in a chain over the flat salmon — one per plan — rather than
   one blend from salmon to blue, so the in-betweens are colours of their own
   instead of the muddy midpoint a direct A-B interpolation gives. Each layer is
   opaque at its own stage and stays opaque under the next, so only two palettes
   are ever on screen at once.

   ONE STAGE PER PLAN (14 Sep 2026: "we have a custom color for each plan except
   for the last one +need more. they share the same color. should be unique for
   each so that the gradient keeps travveling as you scroll through each
   section"). There were three layers against four plans, and the third finished
   before the last plan began, so plan 4 sat on one colour the whole way down.
   With a fourth layer every plan owns a transition, and motion.js no longer
   holds the stage boundaries as fixed fractions — it derives them from where the
   plans actually are, so each hands over as its own section leaves the screen.

   Stage 0 is the flat --salmon on .plans-rail itself, so the rail is correct
   with no JS and correct under prefers-reduced-motion.

   All stages run at exactly 180deg and carry four stops, so each reads as level
   strata of sky. They ran at 172° / 186° / 196° until 12 Sep 2026; a tilt fights
   the horizontal read and a sunset's bands are level.

   **The middle stages are not pink.** One pass ran #efa8b4 → #c99ac9 and put rose
   and lilac across most of the section — "dont like the big presence of pink
   hues". The path is warm sand → clay → mauve → periwinkle: the rosiness is
   carried by desaturated clay rather than by pink itself.

   **g4 ends on #4b7cc4, which is the first stop of .svc.** The rail's bottom
   edge is the seam with that section, and at the end of the travel the rail is
   fully cold, so the two meet on the same value and the sunset runs off the
   bottom of the rail into the services blue instead of stopping at a line.
   If .svc's top colour changes, this stop changes with it.

   All stages are held light enough to keep the dark rail copy above 4.5:1. */
.plans-rail>*{position:relative;z-index:1}
.rail-g,.rail-wash{position:absolute;z-index:0;inset:0;opacity:0;pointer-events:none}
.rail-g1{background:linear-gradient(180deg,#ffe2bc,#ffc79c 34%,#fb9c7f 68%,#f4836e)}
.rail-g2{background:linear-gradient(180deg,#f7c9a6,#dfae9e 38%,#c9a3ae 72%,#b89bbe)}
.rail-g3{background:linear-gradient(180deg,#dcc0cf,#b9aecd 38%,#9aa4cd 72%,#8f93c6)}
.rail-g4{background:linear-gradient(180deg,#c3d7f0,#93b4e4 36%,#6d98d6 72%,#4b7cc4)}
/* The three warm stages are drawn oversized and slide: 100% × 165% of the rail,
   with --drift walking the window down the gradient as the plans pass. That is
   what "a wash smoothly travelling" is here — the sky itself drifting, which has
   no edge anywhere in it and so cannot read as a wipe. g4 is deliberately left
   at natural size and does not drift: it is the layer that has to land exactly
   on .svc's #4b7cc4 at the rail's bottom edge, and a moving window would not. */
.rail-g1,.rail-g2,.rail-g3{background-repeat:no-repeat;background-size:100% 165%;background-position:0 var(--drift,30%)}
/* The wash: one broad swell of light travelling down the rail, centred on --wy.

   Two earlier versions, both rejected, and they failed in opposite directions.
   A 120% × 46% radial ellipse read as "a stretched white oval blurred
   travelling" — an ellipse on a rail this tall is visibly an ellipse — and was
   also "too blurred and hard to see". Answering that with contrast made it
   worse: a white core against black at ±23% gave the band two hard shoulders,
   and those shoulders travelling down the rail are exactly the "easy to see
   horizontal wipes" the user then objected to. A band with an edge is a wipe.

   So: no dark side at all, and no core. The stops below are a bell — each step
   roughly doubles on the way in — so luminance changes continuously across the
   whole height and there is no position you can point at and call the edge of
   it. It is weaker than either previous version on purpose; the movement, not
   the contrast, is what should be visible. The drifting stages above carry most
   of the travel now, and this only has to add light on top of them.

   soft-light keeps it a change in light rather than a fifth colour, so one wash
   reads across all four stages. motion.js owns its opacity: it fades out over
   the last of the travel so nothing sits on top of the seam with .svc, and it
   starts at 0 here so no-JS and prefers-reduced-motion leave the rail flat
   --salmon exactly as drawn, with no vignette on it. */
.rail-wash{--wy:50%;mix-blend-mode:soft-light;background:linear-gradient(180deg,
  rgba(255,255,255,0) calc(var(--wy) - 52%),
  rgba(255,255,255,.05) calc(var(--wy) - 38%),
  rgba(255,255,255,.15) calc(var(--wy) - 26%),
  rgba(255,255,255,.30) calc(var(--wy) - 15%),
  rgba(255,255,255,.44) calc(var(--wy) - 6%),
  rgba(255,255,255,.46) var(--wy),
  rgba(255,255,255,.44) calc(var(--wy) + 6%),
  rgba(255,255,255,.30) calc(var(--wy) + 15%),
  rgba(255,255,255,.15) calc(var(--wy) + 26%),
  rgba(255,255,255,.05) calc(var(--wy) + 38%),
  rgba(255,255,255,0) calc(var(--wy) + 52%))}
.plans-rail h2{font-size:8.4rem;line-height:9.1rem}
.plans-rail p{width:64rem;max-width:100%;margin-top:6rem}
.plans-list{position:relative;overflow:hidden}
/* THE WHITE COLUMN, in three panels that tile it exactly. The two end panels
   used to be masked with the supplied puzzle piece inverted, leaving a
   tab-shaped hole onto the sunset behind — removed 13 Sep 2026 at the user's
   instruction ("remove the mask ... leave it filled white") for both the top
   and bottom tabs; see tasks.md for the full history of that mask (the
   asset, its geometry, the hairline/chord-cut fixes). The three panels are
   now plain solid rectangles and their old position/size math is kept only
   because it still makes them tile without a seam — .paper-top and
   .paper-bot's boxes already reach exactly to .paper-mid's edges, 2px of
   overlap included, so removing just the mask/-webkit-mask declarations was
   enough: no geometry changed. */
.paper{position:absolute;background:var(--paper);pointer-events:none}
.paper-top{left:0;right:auto;width:101.16%;top:0;height:0;padding-bottom:calc(122.4274% + 2px);margin-top:calc(-66.0496% + 4rem)}
.paper-bot{left:auto;right:0;width:101.16%;top:100%;height:0;padding-bottom:calc(122.4274% + 2px);margin-top:calc(-56.3778% - 2px)}
.paper-mid{left:0;right:0;top:0;bottom:0;margin-top:calc(56.3778% + 4rem);margin-bottom:56.3778%}
.plan{position:relative;min-height:100vh;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;padding:12rem 4rem 12rem 8.8rem;--po:1;--py:0px;opacity:var(--po);transform:translateY(calc(var(--py) + var(--pl,0) * -10rem));will-change:opacity,transform}
/* Used to clear the visible salmon tab (calc(55.73% + 18rem)); with the mask
   gone there is no shape left to clear, only a flat white panel, so this is
   now a plain rem value — dropped to 14rem so the heading doesn't sit in a
   long stretch of empty white below the How We Work section. */
.plan--first{justify-content:flex-start;padding-top:14rem}
/* Same story as .plan--first above, mirrored: used to clear the blue tab
   rising from below (calc(55.73% + 20rem)), now a plain rem value since the
   panel is flat. min-height still goes — every other card is sized so the
   next arrives as it leaves, and this one has nothing after it — and this
   padding-bottom is just the section's closing air above .svc.

   --pl then carries the content up as you scroll past (motion.js), so the text
   lifts away as the blue rises into it. */
.plan:last-of-type{min-height:auto;padding-bottom:16rem}
.plan h3{font-size:5.6rem;line-height:6.3rem}
/* Set at 4rem, not the 6.3rem measured on the artwork: the artwork only shows
   "Rp 2.500.000", and the longest real price — "From Rp 15.000.000 / project" —
   has to hold the amount and unit on one line across all four plans. It sat at
   5rem until 12 Sep 2026; the user asked for the whole line smaller, and the
   unit comes down with it (3rem → 2.4rem) so the two keep their proportion. */
.plan-price{margin-top:3.6rem;font-size:4rem;line-height:4.8rem;letter-spacing:-0.01em;white-space:nowrap}
.plan-price em{font-style:normal;font-size:2.4rem;letter-spacing:0}
.plan-lede{margin-top:3.8rem;width:64rem;max-width:100%}

/* Back in the page's sans at 600, and at 2rem — under the body's 2.3rem, so the
   list reads as a specification beside the lede rather than as more prose. Four
   settings on 12 Sep 2026: sans 2.5rem, then 2.1rem ("smaller"), then the body
   serif at 2.3rem, then here. The serif pass is the one this reverses; the ask
   was the sans back, a bit smaller again, so it lands below where the sans was.

   The diamond went 1.2rem → 0.85rem in the same day's work, and the gap after it
   1.4rem → 2.2rem. It is a square turned 45°, so its drawn width is its side
   × √2: 0.85rem reads as 1.2rem across, which keeps it a marker beside the type
   rather than a bullet competing with it. `translate` lifts it off the baseline
   onto the lowercase's optical centre; it tracks the type it sits against, so it
   goes back to the sans value, scaled for 2rem. */
.plan-list{margin:4.4rem 0 0;padding:0;list-style:none;font-family:var(--sans);font-weight:600;font-size:2rem;line-height:3.1rem}
.plan-list li{display:flex;align-items:baseline;gap:2.2rem}
.plan-list i{flex:none;width:.85rem;height:.85rem;background:var(--ink);transform:rotate(45deg);translate:0 -0.13rem}
.plan .btn{margin-top:5.4rem}
.plan-fine{margin-top:6rem;width:64rem;max-width:100%;font-size:1.7rem;line-height:2.6rem}

/* ── 5 Our Services ─────────────────────────────────────────
   Sky is a gradient sampled off the artwork (#4b7cc4 at the band top, running
   to the shelf photograph's OWN sky colour at the photo's top edge, so the two
   meet on one value and the photo's sky continues the page's without a seam —
   #88afef is sampled off the top rows of Documents.webp).

   THE PHOTO IS SHOWN WHOLE AND THE CARDS ARE LAID ON ITS GRID (13 Sep 2026).
   It used to be 97.3rem tall against a background sized 100% auto, which at any
   normal width is shorter than the image's own 1600×897 — so the top of the
   shelf unit was cropped off and the cards, positioned in a rem/vw grid of their
   own, could not line up with the bays.

   NOTHING HERE IS IN vw ANY MORE, and that matters: a vw length includes the
   scrollbar and an element's own width does not, so a box sized 56.0625vw is a
   few pixels TALLER than the image drawn at 100% of that box's width — the
   background then bottom-anchors inside it and every card sits a few pixels off
   the bay it was measured onto. `aspect-ratio` derives the height from the box's
   real resolved width instead, so the box and the image are the same rectangle
   at any width, scrollbar or not.

   The box is 1600×859, not the image's 1600×897: the background is sized
   `100% auto` and pinned to the bottom, so the image's own 38px sky strip is
   clipped off the top and the page's gradient runs straight into the top edge of
   the woodwork. That strip is a flat colour and the gradient above it is not, so
   leaving it visible put a faint level line across the band.

   THE LIGHT GROWS UPWARD OUT OF THE SHELF (13 Sep 2026). The sky is a flat
   #4b7cc4 ground with ONE soft glow laid over it: an eleven-stop alpha ramp of
   #88afef, transparent at its top and fully opaque at its bottom, whose bottom
   edge is pinned to the photo's top edge. --skyh is that glow's height in rem,
   and motion.js grows it from a few rem to the sky's full 64 as the band passes.
   The stops are placed so luminance changes continuously across the whole span
   — no position in it can be pointed at as the edge — and because the profile
   scales with the glow, it is just as diffused at its smallest as at 64
   ("i do not like such sharp gradient line… more diffused even on early stage").
   Weighted towards the bottom on purpose, and the short end of the sweep raised
   from 8rem to 24rem, after the onset still read as a band arriving: "the
   gradient here on shelves is too harsh when it appears, should be more blurred
   gently". A single hard-started ramp was the first version and drew a visible
   line where it met the flat ground.

   Moving the LIGHT end instead was tried twice and cannot work: that end IS the
   seam with the photograph, so any transition on it has to finish before the seam
   is on screen — which is before any of this band is visible at all.

   The default is --skyh 64, the full sky, so with no JS and under
   prefers-reduced-motion the sky is the whole ramp as drawn.

   The sky above is 64rem of the band's own padding, and the band's height is that
   plus the photo ("also there is more room ona above it"). */
.svc{position:relative;padding-top:64rem;--skyh:64;background:linear-gradient(180deg,
  rgba(136,175,239,0),
  rgba(136,175,239,.01) 12%,
  rgba(136,175,239,.03) 24%,
  rgba(136,175,239,.07) 34%,
  rgba(136,175,239,.13) 44%,
  rgba(136,175,239,.22) 54%,
  rgba(136,175,239,.33) 64%,
  rgba(136,175,239,.47) 74%,
  rgba(136,175,239,.64) 84%,
  rgba(136,175,239,.82) 92%,
  #88afef) 0 calc(64rem - var(--skyh) * 1rem)/100% calc(var(--skyh) * 1rem) no-repeat,#4b7cc4;color:#fff;overflow:hidden}
/* THE SOURCE WAS REPLACED 13 Sep 2026 with a higher-resolution render carrying
   the GT logo mark on the lit shelf: 2912×1632, re-encoded to 2560×1435 webp at
   q88, **428kb** against the old 152kb. The old file was 1600 wide and visibly
   soft on a large screen — "looks too compressed, low quality … keep it good
   quality but still optimized". 2560 is 1:1 on the widest common desktop, and
   the geometry is unchanged: every upright and board in the new render lands on
   exactly 1.82× the old pixel positions, so the card grid only had to be
   rescaled, not re-measured. Its sky strip is 76 of 1632 rows (the old was 38 of
   897), which is why the box is 2912/1556 and the card y figures moved a
   fraction.

   CROPPED TO THE OUTER CARDS 14 Sep 2026 ("lets try to cut the sides on this
   image so that the black boxes touch the edges of the browser"). The two end
   bays of the unit — the narrow ones the cards never used — are now cut off
   screen. The first pass cut to exactly card 1's left edge, image x 304, which
   left a grey hairline of the upright showing beside the lower-left card, whose
   bay starts a couple of pixels further in: "you can see these grey lines, lets
   cut a bit more". So the cut is now **x 310 → 2629**, six pixels inside each
   card edge — the background is drawn at **125.57%** wide (2912/2319) and
   positioned at **52.28%**, and the outer cards run a fraction past the viewport
   on both sides rather than meeting it, which .svc's overflow:hidden clips.
   Overlapping the edge is the only way to be sure of it at every width; the same
   reasoning as the hero mask's 8px overlap.

   Vertically nothing changes: the box still shows the same 1556 rows, anchored
   at the bottom, so the box's own ratio is **2319/1556** and every card's y
   figure keeps its meaning.

   THE CARD X FIGURES WERE REMAPPED THROUGH THE SAME CROP — each one carried back
   to the render's own pixels and forward again through the new span, so the five
   still land on the bays they were measured to. */
.svc-shelf{position:relative;width:100%;aspect-ratio:2319/1556;background:url(assets/Documents.webp) 52.28% bottom/125.57% auto no-repeat}
/* Centred in the sky rather than hung off a top offset (13 Sep 2026: "our
   services text is also should be centered vertically, right now it is too on
   top") — the box is the sky's own 64rem and the line centres inside it, so it
   stays centred if that figure ever changes. */
.svc h2{position:absolute;left:0;right:0;top:0;height:64rem;display:flex;align-items:center;justify-content:center;text-align:center;font-size:5.4rem;line-height:1}
/* Same box as the photo, so the cards can be placed in its coordinates. */
.svc-grid{position:absolute;left:0;right:0;bottom:0;aspect-ratio:2319/1556}
/* MEASURED OFF Documents.webp PER BAY, in its own pixels, then divided — x
   against the image's 1600 width, y against the 859 the box shows (so an image y
   becomes (y - 38) / 859). No two bays are quite the same size, so no two cards
   are: every edge below is its own measurement, not a shared column value.

   HOW THE EDGES WERE FOUND. For each upright, at every scanline inside each
   opening, the wood band was located by scanning outward until the pixels turn
   blue (the stacked documents) — giving the bay's true inner faces at that
   height: 171/592 for the left bay, 596/1018 for the middle, 1020/1445 for the
   right. The boards' top edges, found the same way down each column and taken as
   medians, are y 43, 135, 227, 320, 444, with the lit cove starting at 568.

   EVERY EDGE IS THEN BIASED ~2px INTO THE FRAME, never away from it. The
   uprights and boards are a couple of pixels off vertical/level across the
   photo's own perspective, so an edge placed on the average face leaves a
   hairline of bay interior showing at some heights — which is what "there are
   still many gaps" was. Biased inward, the card covers a sliver of the woodwork
   instead, which is invisible: the card is on top of the shelf either way. Same
   principle as the hero mask's overlapping layers.

   Cards are two openings tall and the columns are offset against each other,
   which is the rhythm of the layout: the outer columns take rows 1-2 and rows
   4-5, the middle one takes rows 3-4 between them. The two top cards start at
   the box's own top edge, which the crop has already put at the unit's top.

   HAND-ADJUSTED 13 Sep 2026, three rounds — the five boxes were dragged and
   resized directly in the preview and those positions are the values in this
   rule, converted from the grid box each round was set against (the last one
   1578 × 847.19) into percentages so they hold at any width. They sit within a
   pixel or two of the measured faces above, so the same inward bias still
   holds. Do not "correct" them back to the measured figures.

   HEIGHT, NOT min-height (13 Sep 2026). It was min-height so a card whose copy
   needed one more line could grow instead of clipping — but a percentage
   min-height also beats the inline `height` a drag writes, so the bottom edge
   could not be trimmed in the editor at all, which is how it was found. Plain
   height means the boxes are draggable both ways; the trade is that copy longer
   than its box spills below it rather than pushing it open, so a box should not
   be trimmed past its text. As set, the tallest text block in any card fills
   about two thirds of it. */
.svc-card{position:absolute;background:var(--ink);padding:1.1vw}
.svc-card:nth-child(1){left:-0.2377%;width:33.4212%;top:0.2703%;height:21.3409%}
.svc-card:nth-child(2){left:66.8404%;width:33.4212%;top:0;height:21.4927%}
.svc-card:nth-child(3){left:33.5679%;width:32.9581%;top:21.8951%;height:25.0378%}
.svc-card:nth-child(4){left:-0.1498%;width:33.2124%;top:32.8383%;height:28.6673%}
.svc-card:nth-child(5){left:66.7609%;width:33.3423%;top:32.8383%;height:28.6911%}
/* CARD TYPE IS IN vw, NOT rem, and it is the one place on the page where that
   is right: these cards have to fit inside two bays of a photograph that is
   sized off the viewport width, so their type has to be sized off the same
   thing or the fit changes with every width. In rem the copy overran the bays
   below 1440 by a quarter of a card and sat with a third of a bay spare at
   2560. Sized this way the fit is identical at every width — the tallest card
   (Business & Management Consulting) fills about nine tenths of its bay.
   1.5vw is 22.7px at 1512, 28.8px at 1920; 1.1vw is 16.6px and 21.1px. Raised
   from 1.25/0.92vw on 14 Sep 2026 once the crop (#75) made every card about a
   quarter wider and its bay a quarter taller — the type had stayed at the old
   scale, so the boxes had gone slack. */
.svc-card h3{font-size:1.5vw;line-height:1.68vw}
/* The body copy is held a step below the white titles (#d2d6d8, 14 Sep 2026, set
   by the user): on a black card at this size a full-white paragraph competes
   with its own heading. Still 12.8:1 on the card, well clear. */
.svc-card p{margin-top:0.85vw;color:#d2d6d8;font-size:1.1vw;line-height:1.52vw}

/* ── 6 Why GT ───────────────────────────────────────────────
   Four equal columns, no gutters. Orbs hold the positions drawn on the artwork
   — top in columns 1/3, bottom in 2/4 — and bob about them. The bob is kept
   small deliberately: at full band travel an orb crosses its own column's
   title and blanks it out. Amplitude and 7s cycle are placeholders, not
   measured from anything. */
/* THE ORB IS THE COLUMN'S FULL WIDTH (13 Sep 2026: "they need to touch the sides
   of the box, right now there is too much empty space around them"). width:100%
   with a square aspect-ratio makes its diameter the column's own width at any
   screen size, with no viewport unit in it — so the scrollbar cannot shift it.

   IT NO LONGER STOPS AT THE MIDDLE (14 Sep 2026: "lets make it so that they go
   closer to the sans serif headeds, with a bit of spacing"). Half of --why-pad
   centred the orb, which left a lot of air on the title's side. The travel is
   now --why-pad minus 15.3rem, which is the title's own 5rem offset from the end
   of the column, plus a two-line title's 6.8rem, plus 3.5rem of clearance — so
   the orb comes to rest just under the heading. 44.7rem at the current 60rem
   pad. Single-line titles simply get more air; the two-line case is what the
   figure is set against, since three of the four wrap.

   The body copy is uncovered either way: it ends 24rem from its own end of the
   column (10rem offset plus four lines), well inside the 44.7rem the orb moves.

   That also fixes the arithmetic the old version needed: travel is half of
   --why-pad exactly, a rem value, because the orb's diameter cancels out of
   (height - diameter). The column is --why-pad taller than the orb, so each text
   gets half of it — 26rem — and the offsets below sit inside that with room to
   spare: at 40rem the orb stopped a few pixels off the body copy ("they dont go
   up/down enough, almost touch the serif text"), so the band grew and the copy
   came down to 6rem, which is also where the circle is wide enough to cover a
   27rem measure at rest. 36rem was tried before that and overlapped outright. */
.why{position:relative;display:grid;grid-template-columns:repeat(4,1fr);height:calc(25vw + var(--why-pad));overflow:hidden;--why-pad:60rem;--why-travel:calc(var(--why-pad) - 15.3rem)}
.why-col{position:relative;overflow:hidden}
/* A WASH THAT TRAVELS THE COLUMN WHILE IT IS HOVERED (13 Sep 2026). At rest the
   column is its flat brand colour and nothing is drawn over it — "while not
   interactive they should stay flat color. so we just see this wash travelingn
   when the hoovered". An earlier pass laid a permanent four-stop sky under the
   whole band and slid it, which was both always-on and too faint to read.

   One layer, transparent until hovered. ::before is a band drawn at **260% of the
   column's height** — its soft shoulders are well over a column tall each, which
   is what makes it read as light rather than as a stripe — parked just off the
   column at rest. Hovering fades it in over half a second and walks it across in
   2.4s; leaving fades it out while the position returns, so what you see is a
   single pass, not a wipe reversing.

   EACH COLUMN'S WASH TRAVELS THE WAY ITS ORB DOES: warm columns top to bottom,
   cool columns bottom to top. Note the arithmetic — with a background TALLER
   than its box, a percentage position moves the image UP as it increases
   (offset = (boxH - imgH) × p), so the warm columns run 95% -> 5% to travel
   downward, not the other way round. Getting that backwards is what made both
   directions rise: "on the warm ones it should travel from up to down".

   IT DEEPENS RATHER THAN LIGHTENS, and carries a more saturated hue at its core
   ("some additional more saturated hue in it") — warm through #db5440 into a
   saturated #e8402a, cool through #345cb0 into #2442c6. Deepening is also a
   contrast decision: the copy over it is white, so a bright band would take its
   contrast away. Neither ramp crosses temperature.

   THE ALPHAS ARE HELD LOW ON PURPOSE — .32 at the core against .62 in the first
   version ("mode blured and not soo contrast"). Past that the band stops being
   weather and starts being a shape; the size and the eleven-stop ramp carry the
   effect instead of the opacity.

   TURNED OFF 13 Sep 2026 at the user's instruction — "lets turn this gradient
   wash off for now on these circles". The rules are commented out directly
   below, tuned values and all; uncomment the block to restore it. The columns
   are their flat brand colours at all times again, and only the orb moves.

   The layer sits at z-index 0 with .why-col>* at 1, so the orb still paints over
   both texts. */
.why-col>*{z-index:1}
/* .why-col::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:0;background-repeat:no-repeat;background-size:100% 260%;transition:background-position 2.4s cubic-bezier(.33,0,.15,1),opacity .5s linear}
.why-col:hover::before{opacity:1}
.why-col:nth-child(odd)::before{background-position:0 95%}
.why-col:nth-child(odd):hover::before{background-position:0 5%}
.why-col:nth-child(even)::before{background-position:0 5%}
.why-col:nth-child(even):hover::before{background-position:0 95%}
.why-col:nth-child(odd)::before{background-image:linear-gradient(180deg,
  rgba(233,112,95,0),rgba(233,112,95,.04) 14%,rgba(233,112,95,.10) 26%,rgba(219,84,64,.18) 36%,
  rgba(226,70,42,.26) 44%,rgba(232,64,34,.32) 50%,rgba(226,70,42,.26) 56%,rgba(219,84,64,.18) 64%,
  rgba(233,112,95,.10) 74%,rgba(233,112,95,.04) 86%,rgba(233,112,95,0))}
.why-col:nth-child(even)::before{background-image:linear-gradient(180deg,
  rgba(75,124,196,0),rgba(75,124,196,.04) 14%,rgba(75,124,196,.10) 26%,rgba(52,92,176,.18) 36%,
  rgba(40,74,198,.26) 44%,rgba(36,66,198,.32) 50%,rgba(40,74,198,.26) 56%,rgba(52,92,176,.18) 64%,
  rgba(75,124,196,.10) 74%,rgba(75,124,196,.04) 86%,rgba(75,124,196,0))} */
.why-col:nth-child(odd){background:var(--salmon)}
.why-col:nth-child(even){background:var(--blue)}
.why-orb{position:absolute;left:0;width:100%;aspect-ratio:1/1;border-radius:50%;background:var(--paper)}
/* NOTHING MOVES ON ITS OWN. Every circle rests in the first state — over its body
   copy, with the title showing — and travels only while its own column is hovered,
   uncovering the serif paragraph and covering the sans title. Let go and it
   returns. The band is still until someone reads it.

   This replaced a 12s cycle on 12 Sep 2026 ("lets ditch the automatic
   movements"). The whole apparatus that went with it is gone: two keyframe sets,
   four phase delays chosen so no two circles moved at once, and a JS block that
   reversed the animation's playbackRate on hover. A transition needs none of it —
   :hover is the state, and the transition is the travel between the two.

   0.96s on cubic-bezier(.87,0,.13,1), both carried over from the cycle: it barely
   leaves, crosses the middle in a rush and settles. A softer curve over a move
   this short is indistinguishable from constant speed.

   Hover is left ungated by `@media (hover:hover)` on purpose — a tap on a touch
   device applies :hover, so the reveal works there too. Revisit when the mobile
   pass lands, since that hover can stick until the next tap elsewhere.

   GOING AND COMING BACK ARE STILL DIFFERENT, BUT ONLY JUST. A transition takes
   its timing from the state it is heading *to*, so the rule on :hover times the
   reveal (.85s, no delay) and the rule on the orb itself times the return
   (1.05s, 0.08s behind it). Crossing from one column to the next, that is enough
   for the two circles to read as two objects rather than one hinge, without
   either of them appearing to wait.

   IT USED TO WAIT. The split was 0 / .62s on cubic-bezier(.9,0,.1,1) against
   .38s / 1.5s on a long tail, chosen (#16) so that a horizontal crossing read as
   intent rather than as two glued halves. Over a travel this long it read as a
   fault instead: "there is this weird delay and the whole animation of these
   circles also is not smooth enough". The 0.38s hold is gone, the two durations
   are within 0.2s of each other, and both run one gentle in-out curve —
   cubic-bezier(.33,0,.15,1) leaves and arrives softly where the old .9/.1 curve
   crossed the middle in a rush, which is what made a 26rem move feel abrupt. */
.why-col:nth-child(odd) .why-orb{top:0}
.why-col:nth-child(even) .why-orb{bottom:0}
.why-orb{transition:transform 1.05s cubic-bezier(.33,0,.15,1) .08s;will-change:transform}
.why-col:hover .why-orb{transition:transform .85s cubic-bezier(.33,0,.15,1) 0s}
.why-col:nth-child(odd):hover .why-orb{transform:translateY(var(--why-travel))}
.why-col:nth-child(even):hover .why-orb{transform:translateY(calc(var(--why-travel) * -1))}
.why-col h3{position:absolute;left:0;right:0;width:26rem;margin:0 auto;text-align:center;color:#fff;font-size:2.9rem;line-height:3.4rem}
.why-col:nth-child(odd) h3{bottom:5rem}
.why-col:nth-child(even) h3{top:5rem}
.why-body{position:absolute;left:0;right:0;width:35rem;margin:0 auto;text-align:center;color:#fff;font-size:2.7rem;line-height:3.5rem}
.why-col:nth-child(odd) .why-body{top:10rem}
.why-col:nth-child(even) .why-body{bottom:10rem}
/* The reveal still works; it just arrives without the travel. */
@media (prefers-reduced-motion:reduce){.why-orb{transition:none}}

/* ── 7 Let's Talk ───────────────────────────────────────────
   The artwork's blue beam (a #1e7bfb → #adb6fe wedge, 800 wide at the band top,
   apex 568 down on the page centre) was removed on the user's instruction once
   the scrubbed footage went in: the wedge was drawn over a still, and the film
   lights the room from its own cove strips. */
/* Back to a 30-frame webp sequence (13 Sep 2026), drawn into a canvas by
   room.js — see that file's own header for why the 12 Sep two-still crossfade
   didn't survive the new source clip. assets/room/f00.webp (the dark first
   frame) is also the CSS background here, so with no JS, or before the frames
   finish loading, the room shows dark and un-lit — the same state the scrub
   starts from — and nothing flashes in once JS takes over. The canvas itself
   starts fully transparent so that background shows through until room.js
   paints the first frame. */
/* THE BAND IS THE ROOM'S OWN SHAPE (13 Sep 2026: "this room section is cut on
   some browser sizes. it needs to be displayed in full height everywhere"). It
   was a fixed 104.4rem, and the room is drawn cover-fit, so on any window whose
   proportions did not match the footage the picture was cropped — badly at wide,
   short windows, where the ceiling and the floor both went. aspect-ratio pins
   the band to the frames' own 1120×630, so cover and contain are the same fit
   and the whole room is visible at every size.

   The headline and button then had to leave rem for percentages of the band: they
   sit on the photograph, so their place in it should hold at any width — 19.5rem
   and 44rem of the old 104.4rem height are 18.68% and 42.15%. */
.talk{position:relative;aspect-ratio:1120/630;overflow:hidden;color:#fff;background:#141018 url(assets/room/f00.webp) center/100% 100% no-repeat}
.talk-art{position:absolute;inset:0;width:100%;height:100%;display:block}
/* The headline and the button rise into place as the band comes up — --ro is
   their opacity, --rv how far below their settled position they still are, as a
   multiple of the 4.4rem lift. motion.js drives both.

   The defaults here are the settled state (--ro 1, --rv 0), so with no JS both sit
   exactly where they are drawn and nothing is invisible; prefers-reduced-motion
   pins that same state. The button composes its lift after its own
   translateX(-50%), which is what centres it. */
.talk h2{position:absolute;left:0;right:0;top:18.68%;text-align:center;font-size:5.1rem;line-height:5.4rem;--ro:1;--rv:0;opacity:var(--ro);transform:translateY(calc(var(--rv) * 4.4rem));will-change:opacity,transform}
/* 44rem, not the 57.5rem of the layout export: the h2 above runs 19.5 → 35.7rem
   (three lines of 5.4), so this leaves an 8.3rem gap instead of 21.8 and the
   button reads as belonging to the question rather than floating under it. */
.talk .btn{position:absolute;left:50%;top:42.15%;--ro:1;--rv:0;opacity:var(--ro);transform:translateX(-50%) translateY(calc(var(--rv) * 4.4rem));will-change:opacity,transform}

/* ── Footer ─────────────────────────────────────────────────
   Inset is 11.1rem, not the page gutter: the artwork sets the footer text in
   from the header logo, and the same value is mirrored on the right (the
   artwork's own right inset drifts to 14.7rem, which reads as eyeballing). */
.site-foot{position:relative;height:41.8rem;background:var(--ink);color:#fff}
.site-foot>*{position:absolute;margin:0}
.foot-name{left:11.1rem;top:6.6rem;font-size:2.6rem;line-height:3rem}
.foot-mail{left:11.1rem;top:19.4rem}
.foot-addr{left:11.1rem;top:25.5rem;line-height:3.2rem}
.foot-logo{right:11.1rem;top:5.9rem;width:25.1rem;filter:invert(1)}
.foot-copy{right:11.1rem;top:28.1rem;text-align:right;line-height:3.2rem}

/* ── Mobile ─────────────────────────────────────────────────
   First mobile pass, 13 Sep 2026. The desktop layout is absolute rem geometry
   band by band, so this is not a matter of reflowing it — each band is put back
   into normal flow and given its own stacking order. One breakpoint at 780px:
   the design's two-column bands all depend on a column being wide enough for a
   64rem measure, and below 780 none of them are.

   THE ROOT SCALE IS THE FIRST THING TO CHANGE. Everything on the page is in rem
   off `html`'s font-size, and the desktop formula (0.5208vw) would put the body
   at 8px on a phone. Here it is 1.85vw, capped at 8.2px: body 16.6px at 390,
   17.9px at 430, and the display sizes below are re-set per band rather than
   left to scale, because a 6.2rem hero line that works at 1512 is not the same
   design decision at 390.

   WHAT GETS REORGANISED, AND WHY:
   - Hero: copy first, photo below it full-bleed, with the puzzle bite rotated
     onto its TOP edge so it still faces the text it belongs to.
   - How We Work: the photo moves above the steps (order:-1) — it is the left
     block on desktop, so it reads first either way.
   - Plans: the pinned rail becomes an ordinary block at the top of the band and
     the four cards stack under it on white. The three .paper panels go: their
     geometry is percentages of the column's WIDTH, which is meaningless once the
     column is the screen.
   - Our Services: the cards cannot sit on the shelf's bays at this size, so the
     photograph becomes a band of its own and the five cards stack underneath it
     in flow. The scroll-driven sky glow is replaced by a plain two-stop gradient
     — there is no room for it to be seen gathering.
   - Why GT: 2x2 instead of 4x1, and the hover travel is dropped (there is no
     hover on a phone). Both texts show at once and the orb stays as an arc
     rising from the bottom of each cell.
   - Let's Talk: the copy moves BELOW the room rather than over it. Overlaying it
     would need the band cropped to a portrait box, and the room is meant to be
     seen whole (#61).
   - Footer: one column, everything left-aligned. */
@media (max-width:780px){
  html{font-size:min(1.85vw,8.2px)}
  :root{--gut:6.4vw;
  /* The bite rotated a quarter turn onto the top edge: the asset's own path in a
     72.69x44.001 box, translated and rotated 90 so its flat edge and two tabs
     face upward. Sized off the box's WIDTH, as on desktop — the asset is
     44.001:72.69, so at this box's 1:1.15 aspect the bite is 52.6% of the box's
     height. The complementary solid layer overlaps it by 8px, same as #43. */
  --bite-top:url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2272.69%22%20height=%2244.001%22%20viewBox=%220%200%2072.69%2044.001%22%3E%3Cg%20transform=%22translate%2872.69%200%29%20rotate%2890%29%22%3E%3Cg%20transform=%22translate%2844.001%200%29%20scale%28-1%201%29%22%3E%3Cpath%20fill=%22%23000%22%20d=%22M44.001,70.524c0-2.022-1.808-3.556-3.805-3.239-1.615.256-3.271.39-4.958.39-17.533,0-31.747-14.214-31.747-31.747S17.705,4.181,35.238,4.181c1.687,0,3.343.134,4.958.39,1.997.317,3.805-1.217,3.805-3.239V0H0v72.69h44.001v-2.166Z%22/%3E%3C/g%3E%3C/g%3E%3C/svg%3E')}

  .site-head{position:static;padding:5rem 0 0}
  .site-head img{width:22rem}
  /* 6rem is 43px at this root scale — a pixel under the 44px minimum for a tap
     target, so every button gets a little taller here. */
  .btn{height:7rem;font-size:2.2rem}

  .hero{height:auto;padding:12rem 0 0}
  .hero-copy{position:static;width:auto;padding:0 var(--gut)}
  .hero h1{position:static;white-space:normal}
  .hero h1 .d-serif{font-size:5.6rem;line-height:5.8rem}
  .hero h1 .d-sans{font-size:4.6rem;line-height:5.4rem}
  .hero-body{position:static;width:auto;margin-top:4.5rem}
  .hero-body p+p{margin-top:3rem}
  .hero .btn{position:static;margin-top:5.4rem}
  /* No bite on mobile. The mask is a horizontal composition — a bare edge on one
     side, the puzzle tabs on the other — and rotated onto the top of a
     full-width photo it read as a scoop taken out of the picture rather than as
     a join with the copy. The photo is a plain rectangle here. */
  .hero-art{position:static;margin-top:9rem;width:100%;aspect-ratio:1/1.15;-webkit-mask:none;mask:none}
  /* No bite here, so no off-centring to correct: the photo is centred normally. */
  .hero-art img{position:static;width:100%;max-width:100%}

  .about{height:auto;padding:12rem 0 14rem;background:var(--pale)}
  .about-pale{display:none}
  .about-art{position:static;width:62%;height:auto;aspect-ratio:34.1/56.1;margin:0 auto 9rem}
  .about-copy{position:static;left:auto;top:auto;width:auto;padding:0 var(--gut)}
  .about h2 .d-serif{font-size:4.8rem;line-height:5.2rem}
  .about h2 .d-sans{font-size:4.2rem;line-height:5.2rem}
  .about-body{margin-top:5rem}

  .hww{height:auto;display:flex;flex-direction:column}
  .hww-art{position:relative;order:-1;left:auto;right:auto;top:auto;bottom:auto;width:100%;aspect-ratio:3/3.4}
  /* Up and smaller: the standfirst was landing on the man's head. */
  .hww-head{top:4rem}
  .hww-head h2{font-size:5.4rem}
  .hww-head p{margin-top:1.2rem;font-size:2.3rem;line-height:3rem}
  .hww-steps{position:static;left:auto;width:auto;padding:9rem var(--gut) 11rem}
  .hww-step,.hww-step:nth-child(1),.hww-step:nth-child(2),.hww-step:nth-child(3){position:static;top:auto;width:auto}
  .hww-step+.hww-step{margin-top:6.5rem}
  .hww-step h3{font-size:5rem}
  .hww-step p{font-size:2.3rem;line-height:3.4rem}

  .plans{display:block;background:var(--paper)}
  /* relative, not static: .rail-g/.rail-wash are inset:0 against the rail, and
     with the rail unpositioned they resolved against .plans instead and painted
     the sunset down the whole band behind the white cards. */
  .plans-rail{position:relative;height:auto;margin-bottom:0;display:block;padding:12rem var(--gut) 13rem}
  .plans-rail h2{font-size:6.4rem;line-height:7rem}
  .plans-rail p{width:auto;margin-top:4rem}
  .plans-list{overflow:visible;background:var(--paper)}
  .paper{display:none}
  .plan{min-height:auto;padding:11rem var(--gut)}
  .plan--first{padding-top:12rem}
  .plan:last-of-type{padding-bottom:14rem}
  .plan h3{font-size:4.8rem;line-height:5.4rem}
  .plan-price{font-size:3.6rem;line-height:4.4rem;white-space:normal}
  .plan-price em{font-size:2.3rem}
  .plan-lede,.plan-fine{width:auto}
  .plan-list{font-size:2.2rem;line-height:3.4rem}
  .plan-list li{gap:1.8rem}

  .svc{height:auto;padding-top:0;display:flex;flex-direction:column;background:linear-gradient(#4b7cc4,#88afef)}
  /* Symmetrical padding, so the heading is centred in its own band of sky
     rather than sitting high in a very tall one. */
  .svc h2{position:static;order:1;height:auto;display:block;padding:9rem var(--gut);font-size:5rem;line-height:5.6rem}
  /* The whole unit, not the desktop crop: that crop exists only to make the
     cards meet the viewport edges, and here they have left the photograph. */
  .svc-shelf{order:2;aspect-ratio:2912/1556;background-position:bottom center;background-size:100% auto}
  /* Black behind the cards, so the stack reads as one dark band under the
     photograph instead of five boxes floating on blue. */
  .svc-grid{position:static;order:3;aspect-ratio:auto;left:auto;right:auto;bottom:auto;display:flex;flex-direction:column;gap:3.5rem;padding:8rem var(--gut) 12rem;background:var(--ink)}
  .svc-card,.svc-card:nth-child(1),.svc-card:nth-child(2),.svc-card:nth-child(3),.svc-card:nth-child(4),.svc-card:nth-child(5){position:static;left:auto;top:auto;width:auto;height:auto;padding:4rem}
  .svc-card h3{font-size:3.4rem;line-height:3.9rem;overflow-wrap:break-word}
  /* The desktop titles carry a hard break each, set to balance them inside their
     bay; in a full-width stacked card that break lands mid-measure and costs a
     line, so it is dropped and the titles wrap on their own. Each break has a
     space in front of it in the HTML — collapsed at a line end on desktop, but
     the thing that keeps the two halves apart once the break is gone. Same for
     the Let's Talk headline below. */
  .svc-card h3 br{display:none}
  .svc-card p{margin-top:2rem;font-size:2.3rem;line-height:3.4rem}

  /* Title first, then the copy, then a half circle pinned to the bottom edge of
     the cell — the desktop travel has no meaning without hover, so the orb
     becomes a fixed device instead of a stalled animation. It is the cell's full
     width and sits at bottom:0 pushed down by half its own height, so exactly a
     dome shows and the cut is the cell's own edge; the cells are grid siblings,
     so all four domes line up. The bottom padding is that dome plus air. */
  .why{height:auto;grid-template-columns:repeat(2,1fr)}
  .why-col{display:flex;flex-direction:column;padding:8rem 3vw 18rem}
  /* No overflow-wrap here: it broke "Understanding" and "Commercially" across
     lines mid-word. The measure is widened and the type dropped instead, so the
     longest single word in the four titles — "Understanding", the one that sets
     this size — fits its line whole with a few pixels to spare. */
  .why-col h3,.why-col:nth-child(odd) h3,.why-col:nth-child(even) h3{order:1;position:static;top:auto;bottom:auto;width:auto;margin:0;font-size:2.4rem;line-height:2.9rem;overflow-wrap:normal;word-break:normal}
  .why-body,.why-col:nth-child(odd) .why-body,.why-col:nth-child(even) .why-body{order:2;position:static;top:auto;bottom:auto;width:auto;margin:2.2rem 0 0;font-size:2.1rem;line-height:3rem}
  .why-orb,.why-col:nth-child(odd) .why-orb,.why-col:nth-child(even) .why-orb{top:auto;bottom:0;left:0;width:100%;transform:translateY(50%);transition:none}
  .why-col:nth-child(odd):hover .why-orb,.why-col:nth-child(even):hover .why-orb{transform:translateY(50%)}

  /* The panel lights with the room. room.js already publishes its scrub position
     as --lit on .talk (0 dark, 1 fully lit), so the band interpolates black to
     white off the same number that drives the frames — the copy below the room
     comes up as the room does instead of sitting on a fixed ground. The button
     runs the other way, white on black to black on white, so it stays the
     panel's opposite throughout.

     THE MIDDLE OF THE RAMP IS SALMON, NOT GREY (14 Sep 2026: "dont like this
     gray in-between.. perhpa is could be our salmon color?"). A straight black
     to white mix passes through neutral grey, which belongs to nothing on this
     page. The ground is a two-stage mix instead: ink to --salmon over the first
     half of --lit, salmon to paper over the second. color-mix clamps its
     percentage, so each stage sits out the half that is not its own — the inner
     mix reaches salmon at 0.5 and stays there, the outer one does nothing until
     then.

     THE GROUND CROSSFADES, THE INK STEPS, AND IT STEPS AT 0.35. Fading both
     would put white type on a near-white ground halfway through. The flip is
     early rather than central because the mid-point here is salmon, a light
     colour: at 0.35 the ground is about #b8776a, where white is 3.6:1 and black
     5.8:1 — the crossover point where neither half is worse than the other. Any
     later and the white side drops under 3:1. The `* 10000%` is what makes that
     mix a step rather than a ramp.

     Defaults are the settled state: with no JS, --lit is unset, the fallback 0
     applies and the band is black with white type, which is what the desktop
     band is. prefers-reduced-motion pins --lit to 1. */
  .talk{aspect-ratio:auto;height:auto;display:flex;flex-direction:column;--flip:calc((var(--lit,0) - 0.35) * 10000%);background:color-mix(in srgb,var(--paper) calc((var(--lit,0) - 0.5) * 200%),color-mix(in srgb,var(--salmon) calc(var(--lit,0) * 200%),var(--ink)))}
  .talk-art{position:static;order:1;width:100%;aspect-ratio:1120/630;background:url(assets/room/f00.webp) center/100% 100% no-repeat}
  .talk h2{position:static;order:2;top:auto;padding:10rem var(--gut) 0;text-align:left;color:color-mix(in srgb,var(--ink) var(--flip),var(--paper));font-size:4.4rem;line-height:5.2rem;overflow-wrap:break-word}
  .talk h2 br{display:none}
  .talk .btn--light{background:color-mix(in srgb,var(--ink) var(--flip),var(--paper));color:color-mix(in srgb,var(--paper) var(--flip),var(--ink))}
  .talk .btn{position:static;order:3;left:auto;top:auto;margin:6rem var(--gut) 12rem;transform:translateY(calc(var(--rv) * 4.4rem))}

  .site-foot{height:auto;padding:10rem var(--gut) 12rem}
  .site-foot>*{position:static;left:auto;right:auto;top:auto;text-align:left}
  .foot-name{font-size:2.8rem;line-height:3.4rem}
  .foot-mail{margin-top:5rem}
  .foot-mail a{display:inline-block;padding:1.2rem 0}
  .foot-addr{margin-top:3rem;line-height:3.4rem}
  .foot-logo{margin-top:9rem;width:26rem}
  .foot-copy{margin-top:6rem;text-align:left}
}

/* ── Our Services, THE BENTO ──────────────────────────────────
   "i dont really like how these black boxes look on this image.. looks kind of
   too old school.. lets explore alternative option (while keeping this so we
   revert to it)" — then, once it was built, "lets use the new bento grid with
   services in the main page as well".

   14 Sep 2026. THIS IS WHAT index.html USES: its section carries
   class="svc svc--stacked". The bay layout above is untouched and still the
   revert path — index-services-bays.html is the same page without the class.

   WHAT CHANGES. The cards come off the bays entirely: the photograph goes back
   to the whole unit, uncropped, as a band of its own, and the five services sit
   under it as a bento — a four-column grid where the tiles take different
   widths: 2+1+1 across the top, 2+2 below. Nothing sits on the picture at all,
   which is what takes the old-school look off it; the boxes read as pasted-on
   because they are solid rectangles interrupting a photograph.

   Two earlier passes on this band are worth recording as rejected: hairline
   rules over each cell ("too old school with these grey dividers") — a rule per
   cell is a table's grammar, it draws the grid rather than the content — and
   then 01-05 counters in salmon. The tiles carry their own edges now, so neither
   is needed.

   The tiles are full bleed and butted together — no gutters, no padding around
   the block, no surface colour. Each tile is the band's own black; the only
   thing marking one is a 1px divider, which is the grid's background showing
   through a 1px gap, so the lines are internal only and the block runs to all
   four edges. Earlier passes on this band, all rejected: a hairline rule over
   each cell ("too old school with these grey dividers" — a rule per cell is a
   table's grammar, it draws the grid rather than the content), 01-05 counters in
   salmon, a salmon accent tile, and #111 tiles with 2.4rem gutters, which read
   as five cards floating on the band. No corner radius — nothing on this page
   has one.

   The outer tiles take --gut on their outer side, so the first and last columns
   of type line up with every other band on the page rather than with the screen
   edge.

   The type leaves vw for rem, since it is no longer sized to a bay.

   SCOPED TO DESKTOP. The block sits after the mobile one and its selectors are
   a class deeper, and media queries add no specificity — so unscoped it won the
   phone layout too and pushed three of the five services off the side. The
   min-width matches the mobile block's own 780px edge. */
@media (min-width:781px){
/* Cropped top AND bottom to the cabinet itself (14 Sep 2026: "lets cut the image
   here a bit so that it is aligned to the bold shelves and not these small
   things", then twice more — the dark brown base rail was the gap). Sampled down
   the render's own rows: the grey cabinet doors end at y 1535, then the dark
   brown base rail runs to 1568, a black shadow line to 1590, then the floor. The
   first two cuts stopped at 1590 and 1568, keeping the shadow and then the rail;
   the rail is the "dark brown gap section". The band now ends at **1535**, on the
   doors themselves. The top is the same 76 rows of sky the desktop crop already
   hid, so the box is 2912/1459 and the vertical position is 43.9%, which is where
   a background 173 rows taller than its box puts row 76 on the top edge. */
.svc--stacked .svc-shelf{aspect-ratio:2912/1459;background-position:center 43.9%;background-size:100% auto}
.svc--stacked .svc-grid{position:static;aspect-ratio:auto;display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:#2a2a2a;padding:0}
/* min-height, not a taller padding: the tiles are grid siblings, so a floor on
   each one sets the row height and every tile in that row grows with it. 44rem
   is roughly twice what the tallest paragraph needs, which is the air the band
   was missing (14 Sep 2026: "looks crumped now", then "taller"). The padding is
   asymmetric — 5.5rem over the title, 12.1rem under — so the type sits high in
   its tile and the extra height reads as room below it rather than as the block
   floating in the middle of a box. Tiles with short copy
   simply carry more space under it, which is the point of a bento. */
.svc--stacked .svc-card,
.svc--stacked .svc-card:nth-child(1),
.svc--stacked .svc-card:nth-child(2),
.svc--stacked .svc-card:nth-child(3),
.svc--stacked .svc-card:nth-child(4),
.svc--stacked .svc-card:nth-child(5){position:static;left:auto;top:auto;width:auto;height:auto;min-height:44rem;margin:0;padding:5.5rem 5.5rem 12.1rem;background:var(--ink);border:0}
.svc--stacked .svc-card:nth-child(1),
.svc--stacked .svc-card:nth-child(3),
.svc--stacked .svc-card:nth-child(5){grid-column:span 2}
/* The bento is ordered by how much copy each service carries, not by source
   order (14 Sep 2026: Operational Consulting "seems empty on grey text, perhaps
   we could use the on that has the most text there"). The three wide tiles take
   the three longest paragraphs — Technology, Business & Management, Business
   Development — and the two narrow ones take the two shortest, Market Research
   and Operational Consulting. Done with order rather than a change to the
   markup: the alt page is generated from index.html, so the two stay in step,
   and the reading order in the source is unchanged.

   Laid out: [Technology][Business & Management] / [Business Development]
   [Market Research][Operational] — the two narrow tiles on the right of the
   bottom row, under the wide tile above them. */
.svc--stacked .svc-card:nth-child(5){order:-1}
.svc--stacked .svc-card:nth-child(3){order:1}
.svc--stacked .svc-card:nth-child(2){order:2}
.svc--stacked .svc-card:nth-child(4){order:3}
.svc--stacked .svc-card:nth-child(5),
.svc--stacked .svc-card:nth-child(3){padding-left:var(--gut)}
.svc--stacked .svc-card:nth-child(1),
.svc--stacked .svc-card:nth-child(4){padding-right:var(--gut)}
.svc--stacked .svc-card h3{font-size:3rem;line-height:3.6rem}
/* Every title wraps on its own here except Operational Consulting, which keeps
   its hard break: two words on one line leave an awkward orphan in a narrow tile
   beside the wide ones. Written as a :not() rather than by restoring display on
   that one <br> — a br breaks the line because the UA sheet gives it
   display-outside:newline, so setting display:inline back on it takes the break
   away rather than returning it. */
.svc--stacked .svc-card:not(:nth-child(4)) h3 br{display:none}
/* The page's own body size. These were 2rem/3.2rem, set when the tiles were a
   three-column text grid, and read small against every other band — the serif is
   2.3rem/3.5rem in the hero, About and the plan ledes, all of it inherited from
   body. */
.svc--stacked .svc-card p{margin-top:2.65rem;font-size:2.3rem;line-height:3.5rem}
/* The wide tiles are two columns of grid, so their paragraphs would run to a
   90-character measure — far past the 60-75 the same serif keeps everywhere else
   on the page. Capped at 64rem — the same measure .plan-lede uses, so the two
   long-copy bands on the page read at one width — which still leaves clear space
   beside the type inside a two-column tile rather than running the full bleed. */
.svc--stacked .svc-card:nth-child(1) p,
.svc--stacked .svc-card:nth-child(3) p,
.svc--stacked .svc-card:nth-child(5) p{max-width:64rem}
}

.room-frames{display:none}

/* /sections */
