
  
  
     /* "BI as code" terminal window (Product carousel slide 2) */
  @font-face { font-family: 'Britti Medium'; src: url('../britti-medium.ttf') format('truetype'); font-weight: 500; font-display: swap; }
  img { -webkit-user-drag: none; -khtml-user-drag: none; user-select: none; -webkit-user-select: none; }   /* never let a drag lift a native image ghost (e.g. the title-screen Cloudy PNG) */
  :root {
    --bg:     #f6f8fa;
    --dot:    rgba(40, 55, 80, 0.13);
    --ink:    #3b4252;
    --ink-hi: #6b57d6;
    --line:   #e4e7ec;
    --card:   #ffffff;
  }
  * { box-sizing: border-box; }


  /* DESKTOP background visual (per the user): pixel-art purple space scene behind the transparent WebGL canvas. Scoped to :not(.mfill) so the mobile screen keeps its flat #1A1B25 fill. */

  /* #mfillBg's opaque #07060f fill + drifting CSS dots (z-index:-1) sit ABOVE the body bg and would hide the image — clear both on DESKTOP so the space scene shows through (mobile keeps the dotted starfield backdrop). */
  body:not(.mfill) #mfillBg { background: transparent; animation: none; }
  .gbStage { position: fixed; inset: 0; touch-action: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
  /* crawlable/accessible content layer — in the DOM (indexed + screen-reader readable) but visually hidden behind the 3D experience */
  #seo-content { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); clip-path: inset(50%); border: 0; }
  /* canvas renders at low internal res, upscaled nearest-neighbour → chunky pixels */
  .gbStage canvas:not(#navCloudy):not(#navLoader) {   /* the WebGL canvas fills the stage; the nav-Cloudy/loader overlay canvases are sized in JS, so exclude them (else this !important forces them to full-stage width → giant Cloudy) */
    display: block; width: 100% !important; height: 100% !important;
    image-rendering: pixelated; image-rendering: crisp-edges;
    touch-action: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;   /* no iOS double-tap magnifier / loupe over the D-pad */
  }
  /* crisp nav planet labels — DOM overlay on TOP of the pixelated canvas (positioned each frame from the planets' projected screen coords) */
  #navLabels { position: absolute; inset: 0; pointer-events: none; z-index: 6; overflow: hidden; }
  #navLabels .navLabel { position: absolute; left: 0; top: 0; will-change: transform; white-space: nowrap;
    font-family: 'Press Start 2P', monospace; font-weight: 400; line-height: 1.35; color: #fff; text-transform: uppercase;
    text-shadow: 0 0 0.35em rgba(0,0,0,0.85), 0 0.06em 0.1em rgba(0,0,0,0.9); }   /* pixel font matching the mobile planet grid; positioned each frame via transform (compositor-only, no layout) */
  /* HOVER-STATE EXPERIMENT: four L-bracket "corner chips" framing a planet, faded in with the pixel-resolve effect when Cloudy hovers it. DOM overlay (like #navLabels) tracking each planet's projected bounds. A 200×200 base box is transform-scaled per frame to the planet's on-screen size (compositor-only). */
  #navChipsWrap { position: absolute; inset: 0; pointer-events: none; z-index: 6; overflow: hidden; }
  #navChipsWrap .navChips { position: absolute; left: 0; top: 0; width: 200px; height: 200px; transform-origin: center center; will-change: transform; }   /* JS owns this transform (tracks the planet) → the pop/pulse animate the inner wrapper instead */
  #navChipsWrap .navChipInner { position: absolute; inset: 0; opacity: 0; transform-origin: center center; will-change: transform, opacity; }   /* opacity + scale driven per-frame in JS (pop + pulse), sharing the glow's sine so planet + chips pulse in unison */
  #navChipsWrap .navChip { position: absolute; width: 30px; height: 30px; box-sizing: border-box; }
  #navChipsWrap .navChip.tl { top: 0; left: 0; border-top: 9px solid #9d8dff; border-left: 9px solid #9d8dff; }
  #navChipsWrap .navChip.tr { top: 0; right: 0; border-top: 9px solid #9d8dff; border-right: 9px solid #9d8dff; }
  #navChipsWrap .navChip.bl { bottom: 0; left: 0; border-bottom: 9px solid #9d8dff; border-left: 9px solid #9d8dff; }
  #navChipsWrap .navChip.br { bottom: 0; right: 0; border-bottom: 9px solid #9d8dff; border-right: 9px solid #9d8dff; }
  @keyframes chipPixIn { 0% { opacity: 0; filter: url(#px-lg-a); } 30% { opacity: 1; filter: url(#px-lg-a); } 60% { filter: url(#px-lg-b); } 85% { filter: url(#px-lg-c); } 100% { opacity: 1; filter: none; } }
  /* nav Cloudy + dwell-ring as DOM overlays ABOVE the labels (z 6) — the WebGL Cloudy sat behind the DOM labels; these sit in front */
  #navCloudy { position: absolute; left: 0; top: 0; z-index: 8; pointer-events: none; image-rendering: pixelated; will-change: transform; filter: drop-shadow(0 2px 6px rgba(143,128,255,0.26)); display: none; }
  #navCloudy.on { display: block; }
  #navLoader { position: absolute; left: 0; top: 0; z-index: 9; pointer-events: none; image-rendering: pixelated; will-change: transform; display: none; }
  #navLoader.on { display: none; }   /* LOAD WHEEL REMOVED (per the user): the 3D-nav dwell ring never renders */

  /* ---- control panel ---- */
  #panel {
    position: fixed;
    left: 16px; bottom: 16px;
    width: 224px;
    padding: 11px 12px 10px;
    background: rgba(255,255,255,0.85);
    backdrop-filter: blur(8px);
    border: 1px solid var(--line);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(30,40,60,0.10);
    user-select: none;
  }
  #panel .panelBody { max-height: calc(100vh - 90px); overflow-y: auto; overflow-x: hidden; }   /* the control list outgrew the viewport — scroll it (toggle stays pinned above) */
  #panelToggle { display: block; width: 100%; margin: 0 0 9px; padding: 6px 9px; text-align: left;
    font: 700 10px/1 'Inter', sans-serif; letter-spacing: 0.12em; color: #5b6b7f; text-transform: uppercase;
    background: rgba(120,110,220,0.10); border: 1px solid var(--line); border-radius: 8px; cursor: pointer; }
  #panel.collapsed { width: auto; padding: 8px; }
  #panel.collapsed #panelToggle { margin: 0; }
  #panel.collapsed .panelBody { display: none; }
  #panel .pageBtns { margin-top: 7px; padding-top: 9px; border-top: 1px solid var(--line); }
  #panel .pageBtns button { color: #6a4bd4; }   /* page jumps read distinct from the render controls */
  /* the retired Data Apps dev panel (#daPanel) is display:none !important below — its chrome/layout CSS was dead, removed. */
  #panel h1 {
    margin: 0 0 2px;
    font-size: 9.5px; letter-spacing: 1.5px; text-transform: uppercase;
    color: var(--ink-hi);
  }
  #panel .sub { margin: 0 0 9px; color: #98a0ae; font-size: 8px; letter-spacing: 0.8px; }
  .row { margin: 8px 0; }
  .row.h { display: none; }   /* hidden DEV rows still in the DOM so their JS wiring reads the tuned values */
  .row label {
    display: flex; justify-content: space-between;
    text-transform: uppercase; letter-spacing: 0.8px;
    font-size: 8.5px; color: #7a828f; margin-bottom: 4px;
  }
  .row label b { color: var(--ink); font-weight: 600; }
  input[type=range] {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: 3px; border-radius: 2px;
    background: var(--line); outline: none; cursor: pointer;
  }
  input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 13px; height: 13px; border-radius: 50%;
    background: var(--ink-hi); border: 2px solid #fff;
    box-shadow: 0 1px 4px rgba(107,87,214,0.5);
  }
  .btns { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; }
  .btns button {
    padding: 5px 8px;
    background: #fff; color: #7a828f;
    border: 1px solid var(--line); border-radius: 6px;
    font: inherit; font-size: 8.5px; letter-spacing: 0.5px;
    text-transform: uppercase; cursor: pointer; transition: .15s;
  }
  .btns button:hover { color: var(--ink); border-color: #cfd4dc; }
  .btns button.on { color: var(--ink-hi); border-color: var(--ink-hi); }
  .hint {
    position: fixed; top: 18px; right: 22px; text-align: right;
    font-size: 10px; letter-spacing: 1px; color: #aab0bb;
  }
  .hint b { color: #6b7280; }

  /* the DATA APPS dev panel (bottom-right) is retired — hidden, but kept in the DOM so its layout controls still initialise their CSS vars on load */
  #daPanel { display: none !important; }
  /* DEV panel (bottom-left): dark theme */
  #panel { background: rgba(15,13,24,0.9); border-color: rgba(255,255,255,0.14); box-shadow: 0 10px 30px rgba(0,0,0,0.45); }
  #panelToggle { color: #cfd4ff; background: rgba(140,130,235,0.20); border-color: rgba(255,255,255,0.14); }
  #panel h1 { color: #cfd4ff; }
  #panel .sub { color: #8b93a6; }
  #panel .row label { color: #99a1b2; }
  #panel .row label b { color: #fff; }
  #panel input[type=range] { background: rgba(255,255,255,0.16); }
  #panel .btns button { background: rgba(255,255,255,0.06); color: #aab2c0; border-color: rgba(255,255,255,0.14); }
  #panel .btns button:hover { color: #fff; border-color: rgba(255,255,255,0.30); }
  #panel .btns button.on { color: #cfc0ff; border-color: #8f7bff; }
  #panel .pageBtns { border-top-color: rgba(255,255,255,0.14); }
  #panel .pageBtns button { color: #b9a6ff; }

  /* ---- in-screen UI overlay (locked onto the Game Boy screen) ---- */
  /* container-query units → one spacing scale that scales with the screen, equal padding everywhere */
  .gbScreen {
    position: fixed; left: 0; top: 0; width: 0; height: 0;
    pointer-events: none; opacity: 0; overflow: hidden;
    border-radius: 6cqh;
    transition: opacity .5s steps(7);   /* chunky pixel-style fade-in */
    container-type: size;
    color: #ece9ff;
    font-family: 'Inter', system-ui, sans-serif;
    z-index: 5;
    /* brand-tinted palette (neutrals lean toward the cartridge purple, never pure black) */
    --acc:    #9d8dff;
    --ink:    #ece9ff;
    --muted:  #9990c4;
    --surface: rgba(19,16,38,0.88);
    --surface-hi: rgba(46,40,90,0.92);
    --line:   rgba(158,146,255,0.28);
    --panel-bg: #0d0b1a;
    /* spacing scale (container-relative) */
    --gap: 2cqh; --pad: 3.6cqh; --radius: 2.8cqh;
  }
  .gbScreen.on { opacity: 1; }   /* container lets drags fall through to the canvas so you can tilt; only the interactive bits below catch pointers */
  .gbScreen.on .tile, .gbScreen.on .vclose, .gbScreen.on .cta, .gbScreen.on .daTab, .gbScreen.on .daArrow, .gbScreen.on .daPdf, .gbScreen.on .pcarArrow { pointer-events: auto; touch-action: manipulation; }   /* manipulation → no iOS double-tap-to-zoom on a section */
  #boot .boot-start { touch-action: manipulation; }
  .gbScreen.edge, #boot.edge { opacity: 0 !important; pointer-events: none !important; }
  .gbScreen .view {
    position: absolute; inset: 0;
    padding: var(--pad);
    display: flex; flex-direction: column; gap: var(--gap);
    background: transparent;   /* let the real (pixelated, correctly-rounded) Game Boy screen show through — no mismatched dark panel */
    opacity: 0; transform: scale(0.965);
    transition: opacity .26s ease, transform .34s cubic-bezier(.2,.8,.25,1);
  }
  .gbScreen .view.show { opacity: 1; transform: none; }
  .gbScreen .vhead {
    display: flex; align-items: center; justify-content: space-between;
    padding: 1cqh 0.4cqh 0;   /* title sits near the top */
  }
  .gbScreen .vhead::before { content: ''; flex: 0 0 10.6cqh; }   /* spacer MUST equal the ✕ box width (.vclose 10.6cqh) → the title centres across the full header. Was 6cqh (stale after the ✕ grew 9.2→10.6cqh), which shifted every centred title left. */
  .gbScreen .vtitle { position: relative; z-index: 7; flex: 1; text-align: center; font-family: 'Britti Medium', 'Inter', sans-serif; font-size: 4.4cqh; font-weight: 500; letter-spacing: -0.01em; line-height: 1.1; color: #fff; }   /* centred, above the dither → crisp; tight line-height for wrapped titles */
  .gbScreen .pcarTitles { position: relative; z-index: 7; flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 0.5cqh; }   /* Product carousel: per-slide title + supporting line, stacked + centred in the vhead */
  .gbScreen .pcarTitle { flex: none; }   /* override .vtitle's flex:1 so the name doesn't stretch inside the column */
  .gbScreen .pcarSubRow { display: contents; }   /* desktop: transparent wrapper — subtitle + (hidden) LEARN MORE flow as normal centred children. Mobile turns it into a flex row (subtitle | button) so the TITLE above can span full width. */
  .gbScreen .pcarSub { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 400; font-size: 2.8cqh; line-height: 1.25; letter-spacing: -0.005em; color: #F8FAFB; text-align: center; max-width: 96%; }   /* title sub-text: #F8FAFB, regular weight (per the user) */
  .gbScreen .vtitle span { color: var(--acc); }
  .gbScreen .vtitle .tdot { color: #fff; }   /* the trailing full stop: lives INSIDE the accent span (so a mobile display:block span can't push it to its own line) but stays white — higher specificity than `.vtitle span` so it beats the accent colour */
  .gbScreen .vsub { position: relative; z-index: 7; max-width: 80%; margin: -1.6cqh auto 0; text-align: center; font-family: 'Britti Medium', 'Inter', sans-serif; font-size: 2.8cqh; font-weight: 400; line-height: 1.4; color: #F8FAFB; }   /* hero subtitle under the view title — #F8FAFB, regular weight (per the user) */
  /* Migration + Customers heroes: title left-aligned (drop the centering spacer + left pad) */
  .gbScreen .migview .vhead::before, .gbScreen .custview .vhead::before { display: none; }
  .gbScreen .migview .vhead, .gbScreen .custview .vhead { padding-left: 0; }
  .gbScreen .migview .vhead { padding-left: calc(3% - var(--pad)); }   /* align the title's left edge with the cards below (migRow is inset 3%) */
  .gbScreen .migview .vtitle, .gbScreen .custview .vtitle { text-align: left; }
  .gbScreen .migview .vtitle { font-size: 8cqh; }   /* migration title — snapped to grid */
  .gbScreen .migview .vsub { text-align: left; max-width: 54%; margin-left: 0; margin-right: auto; }   /* narrower subtext */
  .gbScreen .daview .vtitle, .gbScreen .govview .vtitle, .gbScreen .custview .vtitle { font-size: 4.9cqh; margin-top: -0.7cqh; }   /* single-line hero titles: slightly bigger + nudged up */
  body:not(.mfill) .gbScreen .govview .vsub { margin-top: -4.5cqh; max-width: 100%; line-height: 1.25; letter-spacing: -0.005em; }   /* DESKTOP governance: match the Product page sub styling (per the user — line-height 1.25 + tracking) and pull it TIGHT under the title. The ✕ makes the centred .vhead taller than the title text, so the sub sat with a big gap; -4.5cqh closes it. max-width:100% so the one-line sub fits without wrapping. */
  .gbScreen .govSubBrM, .gbScreen .govSubBrD { display: none; }   /* both governance-sub breaks off by default */
  body:not(.mfill) .gbScreen .govSubBrD { display: block; }   /* DESKTOP: 2-line sub, break AFTER "context layer" (per the user) */
  .mfill .gbScreen .govSubBrM { display: block; }   /* MOBILE: break AFTER "powered" so line 1 = "Every chart, dashboard, app is powered"; the vsub max-width below caps every other line to that same length (per the user) */
  /* The taller 2-line sub shortens the flex:1 govStage, which pushed the centred Revenue hub down onto the bottom cards' titles. Scaling govStage does NOT help: about any origin, a scale multiplies the hub↔card gap by s, so a negative (overlapping) gap only becomes a smaller negative — never positive. The overlapping element IS the hub, so COMPACT the hub instead (below) to shrink its vertical footprint and restore clearance. */
  body:not(.mfill) .gbScreen .govSpine { gap: 0.9cqh; padding: 1.1cqh 2.2cqh; }
  body:not(.mfill) .gbScreen .gsMetric { padding-bottom: 0.7cqh; }
  body:not(.mfill) .gbScreen .gsDesc { line-height: 1.2; }
  .gbScreen .vclose {
    position: relative; z-index: 50;   /* above everything so it's always prominent */
    width: 10.6cqh; height: 10.6cqh; flex: none; align-self: flex-start; margin-top: 0.7cqh;   /* TOP-anchored (not centred) so it sits in the SAME spot on every page regardless of title height — nudged up a little (across all 5 screens). DESKTOP box bumped 9.2→10.6cqh (mobile has its own .mfill override) */
    display: flex; align-items: center; justify-content: center;
    font-size: 5.1cqh;   /* DESKTOP ✕ glyph a little bigger (4.4→5.1cqh) per the user */
    background: transparent; color: #fff;
    border: none; border-radius: 1.6cqh;   /* no resting outline — just the ✕ glyph (hover/arming still fill a box for feedback) */
    cursor: pointer; transition: .15s; line-height: 1;
  }
  @media (hover: hover) { .gbScreen .vclose:hover { background: transparent; color: #fff; transform: scale(1.18); } }   /* no filled box — just the ✕ nudges bigger; hover:hover → desktop only, so touch never sticks the state */
  html.gecko .gbScreen .vclose { transform-origin: 100% 0; }   /* FIREFOX ✕ fix: .gbScreen is overflow:hidden + border-radius:6cqh, and the ✕ sits in that rounded top-right corner. Gecko clips transform-scaled descendants to the ancestor's border-radius (Chrome doesn't), so the centre-origin hover/arming scale pushed the glyph INTO the corner curve and clipped it. Anchoring the scale to the top-right corner grows it DOWN-LEFT into the screen instead → never clipped. Rest position (scale 1) is unchanged. Scoped to Gecko so Chrome's tuned centre-origin scale is untouched. */
  .gbScreen .vclose.arming { background: transparent; color: var(--acc); transform: scale(1.25); }   /* Cloudy is dwelling on it → the ✕ glows purple + grows (no box) */

  /* bento accordion — landscape tiles that smoothly resize; an expanded tile widens inside its row (which grows tall) */
  .gbScreen .bento { flex: 1; display: flex; flex-direction: column; justify-content: center; gap: var(--gap); min-height: 0; }
  .gbScreen .brow { flex: 1 1 0; max-height: 15.2cqh; display: flex; gap: var(--gap); min-height: 0;
    transition: flex-grow .38s cubic-bezier(.4,.02,.2,1); }
  .gbScreen .brow.activerow { flex-grow: 5.4; max-height: none; }        /* active row grows big to house the full story */
  .gbScreen .bento > .brow.logorow:not(.activerow) { max-height: 34cqh; }   /* logo rows (JustWatch/Octopus, Adobe/ServiceNow): taller/square; the STAT rows (fal/Workday, Kraken/OakNorth) stay short bars that hug their content */
  .gbScreen .tile {
    flex-grow: var(--fg, 1); flex-shrink: 1; flex-basis: 0;
    min-width: 0; position: relative; overflow: hidden;
    background: transparent;
    display: flex; padding: var(--pad);
    --px: 4px;
    clip-path: polygon(0 var(--px), var(--px) var(--px), var(--px) 0, calc(100% - var(--px)) 0, calc(100% - var(--px)) var(--px), 100% var(--px), 100% calc(100% - var(--px)), calc(100% - var(--px)) calc(100% - var(--px)), calc(100% - var(--px)) 100%, var(--px) 100%, var(--px) calc(100% - var(--px)), 0 calc(100% - var(--px)));   /* single square divot per corner */
    transition: flex-grow .38s cubic-bezier(.4,.02,.2,1);
  }
  /* the pixel frame: a hollow stepped ring via clip-path:path() (a TRUE two-subpath ring → all four edges complete, transparent interior so the starfield shows through). the path is computed per-tile in JS (updateTframes) since path() needs px coords; iOS-reliable */
  .gbScreen .tframe { position: absolute; inset: 0; z-index: 7; pointer-events: none; background-color: rgba(226, 222, 255, 0.72); }   /* strokes stay on screen through a resize (only the content fades) */
  /* Agents page: a TOGGLE BAR of platforms; the selected one's big 3D screenshot card renders below (in the WebGL scene) */
  /* VERTICAL tab column — consistent rhythm: box padding == gap, every tab full-width with equal inner padding */
  .gbScreen .agentTabs { position: absolute; left: var(--atabs-x, 13.5%); top: var(--atabs-y, 16.4cqh); transform: scale(var(--atabs-scale, 1.14)); transform-origin: 0 0; z-index: 3;
    display: flex; flex-direction: column; align-items: stretch; gap: 2cqh; padding: 2.4cqh; min-width: 34cqh; border-radius: 0.8cqh; background: var(--surface); pointer-events: auto;
    box-shadow: 0.4cqh 0.8cqh 0 rgba(8,6,16,0.4), 1.2cqh 1.6cqh 0 rgba(8,6,16,0.22), 2cqh 2.4cqh 0 rgba(8,6,16,0.1); }   /* customer-story look: dark surface + pixel frame; stepped pixel shadow */
  .gbScreen .agentTabs .tframe { background-color: var(--acc); z-index: 0; }   /* purple pixel-stepped ring (same as an open customer story) */
  .gbScreen .agentTab { display: flex; align-items: center; justify-content: flex-start; width: 100%; box-sizing: border-box; padding: 1.6cqh 2.4cqh; border: 0; background: transparent;
    border-radius: 0; cursor: pointer; font-family: inherit; font-weight: 700; font-size: 3.2cqh; color: #fff; line-height: 1; text-align: left;
    opacity: 0; transform: translateX(-1.2cqh);   /* hidden until Cloudy flies over to reveal it */
    transition: background .16s ease, color .16s ease, opacity .3s ease, transform .3s ease; touch-action: manipulation; white-space: nowrap; }
  .gbScreen .agentTab.revealed { opacity: 1; transform: none; }
  .gbScreen .agentTab { position: relative; z-index: 1; outline: none; -webkit-tap-highlight-color: transparent; }   /* sit above the tframe ring; no rounded focus/tap ring flashing on click */
  .gbScreen .agentTab.sel { background: var(--surface-hi); color: #fff; border-radius: 0; }   /* selected = lifted purple surface (matches the story hover fill); square corners */
  /* Migration page: three "pick your starting point" boxes — reuse the agents panel frame styling (dark surface + purple pixel tframe + stepped shadow); box padding == gap, equal inner rhythm */
  .gbScreen .migRow { position: absolute; left: 3%; width: 94%; bottom: 4.2cqh; z-index: 3;   /* bottom gap matches the title's top gap (view pad 3.6cqh + vhead pad 1cqh) */
    display: flex; flex-direction: column; align-items: center; gap: 2.4cqh; }   /* shared header sits above the box row (bottom-aligned) */
  .gbScreen .migBoxes { width: 100%; display: flex; align-items: flex-end; justify-content: center; gap: 1.6cqh; }
  .gbScreen .migBox { position: relative; flex: 1 1 0; min-width: 0; aspect-ratio: 1 / 1.44; box-sizing: border-box;
    padding: 2.4cqh; border-radius: 0.8cqh; background: var(--surface);
    box-shadow: 0.8cqh 0.8cqh 0 rgba(8,6,16,0.4), 1.6cqh 1.6cqh 0 rgba(8,6,16,0.22), 2.4cqh 2.4cqh 0 rgba(8,6,16,0.1); }
  .gbScreen .migBox .tframe { background-color: var(--acc); z-index: 0; }   /* purple pixel-stepped ring, same as the agents/customer panels */
  /* top-left: tool logo, then its supporting text underneath */
  .gbScreen .migHead { position: absolute; z-index: 1; top: 2.8cqh; left: 2.8cqh; right: 2.8cqh;
    display: flex; flex-direction: column; align-items: flex-start; gap: 2.8cqh; }
  .gbScreen .migTool { display: flex; align-items: center; min-height: 4cqh; gap: 1.6cqh; font-family: 'Inter', sans-serif; font-weight: 600; font-size: 3.6cqh; line-height: 1; color: #fff; white-space: nowrap; }   /* icon + tool name (in Inter) */
  .gbScreen .mtoolLogo { height: 3.6cqh; width: auto; flex: 0 0 auto; object-fit: contain; }   /* NO filter — these logos are already white-wordmark + a white app-icon tile, designed for dark backgrounds */
  .gbScreen .mtoolLogo.wordmark { height: 4cqh; }   /* full name-included lockup (e.g. Power BI) */
  .gbScreen .mtoolSvg { display: inline-flex; flex: 0 0 auto; }   /* inline (editable) icon markup — e.g. the Looker mark */
  .gbScreen .mtoolSvg svg { height: 3.6cqh; width: auto; display: block; }
  .gbScreen .migCopy { max-width: 100%; text-align: left; font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 400; font-size: 2.4cqh; line-height: 1.4; color: #F8FAFB; }   /* card subtext — #F8FAFB, regular weight (per the user); bold <b> spans keep their own 700/color */
  /* bottom-right: the abductable customer pills */
  .gbScreen .migFoot { position: absolute; z-index: 1; bottom: 3.8cqh; right: 2.4cqh; left: 2.4cqh;
    display: flex; flex-direction: column; align-items: stretch; gap: 1.6cqh; }   /* lifted up into the empty space above → the "Migrated" eyebrow sits higher and the icon grid gets more room */
  /* per-card "Migrated to Lightdash" eyebrow above the pills: label + a line stretching to the card's far edge to divide the space above/below */
  .gbScreen .migEyebrow { display: flex; align-items: center; gap: 1.6cqh; width: 100%; margin-bottom: 0.8cqh; }
  .gbScreen .migEyebrow .mel { font-family: 'Inter', sans-serif; font-weight: 400; font-size: 2cqh; letter-spacing: 0.05em; text-transform: uppercase; color: var(--acc); white-space: nowrap; }   /* purple, matching the card border; lighter weight */
  .gbScreen .migEyebrow .meline { flex: 1 1 auto; height: 1px; background: rgba(255,255,255,0.18); }
  /* ---- Pricing page: three tiers reusing the migration card frame 1:1 ---- */
  .gbScreen .priceview .vtitle { display: block; }
  .gbScreen .priceview .ptMain { font-size: 5.4cqh; white-space: nowrap; line-height: 1.05; }   /* bumped larger than the Migration title — the lede now wraps to two lines so there's room for a bigger headline */
  .gbScreen .priceview .priceLede { display: block; font-family: inherit; font-weight: 400; font-size: 5.4cqh; letter-spacing: -0.015em; color: #F8FAFB; margin-top: 0.6cqh; max-width: 100%; line-height: 1.12; }   /* pricing sub-lede — #F8FAFB, regular weight (per the user) */
  .gbScreen .priceview .mel { color: #fff; text-transform: none; }    /* tier eyebrows in WHITE (was purple), Title Case not ALL CAPS (source is "Open Source" / "Cloud Pro" / "Enterprise") */
  .gbScreen .priceview .meline { display: none; }                     /* drop the divider line after each eyebrow */
  .gbScreen .priceview .migEyebrow { margin-bottom: 1.8cqh; }   /* breathing room between the tier eyebrow and the title/copy below (title↔copy stays tight via the small migHead gap) */
  .gbScreen .priceview .migHead { gap: 1.2cqh; }                      /* close the gap between the title and its subtext */
  .gbScreen .priceTitle { font-size: 4.6cqh; white-space: nowrap; line-height: 1; align-items: baseline; gap: 0.6cqh; }   /* bigger tier titles; baseline-align so the unit sits beside the number, not centred */
  .gbScreen .priceUnit { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 500; font-size: 2.4cqh; letter-spacing: 0; color: rgba(255,255,255,0.55); margin-left: 0; }
  .gbScreen .priceview .migCopy b { color: #fff; font-weight: 700; }
  .gbScreen .priceBox { background: rgba(5,4,12,0.86); overflow: hidden; }   /* stays near-black always — no purple hover fill; only Cloudy + his green dithered glow appear when he's inside. overflow clips the glow (Cloudy is clipped separately via clip-path) */
  .gbScreen .priceGlow { position: absolute; left: 50%; top: 60%; width: 132%; aspect-ratio: 1; transform: translate(-50%, -50%); z-index: 0; pointer-events: none; opacity: 0; transition: opacity .3s ease; image-rendering: pixelated;
    background: var(--price-glow) center / 100% 100% no-repeat; }   /* EXACT same Bayer-dithered radial halo as the nav planet hover glow (baked in green) */
  .gbScreen .priceBox.lit .priceGlow { opacity: 1; }
  .gbScreen .priceview .migRow { z-index: auto; }   /* drop the row's stacking context so the tframe below can actually rise above screen-level Cloudy */
  .gbScreen .priceBox .tframe { z-index: 13; }   /* the card's purple stroke renders OVER Cloudy (z-12) so it overlaps him — clean "inside the frame" look */
  .gbScreen .priceview .migHead { z-index: 6; }   /* tier text sits BELOW Cloudy (z-12) so he renders IN FRONT of it as he hovers a card (per the user) */
  .gbScreen .priceview .migFoot { z-index: 6; bottom: 2.4cqh; }   /* CTA sits BELOW Cloudy (he hovers in FRONT of it) but above the glow/bg; still clickable since Cloudy is pointer-events:none. bottom gap = the 2.4cqh side gaps */
  .gbScreen .priceview .priceCta { opacity: 1; }   /* beat the base `.cta { opacity: 0 }` (which sits later in source) so the tier CTAs are actually visible */
  .gbScreen .priceBox--feature .tframe { background-color: #b3a4ff; }
  .gbScreen .priceCta { position: static; opacity: 1; width: 100%; max-width: 100%; box-sizing: border-box; justify-content: center;
    background: rgba(255,255,255,0.10); color: #fff; border: 1px solid var(--acc); border-radius: 0;
    padding: 2.2cqh 0.6cqh; font-size: 1.4cqh; letter-spacing: 0.02em; line-height: 1.45; white-space: normal; overflow-wrap: anywhere; }   /* Press Start 2P is wide → 1.4cqh + wrap + max-width keeps the button inside the card on resize (no overflow past the frame) */
  @media (hover: hover) { .gbScreen .priceCta:hover { background: var(--acc); color: #17122e; border-color: var(--acc); } }
  .gbScreen .priceCta.hot { background: var(--acc); color: #17122e; border-color: var(--acc); }
  .gbScreen .priceBox--feature .priceCta { background: #fff; color: #17122e; border-color: #fff; }
  @media (hover: hover) { .gbScreen .priceBox--feature .priceCta:hover { background: #ece9ff; } }
  .gbScreen .priceBox--feature .priceCta.hot { background: #ece9ff; }
  /* pricing CTA hover/dwell → soft light-grey/off-white, NOT the generic .cta black (per the user). `.priceview` bumps specificity above both `.cta.hot`(535/536) AND the feature `#ece9ff` above, so all three tiers get the same off-white with dark text. */
  @media (hover: hover) { .gbScreen .priceview .priceCta:hover { background: #e6e6ec; color: #17122e; border-color: #e6e6ec; } }
  .gbScreen .priceview .priceCta.hot { background: #e6e6ec; color: #17122e; border-color: #e6e6ec; }
  .gbScreen .migLogos { display: flex; flex-direction: column; flex-wrap: nowrap; align-items: center; justify-content: flex-end; width: 100%; gap: 1.8cqh; }   /* the three customer logo+name units stack down the footer (full card width each → the pixel-font name fits on one line) */
  /* customer LOGO (white mark) with its name below in the pixel font */
  .gbScreen .mpill { width: 100%; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 0.5cqh;
    transition: opacity .9s steps(8); }   /* RETURN fade-in (stepped/retro); the 5s wait is handled in JS via the .consumed state */
  .gbScreen .mpillLogo { height: 6.4cqh; max-width: 72%; object-fit: contain; }   /* white logo mark (already white) */
  .gbScreen .mpillLogo.wordmark { height: 4.4cqh; max-width: 88%; }   /* Octopus Energy is a full wordmark (name baked in) → shorter, no pixel name under it */
  .gbScreen .mpillName { font-family: 'Press Start 2P', monospace; font-size: 1.4cqh; line-height: 1.3; color: #fff; text-align: center; letter-spacing: 0; white-space: nowrap; text-transform: uppercase; }   /* company name in the project's pixel font, ALL CAPS */
  /* 4-logo box (Tableau): pack the units into a 2×2 grid instead of a tall column */
  .gbScreen .migLogos--grid { flex-direction: row; flex-wrap: wrap; align-items: flex-start; align-content: center; justify-content: center; gap: 3.6cqh 2.4cqh; }   /* 2-col grid for every box → all flush: 4 logos = 2×2, 3 logos = 2 on top + 1 centred below. Row-gap widened so the two icon rows breathe. */
  .gbScreen .migLogos--grid .mpill { width: 42%; gap: 0.5cqh; }
  .gbScreen .migLogos--grid .mpillLogo { height: 6.2cqh; }
  .gbScreen .migLogos--grid .mpillName { font-size: 1.2cqh; white-space: normal; line-height: 1.3; }   /* smaller + wraps so long names ("Octopus Energy") fit the narrower grid cell */
  body:not(.mfill) .gbScreen .migLogos--grid .mpillName { min-height: 2.6em; display: flex; flex-direction: column; justify-content: center; }   /* DESKTOP ONLY: reserve 2 lines for every label so all logo grids are equal height → the "MIGRATED TO LIGHTDASH" eyebrows sit flush across the side-by-side cards. NOT on mobile — cards stack there, so the extra height only overflowed the Power BI card (longest copy) and ate its bottom padding (per the user) */
  /* ===== Cloudy ABDUCTION: pills float UP into the beam, pixel-DISSOLVING as they rise (none → fine → chunky, the reveal filters in reverse), staggered + slow; fade straight back IN PLACE when he leaves ===== */
  /* --tx / --ty (set per-pill each frame) = LIVE offset from the pill to Cloudy. The pill interpolates along the line to him, so it rides the beam to his ACTUAL position — if he dives below it, the pill follows down and stays in the beam instead of overshooting up */
  @keyframes pillAbduct {
    0%   { opacity: 1;    transform: translate(0, 0) scale(1) rotate(0deg);                                                filter: none;      color: #fff;           border-color: rgba(255,255,255,0.85); }
    20%  { opacity: 1;    transform: translate(calc(var(--tx,0) * 0.2), calc(var(--ty,0) * 0.2)) scale(0.9) rotate(-6deg);   filter: none;      color: #fff;           border-color: rgba(255,255,255,0.85); }
    40%  { opacity: 1;    transform: translate(calc(var(--tx,0) * 0.4), calc(var(--ty,0) * 0.4)) scale(0.8) rotate(6deg);    filter: none;      color: #d6ffe9;        border-color: rgba(150,250,195,0.9); }
    60%  { opacity: 0.95; transform: translate(calc(var(--tx,0) * 0.6), calc(var(--ty,0) * 0.6)) scale(0.68) rotate(-5deg);  filter: url(#px-c); color: rgb(90,244,165); border-color: rgba(90,244,165,0.9); }
    80%  { opacity: 0.6;  transform: translate(calc(var(--tx,0) * 0.8), calc(var(--ty,0) * 0.8)) scale(0.56) rotate(4deg);   filter: url(#px-b); color: rgb(74,240,150); border-color: rgba(74,240,150,0.85); }
    100% { opacity: 0;    transform: translate(var(--tx,0), var(--ty,0)) scale(0.44) rotate(-2deg);                          filter: url(#px-a); color: rgb(74,240,150); border-color: rgba(74,240,150,0.85); }
  }
  .gbScreen .migBox.abducted .mpill { animation: pillAbduct 3.4s steps(16) forwards; animation-delay: calc(var(--pi, 0) * 0.28s); }   /* slower + stepped (old-game cadence, more frames so it's not laggy) */
  .gbScreen .migBox.consumed .mpill { opacity: 0; }   /* stays gone (NO animation) after consumption; removing this class transitions opacity 0→1 → a real fade-back (not a pop) */
  /* each platform's icon as a floating flat tile — position + size fully dev-controlled (no drop shadow) */
  .gbScreen .agentTile { position: absolute; z-index: 6; left: var(--atile-x, 86%); top: var(--atile-y, 44cqh); width: var(--atile-size, 14.5cqh); height: var(--atile-size, 14.5cqh); border-radius: 24%;
    background: transparent; transform: translate(-50%, -50%) rotate(4deg); pointer-events: none;
    display: flex; align-items: center; justify-content: center; animation: atileHover 3.6s steps(9) infinite; }   /* subtle right-tilt + CHUNKY stepped hover (retro low-framerate float, not smooth) */
  .gbScreen .agentTile::before { content: ''; position: absolute; inset: 0; z-index: 0; background: var(--tile); transition: background .2s ease;
    clip-path: polygon(3.2cqh 0, calc(100% - 3.2cqh) 0, calc(100% - 2cqh) 0, calc(100% - 2cqh) 1.2cqh, calc(100% - 1.2cqh) 1.2cqh, calc(100% - 1.2cqh) 2cqh, 100% 2cqh, 100% 3.2cqh, 100% calc(100% - 3.2cqh), 100% calc(100% - 2cqh), calc(100% - 1.2cqh) calc(100% - 2cqh), calc(100% - 1.2cqh) calc(100% - 1.2cqh), calc(100% - 2cqh) calc(100% - 1.2cqh), calc(100% - 2cqh) 100%, calc(100% - 3.2cqh) 100%, 3.2cqh 100%, 2cqh 100%, 2cqh calc(100% - 1.2cqh), 1.2cqh calc(100% - 1.2cqh), 1.2cqh calc(100% - 2cqh), 0 calc(100% - 2cqh), 0 calc(100% - 3.2cqh), 0 3.2cqh, 0 2cqh, 1.2cqh 2cqh, 1.2cqh 1.2cqh, 2cqh 1.2cqh, 2cqh 0); }   /* the tile face — PIXELATED stepped corners (matches the visual housing) */
  /* DARK pixel-dithered drop shadow BEHIND the face (z-index below the ::before) — same dither look as the corner glare, but dark not white */
  .gbScreen .agentTile::after { content: ''; position: absolute; z-index: -1; inset: -22%; transform: translate(0.4cqh, 0.4cqh);
    background-image: var(--tile-shadow); background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; }   /* Bayer-dithered dark shadow baked to match the glare exactly; wide spread so the fade shows */
  .gbScreen .agentTile .atImg { position: relative; z-index: 1; width: 60%; height: 60%; object-fit: contain; display: block; }   /* icon above the face */
  .gbScreen .agentTile .atImg.pop { animation: atImgPop .42s cubic-bezier(.34,1.5,.5,1) both; }   /* icon pops on switch (tile keeps hovering) */
  @keyframes atileHover { 0%, 100% { transform: translate(-50%, -50%) rotate(4deg); } 50% { transform: translate(-50%, calc(-50% - 1.2cqh)) rotate(5deg); } }
  @keyframes atImgPop { 0% { transform: scale(0.4); opacity: 0; filter: url(#px-a); } 45% { opacity: 1; filter: url(#px-a); } 72% { filter: url(#px-b); } 100% { transform: scale(1); opacity: 1; filter: none; } }   /* pops in AND pixel-resolves */
  /* the selected platform's NATIVE visual: a square DOM window, clipped inside the screen so it never spills onto the console */
  .gbScreen .agentVisual { position: absolute; left: var(--avis-x, 60.5%); top: var(--avis-y, 20cqh); transform: translateX(-50%);
    height: var(--avis-size, 71.6cqh); aspect-ratio: 1; max-width: 98%; border-radius: 2.6cqh; overflow: hidden; background: #fff; z-index: 5; }   /* smooth rounded corners; bg matches the active platform (selectAgent sets AGENTS[i].bg) */
  html.touch .gbScreen .agentTile .atImg.pop { animation: atImgPopLite .3s steps(6) both; }   /* MOBILE: icon pop without SVG pixel filters (feMorphology is brutally slow on phone GPUs) */
  @keyframes atImgPopLite { 0% { transform: scale(0.55); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
  .gbScreen .agentVisual::after { content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .13; mix-blend-mode: overlay;
    background-image: repeating-linear-gradient(0deg,#000 0 1px,transparent 1px 3px), repeating-linear-gradient(90deg,#000 0 1px,transparent 1px 3px);
    background-size: 3px 3px; image-rendering: pixelated; }   /* GameBoy pixel dither */
  /* ---- Conversational Analytics platform visuals (carousel slide 0): three NATIVE panels stacked inside .agentVisual — Slack thread + donut (Figma gMK7UAA320oBl0RWB5jU3f 1:16469), Claude chat + stacked bars (1:16365), Lightdash app + line chart (1:16568). Faithful rebuilds of the 527×527 frames at ≈0.136cqh/px — pure CSS + inline SVG, no raster. Inter chrome throughout (Georgia for Claude's serif reply — a system font, nothing loaded). Only the active panel displays; selectAgent()→showAgentVisual() replays its intro (donut winds up · bars shoot up · line draws) by re-adding .play — WITHOUT .play each panel renders complete (backwards-fill safe default, same discipline as .biPlay/.mcPlay). ---- */
  .gbScreen .avis { position: absolute; inset: 0; box-sizing: border-box; display: none; flex-direction: column; font-family: 'Inter', sans-serif; text-align: left; }
  .gbScreen .avis.on { display: flex; }
  /* -- Slack: CEO asks @Lightash AI in a thread → the app answers with a "2026 orders" donut card -- */
  .gbScreen .avSlack { background: #fff; padding: 2.8cqh 3.2cqh 3cqh; gap: 2cqh; }
  .gbScreen .svMsg { flex: none; display: flex; gap: 1.2cqh; align-items: flex-start; }
  .gbScreen .svAva { width: 4.9cqh; height: 4.9cqh; border-radius: 1.1cqh; flex: none; display: block; overflow: hidden; object-fit: cover; }
  .gbScreen .svBody { min-width: 0; padding-top: 0.1cqh; }
  .gbScreen .svHead { display: flex; align-items: baseline; gap: 1cqh; font-size: 1.85cqh; line-height: 1.25; color: #1d1c1d; }
  .gbScreen .svHead b { font-weight: 800; }   /* Slack names render near-Black; Inter is loaded to 700 → synthetic 800 reads right at this size */
  .gbScreen .svTime { font-size: 1.4cqh; color: #616061; font-weight: 400; }
  .gbScreen .svApp { background: #f2f2f2; color: #616061; font-size: 1.2cqh; font-weight: 600; padding: 0.25cqh 0.55cqh; border-radius: 0.35cqh; letter-spacing: 0.02em; }
  .gbScreen .svText { margin-top: 0.4cqh; font-size: 1.85cqh; line-height: 1.4; color: #1d1c1d; }
  .gbScreen .svMention { background: rgba(18, 100, 163, 0.1); color: #1264a3; border-radius: 0.35cqh; padding: 0.1cqh 0.5cqh; }
  .gbScreen .svCard { flex: 1; min-height: 0; border: 0.1cqh solid #e1e4ea; border-radius: 0.9cqh; padding: 1.7cqh 1.9cqh 1.2cqh; display: flex; flex-direction: column; }   /* the shared primary card border (matches .mcRow/.biCard) */
  .gbScreen .svCardTitle { flex: none; font-size: 1.75cqh; font-weight: 700; color: #1d1c1d; }
  .gbScreen .svDonut { flex: 1; min-height: 0; width: 100%; margin-top: 0.5cqh; }
  .gbScreen .svDonut text { font: 600 9px 'Inter', sans-serif; fill: #454245; }
  .gbScreen .svDonut .dseg { stroke-linecap: butt; }   /* butt caps → segments abut with no rounded overlap */
  .gbScreen .avSlack.play .dseg { stroke-dashoffset: 0; animation: avDonutIn var(--dd, 300ms) var(--ds, 0ms) linear both; }   /* linear + duration ∝ each month's share → ONE constant-speed clockwise wind-up from 12 o'clock */
  @keyframes avDonutIn { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }   /* pathLength=100, dasharray "100 100" → offset 100 hides the arc, 0 reveals it start→end (clockwise) */
  .gbScreen .avSlack.play .dlab { animation: mcNumIn 0.45s var(--dl, 0ms) both ease; }   /* month labels + leader lines fade in once the ring has wound up */
  /* -- Claude: user pill · serif reply · "Order history" stacked-bars card · the asterisk mark -- */
  .gbScreen .avClaude { background: #faf9f5; padding: 3cqh 3.2cqh 2.6cqh; }
  .gbScreen .cvPill { flex: none; align-self: flex-end; background: #f0eee6; border-radius: 1.45cqh; padding: 1.15cqh 1.9cqh; font: 500 1.85cqh/1.4 'Inter', sans-serif; color: #141413; }
  .gbScreen .cvSerif { flex: none; margin: 2cqh 0 1.1cqh; padding: 0 1.35cqh; font: 400 1.9cqh/1.5 Georgia, 'Times New Roman', serif; color: #141413; }   /* Claude answers in its serif voice (Figma: Georgia — system font) */
  .gbScreen .cvCard { flex: 1; min-height: 0; background: #fff; border: 0.1cqh solid #e1e4ea; border-radius: 1.75cqh; padding: 2.6cqh 3cqh 2cqh; display: flex; flex-direction: column; }   /* the shared primary card border (matches .mcRow/.biCard) */
  .gbScreen .cvTitle { font: 500 1.95cqh/1.2 'Inter', sans-serif; color: #212529; }
  .gbScreen .cvSub { margin-top: 0.4cqh; font: 400 1.3cqh/1.2 'Inter', sans-serif; color: #9999a3; }   /* subtitle hugs its title (shadcn card header rhythm) */
  .gbScreen .cvLegend { display: flex; justify-content: flex-end; gap: 1.75cqh; margin-top: 1cqh; }
  .gbScreen .cvLegItem { display: inline-flex; align-items: center; gap: 0.65cqh; font: 400 1.3cqh/1 'Inter', sans-serif; color: #6b6b75; white-space: nowrap; }
  .gbScreen .cvSw { width: 1.1cqh; height: 1.1cqh; border-radius: 50%; flex: none; }
  .gbScreen .cvChart { flex: 1; min-height: 0; margin-top: 1.4cqh; display: flex; gap: 0.9cqh; }
  .gbScreen .cvYAxis { flex: none; width: 3cqh; display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; margin: -0.5cqh 0; font: 400 1.05cqh/1 'Inter', sans-serif; color: #535862; font-variant-numeric: tabular-nums; }   /* muted tabular ticks, each centred on its gridline via the negative half-line margins */
  .gbScreen .cvPlot, .gbScreen .biPlot, .gbScreen .mcPlot, .gbScreen .embPlot { flex: 1; min-width: 0; position: relative; }
  .gbScreen .cvRules, .gbScreen .ldRules, .gbScreen .biRules, .gbScreen .mcRules, .gbScreen .embRules { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: space-between; pointer-events: none; }
  .gbScreen .cvRules i { display: block; height: 0.1cqh; background: #f0f1f5; }   /* gridlines one step lighter than the card border so they recede (the shared gridline gray) */
  .gbScreen .cvBars { position: absolute; inset: 0; display: flex; }
  .gbScreen .cvBarCell { flex: 1 1 0; display: flex; justify-content: center; align-items: flex-end; }
  .gbScreen .cvBar { width: 4.15cqh; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; }   /* segments stack top→bottom: lavender · green · purple base (the BI bar palette) */
  .gbScreen .cvBar i, .gbScreen .biBar i { display: block; flex: none; }
  .gbScreen .cvBar i:first-child { border-radius: 0.4cqh 0.4cqh 0 0; }   /* soften the bar top */
  .gbScreen .cvXAxis { flex: none; display: flex; margin: 0.8cqh 0 0 3.9cqh; font: 400 1.05cqh/1 'Inter', sans-serif; color: #535862; }   /* left margin = Y gutter (3) + plot gap (0.9) so the months sit under their bars */
  .gbScreen .cvXAxis span { flex: 1 1 0; text-align: center; }
  .gbScreen .cvLogo { flex: none; width: 3.6cqh; height: 3.6cqh; margin-top: 1.7cqh; color: #d97757; }
  .gbScreen .avClaude.play .cvBar { animation: biBarPop 0.55s var(--bd, 0ms) both cubic-bezier(0.2, 0.8, 0.2, 1.15); transform-origin: bottom; }   /* bars shoot up from the baseline with a left→right stagger (same keyframes as the BI dashboard) */
  /* -- Lightdash: "Help with Q1 dashboard" app window · user pill · agent rows · line-chart card + chart-type toolbar · action bar -- */
  .gbScreen .avLd { background: #fff; }
  .gbScreen .ldHead { flex: none; height: 5.9cqh; border-bottom: 0.1cqh solid #ced4da; display: flex; align-items: center; gap: 1.4cqh; padding: 0 2.2cqh; }
  .gbScreen .ldHead b { font: 500 1.9cqh/1.2 'Inter', sans-serif; color: #111418; margin-right: auto; }
  .gbScreen .ldHead svg { width: 2.2cqh; height: 2.2cqh; color: #495057; flex: none; }
  .gbScreen .ldBody { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 1.1cqh 2.2cqh 1.4cqh; }
  .gbScreen .ldAgo { flex: none; align-self: flex-end; font: 400 1.25cqh/1.4 'Inter', sans-serif; color: #868e96; }
  .gbScreen .ldPill { flex: none; align-self: flex-end; margin-top: 0.3cqh; background: #fff; border: 0.1cqh solid #e9ecef; box-shadow: 0 0.15cqh 0.35cqh rgba(0, 0, 0, 0.06); border-radius: 1.1cqh; padding: 1cqh 1.6cqh; font: 500 1.85cqh/1.35 'Inter', sans-serif; color: #343a40; }
  .gbScreen .ldRow { flex: none; display: flex; align-items: center; gap: 0.9cqh; margin-top: 1.5cqh; font: 500 1.7cqh/1.3 'Inter', sans-serif; color: #343a40; min-width: 0; }
  .gbScreen .ldRow svg { width: 1.9cqh; height: 1.9cqh; color: #495057; flex: none; }
  .gbScreen .ldIt { font: italic 400 1.55cqh/1.3 'Inter', sans-serif; color: #868e96; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .gbScreen .ldMut { font: 400 1.45cqh/1.3 'Inter', sans-serif; color: #adb5bd; }
  .gbScreen .ldChev { font: 400 1.9cqh/1 'Inter', sans-serif; color: #adb5bd; }
  .gbScreen .ldCard { flex: 1; min-height: 0; margin-top: 1.5cqh; border: 0.14cqh dashed #e1e4ea; border-radius: 1.1cqh; padding: 1.8cqh 2cqh 1cqh; display: flex; flex-direction: column; }   /* the shared primary card border (dashed variant) */
  .gbScreen .ldTitle { flex: none; font: 600 2.15cqh/1.25 'Inter', sans-serif; color: #212529; }
  .gbScreen .ldToolbar { flex: none; align-self: flex-end; margin-top: 0.9cqh; display: flex; align-items: center; background: #f1f3f5; border-radius: 0.7cqh; padding: 0.55cqh; }
  .gbScreen .ldTool { width: 3.7cqh; height: 2.8cqh; display: flex; align-items: center; justify-content: center; color: #495057; border-radius: 0.65cqh; }
  .gbScreen .ldTool svg { width: 2cqh; height: 2cqh; }
  .gbScreen .ldTool.sel { background: #4c6ef5; color: #fff; }   /* LINE chart selected (Figma #4c6ef5 pill) */
  .gbScreen .ldTDiv { width: 0.12cqh; height: 2.6cqh; background: #dee2e6; flex: none; }
  .gbScreen .ldChart { flex: 1; min-height: 0; margin-top: 1cqh; display: flex; }
  .gbScreen .ldYLab { flex: none; align-self: center; writing-mode: vertical-rl; transform: rotate(180deg); font: 400 1.25cqh/1 'Inter', sans-serif; color: #868e96; }
  .gbScreen .ldPlotWrap { flex: 1; min-width: 0; padding: 0.6cqh 1cqh 2.5cqh 4.4cqh; }   /* gutters: Y tick labels hang left of the plot, X tick labels below it */
  .gbScreen .ldPlot { position: relative; width: 100%; height: 100%; border-left: 0.12cqh solid #dee2e6; border-bottom: 0.12cqh solid #dee2e6; }
  .gbScreen .ldRules i { display: block; height: 0.1cqh; background: #f0f1f5; }   /* the shared gridline gray (matches .mcRules/.cvRules/.embRules) */
  .gbScreen .ldYT { position: absolute; right: calc(100% + 0.8cqh); transform: translateY(-50%); font: 400 1.3cqh/1 'Inter', sans-serif; color: #535862; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .gbScreen .ldXT { position: absolute; top: calc(100% + 0.8cqh); transform: translateX(-50%); font: 400 1.3cqh/1 'Inter', sans-serif; color: #535862; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .gbScreen .ldSvg, .gbScreen .biSvg, .gbScreen .mcSvg, .gbScreen .embSvg { position: absolute; inset: 0; width: 100%; height: 100%; }
  .gbScreen .ldLine, .gbScreen .biLine, .gbScreen .mcLine, .gbScreen .embLine { fill: none; vector-effect: non-scaling-stroke; stroke-linecap: round; stroke-linejoin: round; }
  .gbScreen .ldLine { stroke: #7262ff; stroke-width: 1.8; }
  .gbScreen .ldFoot { flex: none; display: flex; gap: 1.7cqh; margin-top: 1.2cqh; color: #495057; }
  .gbScreen .ldFoot svg { width: 2.1cqh; height: 2.1cqh; }
  .gbScreen .avLd.play .ldSvg { animation: mcLineDraw 1.15s 0.15s both cubic-bezier(0.25, 0.1, 0.3, 1); }   /* the order line draws on left→right (same wipe as the BI/MC growth lines) */
  /* interior FILL: transparent at rest → starfield shows through; fills to purple only on hover/arming */
  .gbScreen .tile::after {
    content: ''; position: absolute; inset: 1px; clip-path: inherit;
    background-color: color-mix(in srgb, transparent, var(--surface-hi) calc(var(--load, 0) * 100%));
    transition: background-color .12s linear;
  }
  /* cursor hover also fills the tile (fades in) */
  @media (hover: hover) { .gbScreen .tile:not(.active):not(.noexpand):hover::after { background-color: var(--surface-hi); transition: background-color .55s linear; } }   /* only expandable stories get a hover fill; desktop-only so touch never sticks it */
  .gbScreen .tile:not(.active):not(.noexpand) { box-shadow: 0 0 calc(var(--load, 0) * 2.4cqh) rgba(123, 98, 255, calc(var(--load, 0) * 0.55)); transition: box-shadow .18s ease; }   /* HOVER (Cloudy over the tile → --load:1): purple GLOW + ::after fill only — NO scale-grow (per the user: tiles shouldn't change size on hover, and the grown tile's expanded hit-area was overlapping/blocking clicks on neighbouring tiles) */
  .gbScreen .tile > *:not(.tframe):not(.arr) { position: relative; z-index: 1; }   /* logos / quote / text sit above the interior; .tframe + .arr keep their own absolute positioning */
  .gbScreen .tile.active { flex-grow: 300; }
  .gbScreen .tile.active .tframe { background-color: var(--acc); }
  /* expanded tile only: the planet backdrop, opacity knocked under a purple veil, pixel dither on top */
  .gbScreen .tile.active::after {
    background-color: var(--surface-hi);
    background-image: var(--screen-tex), linear-gradient(rgba(46,38,86,0.42), rgba(46,38,86,0.56)), url('../tab-bg.jpg');
    background-size: 5px 5px, cover, cover;
    background-position: 0 0, center, right bottom;
    background-repeat: repeat, no-repeat, no-repeat;
  }
  /* 3-tile activerow (the fal/Workday row once arrangeAdobeStack relocates Adobe into it, desktop): active story widens on its own side; the two siblings stack VERTICALLY in a narrow side column */
  .gbScreen .bento > .brow.activerow:has(> .tile:nth-child(3)) { display: grid; gap: var(--gap); grid-template-columns: 1fr minmax(0, 0.3fr); grid-template-rows: 1fr 1fr; }
  .gbScreen .bento > .brow.activerow:has(> .tile:nth-child(3)) > .tile.active { grid-column: 1; grid-row: 1 / -1; }
  .gbScreen .bento > .brow.activerow:has(> .tile:nth-child(3)):has(> .tile:last-child.active) { grid-template-columns: minmax(0, 0.3fr) 1fr; }
  .gbScreen .bento > .brow.activerow:has(> .tile:nth-child(3)):has(> .tile:last-child.active) > .tile.active { grid-column: 2; grid-row: 1 / -1; }
  .gbScreen .tile.decor { pointer-events: none; }   /* Adobe when stacked under Octopus in the fal view — decorative, non-clickable */
  /* 2-tile rows: horizontal accordion — active widens in place, the sibling shrinks to a strip */
  .gbScreen .brow.activerow .tile:not(.active) { flex-grow: 0; flex-basis: 27%; transition: flex-basis .38s cubic-bezier(.4,.02,.2,1), flex-grow .38s cubic-bezier(.4,.02,.2,1); }
  .gbScreen .arr { position: absolute; top: 2cqh; right: 2.4cqh; width: 2.8cqh; height: 2.8cqh; opacity: .92; transition: opacity .3s; z-index: 3; pointer-events: none; }
  @keyframes arrNudge { 0%, 70%, 100% { transform: translate(0, 0); } 84% { transform: translate(0.4cqh, -0.4cqh); } }   /* a gentle outward nudge (in the ↗ direction) to hint the tile expands — cqh so it scales with the screen instead of a fixed 1.4px */
  .gbScreen .tile:not(.active) .arr { animation: arrNudge 3.4s ease-in-out infinite; }
  .gbScreen .tile.active .arr { opacity: .92; transform: rotate(180deg); }   /* open story → arrow flips to point inward/down = click to collapse */

  /* collapsed preview: logo only (centred), or logo left + stat right-aligned (number & label side-by-side) */
  .gbScreen .preview { flex: 1; display: flex; align-items: center; justify-content: center; gap: 3.2cqh; min-width: 0; transition: opacity .22s ease; }
  .gbScreen .bento.reflow .preview, .gbScreen .bento.reflow .tdetail { opacity: 0 !important; transition: opacity .1s ease !important; }   /* while tiles resize, hide the logos/text so you never see them scale — they fade back in at their new size */
  .gbScreen .preview.withstat { justify-content: space-between; padding: 0 3.2cqh 0 0; gap: 1.2cqh; }   /* right-pad so the stat caption clears the top-right ↗ arrow */
  .gbScreen .clogo { width: auto; height: auto; object-fit: contain; object-position: left center;
    filter: brightness(0) invert(1); opacity: 1; }
  .gbScreen .preview .clogo { max-width: calc(58% * var(--ls,1)); max-height: calc(54% * var(--ls,1)); }
  /* the logo keeps a min-width FLOOR: it may shrink, but never below ~15% — previously it was the only shrinkable flex item (statmini was flex:none), so a long stat label crushed it to 0 width and the wide bars showed a stat with NO logo */
  .gbScreen .preview.withstat .clogo { flex: 0 1 auto; min-width: calc(18% * var(--ls,1)); max-width: calc(34% * var(--ls,1)); max-height: calc(58% * var(--ls,1)); }
  .gbScreen .tile[data-cust="6"] .preview.withstat .clogo { max-width: calc(26% * var(--ls,1)); }   /* OakNorth: narrower wordmark → more daylight before the 128 */
  .gbScreen .tile[data-cust="2"] .preview .clogo { max-height: calc(66% * var(--ls,1)); max-width: calc(66% * var(--ls,1)); }   /* Workday: tall arc+wordmark reads small in the wide bar on desktop — let it grow */
  .gbScreen .tile[data-cust="7"] .preview.withstat .clogo { max-width: calc(21% * var(--ls,1)); }   /* JustWatch: shrink the wide wordmark so space-between opens MORE daylight before the 86% */
  /* number + caption grouped as ONE tight block on the right (logo spaced off to the left via the preview's space-between); gap kept small so the stat and its caption read together */
  .gbScreen .statmini { display: flex; flex-direction: row; align-items: center; justify-content: flex-end; gap: 0.9cqh; flex: 0 1 auto; min-width: 0; }
  .gbScreen .stat-sm { font-size: 5.2cqh; font-weight: 800; color: #fff; line-height: 0.9; letter-spacing: -0.02em; flex: none; }   /* the number never shrinks */
  .gbScreen .statl-sm { font-size: 1.6cqh; letter-spacing: 0.08em; text-transform: uppercase; color: #fff; line-height: 1.45; max-width: 22ch; min-width: 0; text-align: left; }
  .gbScreen .tile.active .preview { display: none; }
  .gbScreen .tile:not(.active) .tdetail { display: none; }
  /* a squeezed (square) sibling of the expanded tile: LOGO top-left, STAT bottom-right — uses the tall square shape instead of dropping the stat. Padding matches the ↗ arrow (2cqh top / 2.4cqh sides); stat type scaled down so the diagonal actually shows daylight between logo and stat */
  .gbScreen .brow.activerow .tile:not(.active) .preview.withstat { position: relative; display: block; padding: 0; }   /* absolute corners → guaranteed diagonal with daylight between */
  .gbScreen .brow.activerow .tile:not(.active) .preview.withstat .clogo { position: absolute; top: -1.3cqh; left: -1.1cqh; min-width: 0; max-width: 40%; max-height: 19%; }   /* logo toward the top-left corner — negative insets cancel part of the tile's 3.6cqh --pad, leaving a bit of breathing room off the frame */
  .gbScreen .brow.activerow .tile:not(.active) .statmini { position: absolute; bottom: -1.3cqh; right: -1.1cqh; display: flex; flex-direction: column; align-items: flex-end; text-align: right; gap: 0.85cqh; }   /* number + caption toward the bottom-right corner (same inset trick, matched padding) */
  .gbScreen .brow.activerow .tile:not(.active) .stat-sm { font-size: 4.4cqh; }
  .gbScreen .brow.activerow .tile:not(.active) .statl-sm { text-align: right; max-width: none; font-size: 1.35cqh; line-height: 1.3; }   /* full-width cap → the label settles into 2 lines instead of 3 ragged ones */
  /* squeezed short STAT bars (a story open in ANOTHER row): smaller stat type so logo + number + label all share the narrow bar */
  .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .stat-sm { font-size: 4cqh; }
  .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .statl-sm { font-size: 1.35cqh; line-height: 1.3; }
  .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .statmini { gap: 1cqh; }
  /* when a story is open, the OTHER (compressed) rows go short — give their logo-only tiles a taller cap so Workday etc. stay readable */
  .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .preview:not(.withstat) .clogo { max-height: 92%; max-width: 80%; }
  .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="2"] .clogo { max-height: 120%; max-width: 100%; }   /* Workday's tall arc+wordmark reads small in the short compressed WIDE BAR — let it grow bigger (slight height overflow into the tile pad) */
  .gbScreen .brow.activerow .tile:not(.active)[data-cust="2"] .preview .clogo { max-height: 52%; max-width: 52%; }   /* Workday as the TALL activerow sibling card (e.g. fal's story open) — its arc+wordmark reads too big there; shrink it a touch */
  /* ---- DESKTOP-ONLY customer-logo size bumps (per the user: the compressed story-bar + sibling logos read tiny on desktop). All body:not(.mfill) → higher specificity wins on desktop, inert on mobile so those sizes are untouched. ---- */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .preview:not(.withstat) .clogo { max-height: 152%; max-width: 100%; }   /* Octopus · ServiceNow · Kraken · Adobe wide bars — nudged up 135→152% (slightly bigger on desktop, per the user); they're height-bound so the extra height reads as a size bump */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="2"] .clogo { max-height: 242%; max-width: 100%; }   /* Workday wide bar — its 2:1 logo (arc ABOVE the wordmark) is height-constrained by the tile pad, so it needs to overflow more to read bigger. Nudged 215→242% to match the slight size bump applied to the other collapsed logos (per the user) */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .preview.withstat .clogo { max-height: 102%; }   /* JustWatch · OakNorth stat-bars: taller wordmark — nudged 90→102% (slightly bigger on desktop, per the user) */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="5"] .preview.withstat .clogo { max-height: 150%; max-width: calc(42% * var(--ls,1)); }   /* fal collapsed stat-bar — bump its small logo (per the user) */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="7"] .preview.withstat .clogo { max-width: calc(40% * var(--ls,1)); }   /* JustWatch wordmark wider — 36→40% (slightly bigger on desktop, per the user) */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="6"] .preview.withstat .clogo { max-width: calc(29% * var(--ls,1)); }   /* OakNorth wordmark wider — 26→29% (slightly bigger on desktop, per the user) */
  body:not(.mfill) .gbScreen .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="6"] .preview.withstat .clogo { max-width: calc(42% * var(--ls,1)); }   /* OakNorth wordmark wider */
  body:not(.mfill) .gbScreen .brow.activerow .tile:not(.active) .preview.withstat .clogo { max-width: 48%; max-height: 23%; }   /* fal · JustWatch · OakNorth as activerow siblings (top-left corner) — a bit bigger */

  /* expanded story: logo top-left, big left-aligned quote, person beneath it, CTA bottom-right */
  .gbScreen .tdetail {
    position: relative;
    flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 3cqh;
    pointer-events: none;
    transition: opacity .22s ease;   /* fast fade-in when the content reappears after the tiles re-flow (matches the logos) */
  }
  .gbScreen .tile.active .tdetail { pointer-events: auto; }
  .gbScreen .dlogo { position: relative; flex: 0 0 auto; align-self: flex-start; max-width: 15%; max-height: 4cqh; margin-bottom: 2.5cqh; }   /* logo is a FLOW item at the top → the quote always flows BELOW it (never overlaps); extra space under it before the quote. Read-story CTA stays anchored bottom-right */
  .gbScreen .quote { font-family: 'Britti Medium', 'Inter', sans-serif; text-align: left; font-size: 4.7cqh; font-weight: 500; color: #fff; line-height: 1.15; max-width: 92%; }
  /* premium word-by-word reveal — each word softly focuses in; speaker + CTA follow the last word */
  .gbScreen .quote .w { display: inline-block; opacity: 0; transform-origin: 50% 90%; will-change: opacity, filter, transform; }
  .gbScreen .who, .gbScreen .dlogo, .gbScreen .cta { opacity: 0; }
  /* two-tone pixel resolve (matches the title spell-out): each word pixelates in as the DARK tone, then brightens to white as it sharpens */
  @keyframes wordIn {
    0%   { opacity: 0; color: #2b2358; filter: url(#px-a); }
    28%  { opacity: 1; color: #2b2358; filter: url(#px-a); }
    62%  { color: #7166cf; filter: url(#px-b); }
    84%  { color: #ffffff; filter: url(#px-c); }
    100% { opacity: 1; color: #ffffff; filter: none; }
  }
  @keyframes fadeUp { from { opacity: 0; transform: translateY(.45em); filter: blur(4px); } to { opacity: 1; transform: none; filter: blur(0); } }
  @keyframes fadeUpPlain { from { opacity: 0; transform: translateY(.45em); } to { opacity: 1; transform: none; } }  /* no filter — keeps the logo's whitening filter intact */
  .gbScreen .tile.active .quote .w { animation: wordIn .55s cubic-bezier(.22,.61,.36,1) both; animation-delay: calc(.1s + var(--i) * .052s); }
  .gbScreen .tile.active .dlogo { animation: fadeUpPlain .45s ease-out both; animation-delay: .06s; }
  .gbScreen .tile.active .who { animation: fadeUp .55s cubic-bezier(.22,.61,.36,1) both; animation-delay: calc(.16s + var(--words) * .052s); }
  .gbScreen .tile.active .cta { animation: fadeUp .5s ease-out both; animation-delay: calc(.24s + var(--words) * .052s); }
  .gbScreen .who { display: flex; align-items: center; gap: 2cqh; }   /* centred under the quote */
  .gbScreen .avatar {
    width: 7.2cqh; aspect-ratio: 1; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    background: #fff; font-weight: 800; color: var(--panel-bg); font-size: 2.8cqh; letter-spacing: -0.02em;
  }
  .gbScreen .avatar--img { background: none; border-radius: 0; overflow: hidden; }   /* photo avatars (per the user) — SHARP square corners, no rounding; the image is clipped inside */
  .gbScreen .avatarImg { width: 100%; height: 100%; object-fit: cover; display: block; }
  .gbScreen .wn { font-weight: 700; color: #fff; font-size: 2.8cqh; }
  .gbScreen .wr { color: rgba(255,255,255,0.88); font-size: 2cqh; }   /* role · company in white (slightly softer than the bold name) */
  .gbScreen .cta {
    pointer-events: auto; position: absolute; right: 0; bottom: 0;
    background: #fff; color: var(--panel-bg); text-transform: uppercase;
    border: none; border-radius: 0; cursor: pointer;
    padding: 2.4cqh 2.8cqh; font-size: 1.6cqh; line-height: 1;
    font-family: 'Press Start 2P', monospace; letter-spacing: 0.02em; transition: background-color .15s, color .15s;   /* ONLY colour transitions — never layout. `transition: .15s` (all props) made the agentCta ease to its new cqh top on every resize frame → the "bounce". Siblings have no transition, so they snap; now the CTA snaps too. */
    display: inline-flex; align-items: center; white-space: nowrap;
  }
  @media (hover: hover) { .gbScreen .cta:hover { background: #000; color: #fff; } }   /* hover (cursor) → black fill; desktop-only so touch never sticks it */
  .gbScreen .cta.hot { background: #000; color: #fff; }   /* Cloudy over it → black fill, white text (desktop dwell) */
  .gbScreen .vlearn { display: none; }   /* LEARN MORE header CTA — MOBILE ONLY (revealed by the .mfill rule); desktop keeps its own in-visual CTAs so this stays hidden */
  .gbScreen .agentCta { left: var(--avis-x, 60.5%); right: auto; top: calc(var(--avis-y, 20cqh) + var(--avis-size, 71.6cqh)); bottom: auto; transform: translate(-50%, -50%); opacity: 1; z-index: 8; background: #5B5BD6; color: #fff; }   /* Agents page: "Explore Agents" CTA — pinned to the bottom-centre EDGE of the product visual (top = visual top + height, then translateY(-50%) to straddle the edge). Ties it to the visual's own geometry so it rides the .agentGroup scale as one piece; purple fill, white label */
  .gbScreen .agentCta:hover, .gbScreen .agentCta.hot { background: #4a3fc9; color: #fff; }   /* hover: slightly darker purple */
  /* ---- Product page carousel: slide 0 = the agents visual, slide 1 = the (migrated) Data Apps visual. Left/right arrows on the screen edges page between them. The window fills the whole view (behind the fixed vhead) so each slide's absolutely-positioned, cqh-based content keeps resolving against the full screen. ---- */
  .gbScreen .pcarWin { position: absolute; inset: 0; overflow: hidden; z-index: 1; }
  .gbScreen .pcarTrack { display: flex; width: 100%; height: 100%; transition: transform .5s cubic-bezier(.4,0,.2,1); }
  .gbScreen .pslide { position: relative; flex: 0 0 100%; width: 100%; height: 100%; }
  /* the Data Apps visual on carousel slide 2 gets its OWN transform (the Product page's vhead title sits where the visual's default -4cqh lift would collide) — driven by the DEV panel's DA VISUAL X/Y/SCALE (--pdg-*), independent of the standalone page's --dg-* */
  .gbScreen .pslide1 .daGroup { transform: translate(var(--pdg-x, -3.5cqh), var(--pdg-y, 13cqh)) scale(var(--pdg-s, 1.01)); }
  /* slide 1 (agents visual) as a scalable group so the DEV panel can shrink/move the whole product visual — its children stay absolutely positioned against this full-slide box */
  .gbScreen .agentGroup { position: absolute; inset: 0; transform: translate(var(--ag-x, 0cqh), var(--ag-y, 0cqh)) scale(var(--ag-s, 1)); transform-origin: center center; }
  .gbScreen .pcarArrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 11; cursor: pointer;
    width: 11cqh; height: 11cqh; display: flex; align-items: center; justify-content: center;   /* bigger, and w === h (cqh both axes) so it's a true square on any screen aspect */
    background: transparent; border: none; border-radius: 0; padding: 0;
    transition: opacity .3s ease; }
  .gbScreen .pcarArrow .tframe { background-color: var(--acc); z-index: 0; }   /* purple pixel-stepped ring, same as the agent tabs — cut by updateTframes each frame */
  .gbScreen .pcarArrow::before { content: ''; position: absolute; inset: 0; z-index: -1; background: rgba(14,12,26,0.82); transition: background .2s ease; }   /* dark fill; the PARENT button carries the stepped clip-path (set by updateTframes) which clips this fill to the divoted shape — same technique as the customer tiles' ::after (element clip-path clips the fill) → never a square behind the divots */
  .gbScreen .pcarGlyph { position: relative; z-index: 2; color: #fff; font: 400 6.6cqh/1 'Inter', sans-serif; padding-bottom: 0.5cqh; pointer-events: none; }
  @media (hover: hover) { .gbScreen .pcarArrow:hover::before { background: rgba(46,38,92,0.92); } }
  .gbScreen .pcarArrow.hot::before { background: rgba(46,38,92,0.92); }
  .gbScreen .pcarArrow.hot .tframe { background-color: #b9abff; }   /* Cloudy dwelling on it → brighter frame */
  .gbScreen .pcarPrev { left: 2.6cqh; }
  .gbScreen .pcarNext { right: 2.6cqh; }
  /* the carousel LOOPS now (setProductSlide wraps) — both arrows stay active on EVERY slide so last→first and first→last are reachable (the old data-slide="0"/data-last dim+hide rules are gone) */
  /* alpha-product placeholder slide: centred product name until its real visual is built */
  .gbScreen .pslideph { display: flex; align-items: center; justify-content: center; }
  .gbScreen .phInner { display: flex; flex-direction: column; align-items: center; gap: 2.2cqh; text-align: center; }
  .gbScreen .phEyebrow { font: 400 1.7cqh/1 'Press Start 2P', monospace; letter-spacing: 0.24em; color: #b9abff; padding: 0.9cqh 1.4cqh; border: 0.28cqh solid rgba(160,140,255,0.5); }
  .gbScreen .phTitle { font: 800 6.4cqh/1.05 'Inter', sans-serif; color: #fff; letter-spacing: -0.01em; }
  .gbScreen .phNote { font: 500 2.4cqh/1 'Inter', sans-serif; color: rgba(230,228,244,0.5); }
  /* ---- Product carousel slide 2: "BI as code" — a dark agent terminal (top-left) streaming a build-a-dashboard session, with the finished light "Sales KPI Dashboard" window overlapping its lower-right. Faithful rebuild of Figma MTh1NZIIPbrnFjn10ipvXW frame 1:3 (1596×977px → cqh at 0.075cqh/px, sized so the composition fits the 133.6×100cqh screen under the vhead title). Charts are pure CSS stacked bars + an inline SVG polyline — no raster assets, crisp at any screen scale. ---- */
  .gbScreen .biGroup { position: absolute; inset: 0; transform: translate(var(--bg-x, 0cqh), var(--bg-y, 4cqh)) scale(var(--bg-s, 0.95)); transform-origin: center center; }   /* group transform (mirrors .agentGroup/--pdg-*) so the whole visual moves/scales as ONE piece; default +3.5cqh drop clears the fixed vhead title */
  .gbScreen .biComp { position: absolute; left: 50%; top: 50%; width: 119.7cqh; height: 73.3cqh; transform: translate(-50%, -50%); }   /* fixed-size composition box (Figma 1596×977 × 0.075) centred in the slide — terminal + dashboard are absolute cqh children so the overlap is exact at every screen size */
  .gbScreen .biCta { position: absolute; z-index: 8; left: var(--bicta-x, 25.5cqh); top: var(--bicta-y, 51.5cqh); transform: translateX(-50%) scale(var(--bicta-s, 1)); transform-origin: center top; pointer-events: auto; cursor: pointer; background: #5B5BD6; color: #fff; border: none; border-radius: 0; font-family: 'Press Start 2P', monospace; font-size: calc(1.6cqh / 0.95); line-height: 1; letter-spacing: 0.02em; text-transform: uppercase; padding: calc(2.4cqh / 0.95) calc(2.8cqh / 0.95); white-space: nowrap; transition: background-color .15s; }   /* font/padding divided by the .biGroup scale (0.95) so it RENDERS at the shared CTA size (1.6cqh / 2.4×2.8, same as .cta) — every product CTA is the same on-screen size despite different group scales. "Book a demo" CTA under the terminal — inside .biComp so it rides the .biGroup transform/scale; position dialled in via the DEV panel (--bicta-x/y/s) */
  .gbScreen .biCta:hover, .gbScreen .biCta.hot { background: #4a3fc9; }
  /* -- layer 1: the terminal window (background, top-left; Figma 960×734) -- */
  .gbScreen .biTerm { position: absolute; left: 0; top: 0; width: 72cqh; height: 55cqh; background: #181818; border: 0.14cqh solid #191922; border-radius: 2.75cqh; overflow: hidden; }
  .gbScreen .biTermBar { height: 5.25cqh; background: #3a3a3a; border-bottom: 0.12cqh solid #0f0f0f; display: flex; align-items: center; gap: 0.95cqh; padding: 0 1.65cqh; }
  .gbScreen .biDot { width: 1.55cqh; height: 1.55cqh; border-radius: 50%; flex: none; }   /* macOS traffic lights — colored inline in the terminal, gray in the dashboard */
  .gbScreen .biTermBody { padding: 2cqh 2.3cqh 0; font: 400 2.1cqh/1.4 'JetBrains Mono', monospace; color: #c7c7c7; white-space: pre; }   /* white-space:pre → the continuation lines align under their parent tokens via literal spaces */
  .gbScreen .biTermBody b { font-weight: 700; }
  .gbScreen .biMut { color: #656565; }    /* chevrons + └ corners */
  .gbScreen .biBri { color: #d5d5d5; }    /* the brighter user-prompt line */
  .gbScreen .biFn  { color: #a499ff; }    /* tool name (Search) */
  .gbScreen .biKey { color: #03d6e5; }    /* parameter keys */
  .gbScreen .biStr { color: #37b24d; }    /* string literals */
  .gbScreen .biFile{ color: #c3e88d; }    /* found .yml files */
  .gbScreen .biNum { color: #f68c6d; }    /* numeric tokens */
  /* -- layer 2: the "Sales KPI Dashboard" window (foreground; Figma 960×722 at x636,y255 → overlaps the terminal's lower-right and extends beyond it) -- */
  .gbScreen .biDash { position: absolute; left: 47.7cqh; top: 19.1cqh; width: 72cqh; height: 54.2cqh; background: #f5f5f7; border: 0.08cqh solid #e1e4ea; border-radius: 2.05cqh; box-shadow: 0 0.3cqh 1.5cqh rgba(0,0,0,0.1); overflow: hidden; display: flex; flex-direction: column; }
  .gbScreen .biDashBar { flex: 0 0 3.3cqh; background: #fff; border-bottom: 0.08cqh solid #e1e4ea; display: flex; align-items: center; gap: 0.6cqh; padding: 0 1.65cqh; }
  .gbScreen .biDashBar .biDot { width: 1.22cqh; height: 1.22cqh; background: #d3d3d8; }   /* light window → muted gray dots */
  .gbScreen .biDashBody { flex: 1; min-height: 0; padding: 1.7cqh; display: flex; flex-direction: column; gap: 1.45cqh; font-family: 'Inter', sans-serif; }   /* one 1.45cqh (≈19px) gap rhythm between filter row / columns / cards */
  .gbScreen .biFilters { display: flex; gap: 1.1cqh; flex: none; }
  .gbScreen .biChip { display: inline-flex; align-items: center; gap: 0.6cqh; background: #fff; border: 0.08cqh solid #e1e4ea; border-radius: 99cqh; padding: 0.85cqh 1.2cqh; font: 500 1.5cqh/1 'Inter', sans-serif; color: #212529; white-space: nowrap; }
  .gbScreen .biChipMut { color: #858e97; font-weight: 400; }
  .gbScreen .biGrid { flex: 1; min-height: 0; display: flex; gap: 1.45cqh; }
  .gbScreen .biCol { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 1.45cqh; }
  /* card base: subtle 1px border for elevation (no shadow — shadow is reserved for the floating window), uniform 2.1cqh (≈28px) padding all round */
  .gbScreen .biCard { background: #fff; border: 0.08cqh solid #e1e4ea; border-radius: 1.2cqh; padding: 2.1cqh; overflow: hidden; display: flex; flex-direction: column; }   /* the shared primary card border (matches .mcRow) */
  .gbScreen .biCard--kpi { flex: 0 0 13.8cqh; }     /* both KPI cards share one height, both chart cards share the rest → the two columns stay level */
  .gbScreen .biCard--chart { flex: 1; min-height: 0; }
  .gbScreen .biCardTitle { font: 500 1.5cqh/1.25 'Inter', sans-serif; color: #212529; }
  .gbScreen .biCardSub { font: 400 1.2cqh/1.3 'Inter', sans-serif; color: #9999a3; margin-top: 0.35cqh; }   /* subtitle hugs its title; the larger gap sits between header and content */
  .gbScreen .biValRow { display: flex; align-items: center; gap: 1.2cqh; margin-top: auto; padding-top: 0.9cqh; }   /* KPI value pinned to the card bottom → the two stat cards' baselines match */
  .gbScreen .biVal { font: 600 4.3cqh/1 'Inter', sans-serif; color: #212529; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }   /* tabular so the biCountUp roll doesn't jitter (matches .embVal/.mcCount) */
  .gbScreen .biBadge { background: #f4fbfb; border-radius: 1.5cqh; padding: 0.6cqh 0.9cqh; font: 500 1.2cqh/1 'Inter', sans-serif; color: #37b24d; white-space: nowrap; }
  .gbScreen .biLegend { display: flex; justify-content: flex-end; gap: 1.2cqh; margin-top: 0.9cqh; flex: none; }
  .gbScreen .biLegItem { display: inline-flex; align-items: center; gap: 0.45cqh; font: 400 1.05cqh/1 'Inter', sans-serif; color: #6b6b75; white-space: nowrap; }
  .gbScreen .biSw { width: 0.75cqh; height: 0.75cqh; border-radius: 50%; flex: none; }
  /* shared chart scaffold: right-aligned muted Y labels · hairline gridlines (lighter than the card border so they recede) · plot area fills the rest */
  .gbScreen .biChart { flex: 1; min-height: 0; margin-top: 1cqh; display: flex; gap: 0.7cqh; }
  .gbScreen .biYAxis { flex: none; display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; margin: -0.42cqh 0; font: 400 0.85cqh/1 'Inter', sans-serif; color: #535862; font-variant-numeric: tabular-nums; }   /* negative half-line margins → each label centres on its gridline */
  .gbScreen .biRules i { display: block; height: 0.08cqh; background: #f0f1f5; }   /* the shared gridline gray (matches .mcRules/.cvRules/.embRules) */
  .gbScreen .biBars { position: absolute; inset: 0; display: flex; justify-content: space-around; align-items: flex-end; }
  .gbScreen .biBar { width: 1.6cqh; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; }   /* segments stack top→bottom: lavender · green · purple base */
  .gbScreen .biBar i:first-child { border-radius: 0.3cqh 0.3cqh 0 0; }   /* soften the bar top */
  .gbScreen .biLine { stroke: #7262ff; stroke-width: 1.7; }   /* "Current year" — the shared line-series purple (matches .mcLine/.ldLine/.embLine) */
  .gbScreen .biLine--prev { stroke: #d5d0ff; }   /* "Previous year" — drawn first so the current-year line passes over it; the shared comparison lavender */
  /* -- BI-as-code intro animation (biAnimate() on entering carousel slide 2): terminal types line-by-line, dashboard elements pop in order, KPI numbers roll up, bars grow, growth lines fly up. Every hidden state is the animations' `backwards` fill — so WITHOUT .biPlay the slide renders fully (safe default; also what a browser with reduced-motion sees). -- */
  .gbScreen .biTLine { display: block; white-space: pre; overflow: hidden; min-height: 2.94cqh; }   /* one terminal line; width-clipped left→right to fake typing (monospace → 1ch = 1 char). min-height reserves the row so nothing reflows as text types in */
  .gbScreen .biComp.biPlay .biTLine { animation-name: biTypeIn; animation-fill-mode: both; }   /* per-line duration/delay/steps() are set inline (computed from each line's char count) */
  @keyframes biTypeIn { from { width: 0; } to { width: var(--w, 100%); } }
  .gbScreen .biComp.biPlay .biPop { animation: mcPop 0.5s var(--pd, 0ms) both cubic-bezier(0.2, 0.7, 0.2, 1.1); }
  .gbScreen .biComp.biPlay .biBar { animation: biBarPop 0.5s var(--bd, 0ms) both cubic-bezier(0.2, 0.8, 0.2, 1.15); transform-origin: bottom; }
  @keyframes biBarPop { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  .gbScreen .biComp.biPlay .biSvg { animation: mcLineDraw 1.1s var(--ld, 0ms) both cubic-bezier(0.25, 0.1, 0.3, 1); }   /* the growth lines DRAW on left→right (like the chart plotting over time), not rise from the bottom */
  /* ---- Product carousel slide 3: "Metrics Catalog" — a white catalog of 5 metric rows (icon+name · description · category pill · usage count). Faithful rebuild of Figma gMK7UAA320oBl0RWB5jU3f node 1:15393, whose 3 numbered steps play as ONE intro timeline (mcAnimate): rows shoot up → an SVG cursor flies in and clicks the top row → the others dim → the top row expands into a line chart + an orders.yml terminal. Base CSS is the FINAL step-3 state, so the slide renders fully without the trigger. Pure CSS + inline SVG — no raster assets. ---- */
  .gbScreen .mcGroup { position: absolute; inset: 0; transform: translate(var(--mcg-x, 0cqh), var(--mcg-y, 6.5cqh)) scale(var(--mcg-s, 1.56)); transform-origin: center center; }   /* group transform (mirrors .biGroup/--bg-*) so the whole visual moves/scales as ONE piece; default +4cqh drop clears the fixed vhead title */
  .gbScreen .mcComp { position: absolute; left: 50%; top: 50%; width: 58cqh; height: 48cqh; transform: translate(-50%, -50%); }   /* narrower box (less horizontal dead space after the description + pill) — the expand body OVERLAYS the rows now, so height = header + open (≈44.6cqh), not the pushed-down stack */
  .gbScreen .mcList { position: absolute; inset: 0 0 auto 0; display: flex; flex-direction: column; gap: 1.2cqh; }   /* one 1.2cqh gap rhythm between row cards — a single spacing token everywhere (shadcn table discipline) */
  .gbScreen .mcRow { background: #fff; border: 0.08cqh solid #e1e4ea; border-radius: 1.2cqh; overflow: hidden; }
  .gbScreen .mcRowHead { height: 6.6cqh; display: grid; grid-template-columns: 21cqh 1fr 10cqh 6.5cqh; align-items: center; padding: 0 1.6cqh; }   /* name column wide enough for the full title (no "…" truncation); the wider name also shrinks the flexible desc column → less dead space after the description */
  .gbScreen .mcName { display: flex; align-items: center; gap: 0.9cqh; padding-right: 1.6cqh; font: 600 1.5cqh/1.2 'Inter', sans-serif; color: #1a1a21; min-width: 0; }
  .gbScreen .mcName span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .gbScreen .mcIco { flex: none; width: 1.4cqh; height: 1.4cqh; color: #98a2b3; }   /* tiny bar-chart metric glyph, muted gray */
  .gbScreen .mcDesc { align-self: stretch; display: flex; align-items: center; border-left: 0.08cqh solid #e9ecef; padding: 0 1.6cqh; font: 400 1.35cqh/1.3 'Inter', sans-serif; color: #667085; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }   /* full-height hairline divider after the name column (subtle 1px separation, not heavy) */
  .gbScreen .mcPillCell { align-self: stretch; display: flex; align-items: center; justify-content: center; border-left: 0.08cqh solid #e9ecef; }
  .gbScreen .mcPill { border: 0.08cqh solid #d9d6fe; background: #f4f3ff; color: #5925dc; border-radius: 99cqh; padding: 0.45cqh 1.05cqh; font: 500 1.15cqh/1 'Inter', sans-serif; white-space: nowrap; }
  .gbScreen .mcCount { align-self: stretch; display: flex; align-items: center; justify-content: flex-end; gap: 0.55cqh; padding-left: 1.6cqh; border-left: 0.08cqh solid #e9ecef; font: 500 1.35cqh/1 'Inter', sans-serif; color: #344054; font-variant-numeric: tabular-nums; }   /* full-height hairline divider to the LEFT of the count (i.e. to the right of the pill cell), matching the name/desc/pill dividers; numbers right-aligned + tabular so counts line up down the column */
  .gbScreen .mcCount .mcIco { width: 1.25cqh; height: 1.25cqh; }
  .gbScreen .mcRow--dim { opacity: 0.35; }   /* rows 2–5's FINAL state (Figma step-2 dim); the intro pops them in at full opacity, then mcDim fades them here on the click */
  .gbScreen .mcRow--top { position: relative; z-index: 6; overflow: visible; border-radius: 1.2cqh 1.2cqh 0 0; border-color: transparent; }   /* the clicked card sits ABOVE the dim rows; its expand body joins directly below (one continuous card). Own border HIDDEN (kept for geometry): header + body used to draw two independent borders whose border-box edges sub-pixel-round apart at real DPRs (±0.7px, direction flips with on-screen x) → a stray hairline along the header's bottom / card edges. The whole expanded card now gets ONE continuous rounded outline via ::after below (same single-outline model as the .mfill mobile fix). */
  .gbScreen .mcRow--top::after { content: ''; position: absolute; left: -0.08cqh; top: -0.08cqh; right: -0.08cqh; height: calc(6.6cqh + 40cqh + 0.08cqh); box-sizing: border-box; border: 0.08cqh solid #e1e4ea; border-radius: 1.2cqh; pointer-events: none; z-index: 9; }   /* ONE rounded stroke around header(6.6cqh) + body(40cqh). GEOMETRY: the -0.08cqh offsets resolve from the row's PADDING edge, so this box lands exactly on the row's border box — the same box the white bg paints to (the row's own 0.08cqh border is transparent, not removed). The radius must therefore MATCH the bg's 1.2cqh: a bigger radius (was 1.32cqh) on the same box cuts MORE off the corner, so the squarer white bg corner poked ~0.4px past the stroke's outer curve → the stray white corner sliver (worst at the collapsed bottom-left via mcHeaderRound's backwards fill). At 1.2cqh the stroke's outer curve coincides with the bg curve and it rasterizes exactly like the sibling rows' real 0.08/1.2 borders. Height is +0.08 (not +0.16) so the box's BOTTOM edge = .mcOpen's bg bottom (top 6.6cqh + height 40cqh), mirroring the top edge = header bg edge — one exact nesting at all four corners. Single element → no doubled/offset border → no stray hairline anywhere. Applies on BOTH desktop and mobile (identical cqh geometry; mobile just scales the group). */
  /* -- the expanded body inside the top row (Figma step 3): line chart + orders.yml terminal. ABSOLUTE (out of flow) so opening COVERS the rows below instead of pushing them down; height-clipped → mcOpenUp reveals it top→bottom -- */
  .gbScreen .mcOpen { position: absolute; left: -0.08cqh; right: -0.08cqh; top: 6.6cqh; width: calc(100% + 0.16cqh); height: 40cqh; overflow: hidden; box-sizing: border-box; background: #fff; border-left: 0.08cqh solid transparent; border-right: 0.08cqh solid transparent; border-radius: 0 0 1.2cqh 1.2cqh; }   /* the body card AND the height-clip in one: as mcOpenUp grows its height, its rounded bottom corners ride the descending edge → the bottom curvature is constant through the whole descent (no square→round snap at the end). LEFT/RIGHT borders only, kept for geometry but TRANSPARENT — the visible stroke is the single .mcRow--top::after outline (two independent borders sub-pixel-mismatch → stray hairline; see .mcRow--top). NO bottom border: with one, the collapsed height:0 box still kept a 0.08cqh-tall border-box whose WHITE bg (bg paints under transparent borders) drew a ~0.8px strip across the header's bottom — poking past the rounded bottom corners as THE stray white line during the whole collapsed/transition phase. Without it, height:0 → 0-tall border-box → paints nothing; the bg bottom edge at height 40cqh is unchanged (border-box sizing). Explicit width = header border-box so it's exactly as wide as the header (flush). */
  .gbScreen .mcOpenIn { box-sizing: border-box; height: 40cqh; padding: 1.4cqh 1.6cqh; display: flex; flex-direction: column; gap: 1.4cqh; }   /* content wrapper only — the frame lives on .mcRow--top::after; fixed 40cqh so the chart+terminal render at full size and are revealed (not squashed) by the growing clip. Horizontal padding 1.6cqh = .mcRowHead's own 1.6cqh → the chart box + terminal left/right edges line up with the header content (concentric interior frames, one consistent card) */
  .gbScreen .mcChartBox { flex: none; height: 16.6cqh; border: 0.08cqh solid #e9ecef; border-radius: 0.9cqh; padding: 1cqh 1.3cqh 0.9cqh; display: flex; flex-direction: column; }   /* the chart gets its own subtle frame, one step lighter than the card border, like the Figma popover */
  .gbScreen .mcLegend { flex: none; display: flex; justify-content: flex-end; gap: 1.5cqh; padding-right: 0.4cqh; }   /* legend right-aligned (matches the Figma) */
  .gbScreen .mcLegItem { display: inline-flex; align-items: center; gap: 0.5cqh; font: 500 1cqh/1 'Inter', sans-serif; color: #5c5f66; white-space: nowrap; }
  .gbScreen .mcSw { width: 0.8cqh; height: 0.8cqh; border-radius: 50%; flex: none; }
  .gbScreen .mcChart { flex: 1; min-height: 0; margin-top: 0.9cqh; display: flex; gap: 0.8cqh; }
  .gbScreen .mcYAxis { flex: none; width: 2.2cqh; display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; margin: -0.47cqh 0; font: 400 0.95cqh/1 'Inter', sans-serif; color: #535862; font-variant-numeric: tabular-nums; }   /* negative half-line margins → each muted tabular label centres on its gridline */
  .gbScreen .mcRules i { display: block; height: 0.08cqh; background: #f0f1f5; }   /* gridlines lighter than the frame so they recede behind the data */
  .gbScreen .mcLine { stroke: #7262ff; stroke-width: 1.6; }   /* "Current year" */
  .gbScreen .mcLine--prev { stroke: #d5d0ff; }   /* "Previous year" — drawn first so the current-year line passes over it */
  .gbScreen .mcXAxis { flex: none; display: flex; justify-content: space-between; margin: 0.55cqh 0 0 3cqh; font: 400 0.95cqh/1 'Inter', sans-serif; color: #535862; }   /* left margin = Y-axis gutter (2.2) + plot gap (0.8) so the month labels sit under the plot */
  /* -- the orders.yml terminal (Figma 1:16111): dark shell · header bar · numbered JetBrains Mono code that types in -- */
  .gbScreen .mcTerm { flex: none; height: 18.8cqh; background: #181818; border: 0.1cqh solid #191922; border-radius: 0.7cqh; padding: 0; display: flex; flex-direction: column; overflow: hidden; }   /* no padding → bar + body span edge-to-edge (seamless, like the BI terminal); overflow:hidden clips them to the rounded corners */
  .gbScreen .mcTermBar { flex: none; background: #3a3a3a; border-bottom: 0.1cqh solid #0f0f0f; padding: 0.55cqh 1cqh; font: 400 1.4cqh/1.2 'JetBrains Mono', monospace; color: #fff; text-align: center; }   /* orders.yml — full-width bar with a hairline divider, matching the BI title bar (no inset stroke around it) */
  .gbScreen .mcTermBody { flex: 1; min-height: 0; background: #181818; padding: 0.6cqh 1cqh 0.4cqh; font: 400 1.5cqh/1.5 'JetBrains Mono', monospace; color: #c7c7c7; }   /* BI terminal body colour; line-height 1.5 uses the leftover bottom space + gives descenders (Y in DAY) room */
  .gbScreen .mcCLine { display: flex; }
  .gbScreen .mcLn { flex: none; width: 2ch; margin-right: 1.3ch; text-align: right; color: #656565; }   /* line-number gutter (BI muted) */
  .gbScreen .mcCode { white-space: pre; padding-right: 1.5ch; }   /* typed via clip-path (horizontal-only) — nothing trimmed vertically; padding-right guarantees the last char (Y in DAY) is never right-clipped */
  .gbScreen .mcKw { color: #37b24d; }   /* default_time_dimension — BI string green */
  .gbScreen .mcHl { color: #03d6e5; border: 0.08cqh solid #03d6e5; background: rgba(3, 214, 229, 0.14); border-radius: 0.3cqh; padding: 0 0.45ch; }   /* order_date in a highlight box — BI key cyan */
  .gbScreen .mcCursor { position: absolute; z-index: 10; left: 44cqh; top: 2.5cqh; width: 3cqh; height: auto; opacity: 0; transform-origin: 18% 10%; pointer-events: none; filter: drop-shadow(0 0.25cqh 0.6cqh rgba(0, 0, 0, 0.35)); }   /* rest spot = over the top row's description (Figma step 2); base opacity 0 — the cursor is gone in the final state */
  /* -- Metrics-catalog intro animation (mcAnimate() on entering carousel slide 3). Timeline (ms): rows pop 0–860 (90ms stagger) · cursor flies in 700, presses ≈1850, fades by 2500 · other rows dim + click pulse 1850 · top row expands 2450–3200 · chart lines draw 2750–3850 · code types from ≈3050 (per-line delays inline). Every hidden state is the animations' `backwards` fill — WITHOUT .mcPlay the slide renders fully (safe default; also the reduced-motion render). -- */
  .gbScreen .mcComp.mcPlay .mcRow { animation: mcPop 0.5s var(--md, 0ms) both cubic-bezier(0.2, 0.7, 0.2, 1.1); }
  @keyframes mcPop { from { opacity: 0; transform: translateY(7cqh); } to { opacity: 1; transform: none; } }   /* shoot UP from below into place (same feel as biPop) */
  .gbScreen .mcComp.mcPlay .mcRow--dim { animation: mcPop 0.5s var(--md, 0ms) both cubic-bezier(0.2, 0.7, 0.2, 1.1), mcDim 0.4s 1850ms forwards ease; }   /* dim fills FORWARDS only — a backwards fill would pin opacity:1 through the pop-in fade */
  @keyframes mcDim { from { opacity: 1; } to { opacity: 0.35; } }
  .gbScreen .mcComp.mcPlay .mcRow--top { animation: mcPop 0.5s both cubic-bezier(0.2, 0.7, 0.2, 1.1), mcClick 0.3s 1850ms ease, mcHeaderRound 0.35s 2450ms both ease; }   /* header bottom corners: white bg rounded through the collapsed phase, straighten as the body opens (connected). No divider fade any more — the header/body hairline is gone (single seamless card; the old mcHeaderLine border-bottom read as a stray line at the header's bottom). */
  @keyframes mcHeaderRound { from { border-bottom-left-radius: 1.2cqh; border-bottom-right-radius: 1.2cqh; } to { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } }
  .gbScreen .mcComp.mcPlay .mcRow--top::after { animation: mcOutlineGrow 0.75s 2450ms both cubic-bezier(0.3, 0, 0.2, 1); }   /* sync the outline's UNFOLD with mcOpenUp (same 0.75s/2450ms/easing) so the rounded bottom descends in lockstep with the revealing body — wraps just the header while collapsed, then grows to the full card */
  @keyframes mcOutlineGrow { from { height: calc(6.6cqh + 0.16cqh); } to { height: calc(6.6cqh + 40cqh + 0.08cqh); } }   /* from = the header's full border box (its bg bottom edge); to = .mcOpen's bg bottom edge (6.6 + 40, measured from the -0.08 top) — the outline's box edge sits ON the white bg edge at both ends so the matching 1.2cqh radii nest exactly (no corner sliver, no dark gap) */
  @keyframes mcClick { 0%, 100% { transform: none; box-shadow: none; } 40% { transform: scale(0.985); box-shadow: 0 0 0 0.4cqh rgba(114, 98, 255, 0.22); } }   /* click feedback: quick press-in + a purple focus-ring blip */
  .gbScreen .mcComp.mcPlay .mcOpen { animation: mcOpenUp 0.75s 2450ms both cubic-bezier(0.3, 0, 0.2, 1); }
  @keyframes mcOpenUp { from { height: 0; } to { height: 40cqh; } }   /* the top row opens downward to reveal chart + terminal */
  .gbScreen .mcComp.mcPlay .mcCursor { animation: mcCursor 1.8s 700ms both cubic-bezier(0.3, 0.05, 0.25, 1); }
  @keyframes mcCursor {
    0% { opacity: 0; transform: translate(34cqh, 44cqh) scale(1); }   /* enters from off the lower-right */
    12% { opacity: 1; }
    58% { transform: translate(0cqh, 0cqh) scale(1); }                /* arrives over the top row */
    64% { transform: translate(0cqh, 0cqh) scale(0.8); }              /* press (≈1850ms — the dim + click pulse fire here) */
    71% { transform: translate(0cqh, 0cqh) scale(1); }                /* release */
    86% { opacity: 1; transform: translate(0cqh, 0cqh) scale(1); }
    100% { opacity: 0; transform: translate(0cqh, 0cqh) scale(1); }   /* slips away before the expand */
  }
  .gbScreen .mcComp.mcPlay .mcSvg { animation: mcLineDraw 1.1s 2750ms both cubic-bezier(0.25, 0.1, 0.3, 1); }
  @keyframes mcLineDraw { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }   /* both series DRAW on left→right as the box opens (same as the BI growth lines) */
  .gbScreen .mcComp.mcPlay .mcCode { animation-name: mcTypeIn; animation-fill-mode: both; }   /* per-line duration/delay/steps() set inline, computed from char counts (like .biTLine) */
  @keyframes mcTypeIn { from { clip-path: inset(-40% 100% -40% 0); } to { clip-path: inset(-40% -4% -40% 0); } }   /* reveal left→right (steps() per char); top/bottom insets are NEGATIVE so the clip never trims a glyph vertically (the Y's descender was being cut at the line-box bottom); right over-reveals -4% so the last char is fully shown */
  .gbScreen .mcComp.mcPlay .mcLn { animation: mcNumIn 0.15s var(--nd, 0ms) both ease; }   /* each line number fades in with its line */
  @keyframes mcNumIn { from { opacity: 0; } to { opacity: 1; } }
  /* "Explore Metrics" CTA — straddles the bottom-centre EDGE of the expanded card (top = header 6.6 + open 40 = 46.6cqh, then translateY(-50%)); pixel button reusing .cta. Base = visible (safe default); the intro fades it in AFTER the box has finished descending. */
  .gbScreen .mcCta { left: 50%; right: auto; top: 46.6cqh; bottom: auto; transform: translate(-50%, -50%); z-index: 12; opacity: 1; background: #5B5BD6; color: #fff; font-size: calc(1.6cqh / 1.56); padding: calc(2.4cqh / 1.56) calc(2.8cqh / 1.56); }   /* font/padding divided by the .mcGroup scale (1.56) → RENDERS at the shared CTA size (1.6cqh / 2.4×2.8), matching every other product CTA */
  .gbScreen .mcComp.mcPlay .mcCta { animation: mcCtaIn 0.45s 3450ms both cubic-bezier(0.2, 0.7, 0.2, 1.1); }   /* backwards fill keeps it hidden until 3450ms — just after the expand (2450+750) settles */
  @keyframes mcCtaIn { from { opacity: 0; transform: translate(-50%, calc(-50% + 1.4cqh)); } to { opacity: 1; transform: translate(-50%, -50%); } }
  /* ---- Product carousel slide 4: "Embedding" — a mock THIRD-PARTY app ("Bolt Inc.") with a Lightdash chart embedded in its dashboard. Faithful rebuild of Figma thSruvgzhdYk47KSTBCFEa node 0:15 (830×727 → cqh at 0.09cqh/px): left sidebar (brand · search · nav) + main (header · embed area · inventory table). The embed area plays a 3-step intro (embAnimate): a dashed placeholder marks the mount point → a light-purple sheen sweeps + the bolt pixel-resolves in → the real embedded chart takes over (KPI rolls up, order line flies upward). Base CSS is the FINAL embedded-chart state, so the slide renders fully without the trigger. Pure CSS + inline SVG — the only image is the existing ld-logo.svg bolt. ---- */
  .gbScreen .embGroup { position: absolute; inset: 0; transform: translate(var(--emg-x, 0cqh), var(--emg-y, 6.5cqh)) scale(var(--emg-s, 1.05)); transform-origin: center center; }   /* group transform (mirrors .mcGroup/--mcg-*) so the whole visual moves/scales as ONE piece; default +6.5cqh drop clears the fixed vhead title */
  .gbScreen .embComp { position: absolute; left: 50%; top: 50%; width: 74.7cqh; height: 65.4cqh; transform: translate(-50%, -50%); }   /* fixed-size composition box (Figma 830×727 × 0.09) centred in the slide — sidebar + main are cqh children so proportions hold at every screen size */
  .gbScreen .embCta { position: absolute; left: 50%; right: auto; top: 65.4cqh; bottom: auto; transform: translate(-50%, -50%); z-index: 12; opacity: 1; background: #5B5BD6; color: #fff; font-size: calc(1.6cqh / 1.05); padding: calc(2.4cqh / 1.05) calc(2.8cqh / 1.05); }   /* "Learn more" CTA straddling the bottom-centre EDGE of the embedding visual — child of .embComp so it rides the .embGroup 1.05 scale; font/padding ÷1.05 → renders at the shared CTA size (1.6cqh / 2.4×2.8), matching .agentCta/.biCta/.mcCta */
  .gbScreen .embCta:hover, .gbScreen .embCta.hot { background: #4a3fc9; }
  /* -- the app window: sidebar | main, rounded, soft drop shadow (the only shadow — it's the floating third-party window) -- */
  .gbScreen .embApp { position: absolute; inset: 0; display: flex; background: #f7f8fb; border: 0.08cqh solid #dedfe3; border-radius: 2.9cqh; box-shadow: 0 0.3cqh 1.5cqh rgba(0,0,0,0.1); overflow: hidden; font-family: 'Inter', sans-serif; }   /* overflow:hidden clips the sidebar/main fills + the intro pops to the rounded window */
  .gbScreen .embSide { flex: none; width: 20.5cqh; box-sizing: border-box; background: #f5f5f7; border-right: 0.08cqh solid #dedfe3; padding: 2.5cqh; display: flex; flex-direction: column; gap: 1.45cqh; }   /* Figma 228px wide, 27.9px padding, one 16px gap token */
  .gbScreen .embBrand { display: flex; align-items: center; gap: 1.1cqh; font: 500 1.44cqh/1.2 'Inter', sans-serif; color: #000; }
  .gbScreen .embB { flex: none; width: 2.9cqh; height: 2.9cqh; border-radius: 0.72cqh; background: #000; color: #fff; display: flex; align-items: center; justify-content: center; font: 700 1.6cqh/1 'Inter', sans-serif; }   /* the Figma "B" tile is Space Grotesk — existing Inter bold instead (no new fonts) */
  .gbScreen .embSearch { display: flex; align-items: center; gap: 0.8cqh; background: #fff; border: 0.08cqh solid rgba(2,2,16,0.1); border-radius: 0.55cqh; padding: 0.56cqh 1.13cqh; font: 400 1.17cqh/1.4 'Inter', sans-serif; color: #8897ae; }
  .gbScreen .embSearch svg { flex: none; width: 1.25cqh; height: 1.25cqh; color: #8897ae; }
  .gbScreen .embNav { display: flex; flex-direction: column; gap: 1.45cqh; }   /* same 16px gap token as the sidebar column */
  .gbScreen .embNavRow { display: flex; align-items: center; gap: 0.9cqh; padding: 0.85cqh 1.13cqh; border-radius: 0.55cqh; font: 500 1.05cqh/1.3 'Inter', sans-serif; color: #344054; opacity: 0.6; }   /* inactive items knocked back (Figma opacity 0.6) */
  .gbScreen .embNavRow svg { flex: none; width: 1.3cqh; height: 1.3cqh; color: #98a2b3; }   /* muted gray glyphs (the mcIco gray) */
  .gbScreen .embNavRow.sel { background: #fff; color: #020210; opacity: 1; box-shadow: 0 0.1cqh 0.3cqh rgba(2,2,16,0.06); }   /* "Dashboard" — the ACTIVE page (this is where Lightdash is embedded) */
  .gbScreen .embMain { flex: 1; min-width: 0; box-sizing: border-box; background: #fff; padding: 2.5cqh; display: flex; flex-direction: column; gap: 1.8cqh; }   /* Figma 27.9px padding, one 19.8px gap token between header / embed / heading / table */
  .gbScreen .embEyebrow { font: 400 1.26cqh/1.3 'Inter', sans-serif; color: #666d80; }
  .gbScreen .embTitle { margin-top: 0.9cqh; font: 500 1.67cqh/1.2 'Inter', sans-serif; color: #020210; }
  /* -- THE EMBED AREA (Figma 552.75×361.45 ≈ 1.53:1) — the animated centrepiece. Final chart + intro placeholder are stacked absolute layers; the placeholder paints on top and is opacity:0 at rest -- */
  .gbScreen .embEmbed { position: relative; flex: none; height: 32.5cqh; }
  .gbScreen .embPh { position: absolute; inset: 0; box-sizing: border-box; border: 0.18cqh dashed #7c5cfc; border-radius: 1.1cqh; background: linear-gradient(135deg, rgba(124,92,252,0.02), rgba(124,92,252,0.09)); display: flex; align-items: center; justify-content: center; overflow: hidden; opacity: 0; pointer-events: none; }   /* step-1 mount marker — hidden at REST (the resting state is the embedded chart); embPhIn/embPhOut run it only during the intro */
  .gbScreen .embBolt { width: 7.2cqh; height: auto; display: block; }   /* the Lightdash bolt (ld-logo.svg), centred in the placeholder */
  .gbScreen .embSheen { position: absolute; inset: -30% -60%; background: linear-gradient(115deg, transparent 38%, rgba(170,150,255,0.28) 47%, rgba(215,205,255,0.6) 50%, rgba(170,150,255,0.28) 53%, transparent 62%); opacity: 0; pointer-events: none; }   /* the step-2 light-purple highlight band; oversized so the diagonal sweep clears both edges */
  /* -- the embedded Lightdash chart (the FINAL resting state): headline KPI + rising "orders per week" line — styled like the BI/MC charts -- */
  .gbScreen .embChart { position: absolute; inset: 0; box-sizing: border-box; background: #fff; border: 0.08cqh solid #e1e4ea; border-radius: 1.1cqh; padding: 1.5cqh 1.7cqh 1.2cqh; display: flex; flex-direction: column; }   /* same card border/radius family as the MC rows + BI cards */
  .gbScreen .embChartHead { flex: none; display: flex; align-items: flex-start; justify-content: space-between; }
  .gbScreen .embKpiLabel { font: 500 1.2cqh/1.25 'Inter', sans-serif; color: #212529; }
  .gbScreen .embValRow { display: flex; align-items: center; gap: 1cqh; margin-top: 0.6cqh; }
  .gbScreen .embVal { font: 600 2.9cqh/1 'Inter', sans-serif; color: #212529; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }   /* tabular so the biCountUp roll doesn't jitter (matches biVal) */
  .gbScreen .embBadge { background: #f4fbfb; border-radius: 1.5cqh; padding: 0.5cqh 0.8cqh; font: 500 1.05cqh/1 'Inter', sans-serif; color: #37b24d; white-space: nowrap; }   /* KPI delta badge — the biBadge green (one green for "positive delta" everywhere) */
  .gbScreen .embLegend { display: flex; justify-content: flex-end; gap: 1.3cqh; padding-top: 0.3cqh; }   /* right-aligned dot legend, like every other chart */
  .gbScreen .embLegItem { display: inline-flex; align-items: center; gap: 0.5cqh; font: 400 1.05cqh/1 'Inter', sans-serif; color: #6b6b75; white-space: nowrap; }
  .gbScreen .embSw { width: 0.85cqh; height: 0.85cqh; border-radius: 50%; flex: none; }
  .gbScreen .embChartArea { flex: 1; min-height: 0; margin-top: 1.2cqh; display: flex; gap: 0.8cqh; }
  .gbScreen .embYAxis { flex: none; width: 3cqh; display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; margin: -0.5cqh 0; font: 400 1cqh/1 'Inter', sans-serif; color: #535862; font-variant-numeric: tabular-nums; }   /* negative half-line margins → each muted tabular label centres on its gridline */
  .gbScreen .embRules i { display: block; height: 0.08cqh; background: #f0f1f5; }   /* gridlines lighter than the card border so they recede */
  .gbScreen .embLine { stroke: #7262ff; stroke-width: 1.6; }   /* "Current period" — the MC/LD line purple */
  .gbScreen .embLine--prev { stroke: #d5d0ff; }   /* "Previous period" — drawn first so the current line passes over it */
  .gbScreen .embXAxis { flex: none; display: flex; justify-content: space-between; margin: 0.55cqh 0 0 3.8cqh; font: 400 1cqh/1 'Inter', sans-serif; color: #535862; }   /* left margin = Y-axis gutter (3) + plot gap (0.8) so the months sit under the plot */
  /* -- the Inventory table (third-party app chrome, Figma colors kept as-is — it's intentionally NOT Lightdash-styled) -- */
  .gbScreen .embInvHead { font: 500 1.26cqh/1.2 'Inter', sans-serif; color: #020210; }
  .gbScreen .embTable { border: 0.08cqh solid #ebedf1; border-radius: 0.78cqh; background: #fff; overflow: hidden; }
  .gbScreen .embTr { display: grid; grid-template-columns: 2.7cqh 13.3cqh 15.5cqh 1fr 2.7cqh; align-items: center; height: 4.13cqh; padding: 0 0.9cqh; border-top: 0.08cqh solid #ebedf1; }   /* Figma 45.9px row height */
  .gbScreen .embTr--head { height: 2.7cqh; background: #f5f5f7; border-top: none; font: 500 0.84cqh/1 'Inter', sans-serif; color: #696977; }
  .gbScreen .embCheck { width: 1.15cqh; height: 1.15cqh; border: 0.08cqh solid rgba(0,0,0,0.1); border-radius: 0.21cqh; background: #fff; box-sizing: border-box; }
  .gbScreen .embId { font: 500 0.9cqh/1 'Inter', sans-serif; color: #020210; font-variant-numeric: tabular-nums; }
  .gbScreen .embDate { font: 400 0.9cqh/1 'Inter', sans-serif; color: #696977; font-variant-numeric: tabular-nums; }
  .gbScreen .embStock { justify-self: start; background: rgba(57,191,129,0.1); border: 0.08cqh solid #39bf81; color: #39bf81; border-radius: 99cqh; padding: 0.3cqh 0.75cqh; font: 500 0.84cqh/1 'Inter', sans-serif; }
  .gbScreen .embKebab { justify-self: center; color: #98a2b3; display: flex; }
  .gbScreen .embKebab svg { width: 0.4cqh; height: 1.4cqh; }
  /* -- Embedding intro animation (embAnimate() on entering carousel slide 4). Timeline (ms): app chrome pops 0–840 (staggered) · dashed placeholder fades in 250–650 · sheen sweeps 900–1400 · bolt pixel-resolves 1000–1450 · placeholder + bolt fade out 2050–2400 · chart card rises in 2100–2600 · KPI pops 2150 + rolls up 2200–3200 (biCountUp) · order line flies up + draws on 2300–3300. Every hidden state is the animations' `backwards` fill — WITHOUT .embPlay the slide renders fully (safe default; also the reduced-motion render). -- */
  .gbScreen .embComp.embPlay .embPop { animation: mcPop 0.5s var(--pd, 0ms) both cubic-bezier(0.2, 0.7, 0.2, 1.1); }   /* app chrome shoots up from below into place (the MC/BI pop) */
  .gbScreen .embComp.embPlay .embPh { animation: embPhIn 0.4s 250ms both cubic-bezier(0.2, 0.7, 0.2, 1.1), embPhOut 0.35s 2050ms forwards ease; }   /* fade/scale in, hold through the sheen + bolt, then fade out as the chart takes over — embPhOut is declared LAST so its forwards fill wins over embPhIn's from 2050ms (the mcPop+mcDim layering trick) */
  @keyframes embPhIn { from { opacity: 0; transform: scale(0.985); } to { opacity: 1; transform: none; } }
  @keyframes embPhOut { from { opacity: 1; } to { opacity: 0; } }
  .gbScreen .embComp.embPlay .embSheen { animation: embSheen 0.5s 900ms both linear; }
  @keyframes embSheen { 0% { opacity: 0; transform: translateX(-52%); } 12% { opacity: 1; } 88% { opacity: 1; } 100% { opacity: 0; transform: translateX(52%); } }   /* one quick diagonal light-purple sweep across the placeholder; ends transparent so only the intro ever shows it */
  .gbScreen .embComp.embPlay .embBolt { animation: atImgPop 0.45s 1000ms cubic-bezier(.34,1.5,.5,1) both; }   /* the bolt pops + pixel-resolves with the SAME px-a→px-b→none chain as the agent tiles */
  html.touch .gbScreen .embComp.embPlay .embBolt { animation: atImgPopLite 0.3s 1000ms steps(6) both; }   /* MOBILE: no feMorphology filters (brutally slow on phone GPUs) — same fallback as the tiles */
  .gbScreen .embComp.embPlay .embChart { animation: embChartIn 0.5s 2100ms both cubic-bezier(0.2, 0.7, 0.2, 1.1); }
  @keyframes embChartIn { from { opacity: 0; transform: translateY(3cqh); } to { opacity: 1; transform: none; } }   /* the real chart resolves in under the fading placeholder */
  .gbScreen .embComp.embPlay .embSvg { animation: embLineFly 1s 2300ms both cubic-bezier(0.25, 0.1, 0.3, 1); }
  @keyframes embLineFly { from { clip-path: inset(0 100% 0 0); transform: translateY(14%); } to { clip-path: inset(0 0 0 0); transform: none; } }   /* the order line DRAWS on left→right (the BI/MC wipe) while the whole path rises — it "flies upward" into place */
  .gbScreen .pcarPlaceholder { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; padding: 0 12cqh;
    color: rgba(230,228,244,0.55); font: 600 3.2cqh/1.4 'Inter', sans-serif; }
  .gbScreen .daCta { position: absolute; left: auto; right: var(--dcta-x, 6.5%); bottom: var(--dcta-y, -5%); transform: scale(var(--dcta-s, 1)); transform-origin: bottom right; opacity: 1; z-index: 12; background: #5B5BD6; color: #fff; }   /* Data Apps CTA — CHILD of .daStage, so it rides the card's transform (--vs-x/y/s + group --dg-s). Position is a %  OF THE CARD BOX (not cqh) → it holds the same relative spot as the card reflows/reaspects on resize + mobile, moving AS ONE with the visual instead of drifting. --dcta-s is a fixed size multiplier that also rides the group scale. Dev UI: #daPanel → BOOK-A-DEMO CTA. */
  .gbScreen .daCta:hover, .gbScreen .daCta.hot { background: #4a3fc9; color: #fff; }   /* hover: slightly darker purple */
  .gbScreen .placeholder {
    flex: 1; display: flex; align-items: center; justify-content: center;
    color: var(--muted); font-size: 3.2cqh; font-weight: 600; letter-spacing: 0.08em;
  }
  /* while browsing a view, get the dev panel out of the way so nothing overlaps the screen */
  #panel { transition: opacity .3s ease, transform .3s ease; }
  body.viewing #panel { opacity: 1; pointer-events: auto; }   /* keep the dev controls visible while viewing a section (so the Game Boy + agent sliders stay reachable) */
  /* the UFO cursor drawn on top of the bento (drives which tile is expanded) */
  #uiCloudy {
    position: absolute; width: 13.3%; aspect-ratio: 56/48; z-index: 12;   /* match the nav Cloudy (0.464 / 3.5 of the screen); above the carousel arrows so he flies in FRONT of them */
    pointer-events: none; transform: translate(-50%, -50%);
    filter: drop-shadow(0 3px 8px rgba(126,224,168,0.55)); display: none;   /* green outer glow to match his thrusters */
  }
  #uiCloudy.on { display: block; }
  #uiCloudy.agentpilot { transition: left .42s cubic-bezier(.34,1.35,.5,1), top .42s cubic-bezier(.34,1.35,.5,1); z-index: 6; }   /* zips playfully along the tab bar */
  #uiCloudy canvas { width: 100%; height: 100%; image-rendering: pixelated; position: relative; }
  /* carousel BOOST: a bright thrust trail streams off Cloudy in the travel direction as the slide flies in */
  #uiCloudy.pboost::after, .pcloudyExtra.pboost::after { content: ''; position: absolute; top: 22%; height: 56%; z-index: -1; pointer-events: none; image-rendering: pixelated;   /* band matches the procedural pixel sprite: the trail streams off the saucer's mid-band */
    -webkit-mask-image: var(--screen-tex); mask-image: var(--screen-tex); -webkit-mask-size: 4px 4px; mask-size: 4px 4px; -webkit-mask-repeat: repeat; mask-repeat: repeat;   /* chunkier Bayer pixel-dither punched through the streak (same dither as the screen/tiles), more prominent */
    animation: pboostTrail .86s ease-out forwards; }
  #uiCloudy.pboost[data-boost="r"]::after, .pcloudyExtra.pboost[data-boost="r"]::after { right: 38%; width: 560%; transform-origin: right center; background: linear-gradient(270deg, rgba(224,255,238,1) 0%, rgba(126,224,168,1) 32%, rgba(126,224,168,0.82) 60%, rgba(126,224,168,0.45) 82%, rgba(126,224,168,0) 100%); }
  #uiCloudy.pboost[data-boost="l"]::after, .pcloudyExtra.pboost[data-boost="l"]::after { left: 38%; width: 560%; transform-origin: left center; background: linear-gradient(90deg, rgba(224,255,238,1) 0%, rgba(126,224,168,1) 32%, rgba(126,224,168,0.82) 60%, rgba(126,224,168,0.45) 82%, rgba(126,224,168,0) 100%); }
  @keyframes pboostTrail { 0% { opacity: 0; transform: scaleX(0.25); } 18% { opacity: 1; } 100% { opacity: 0; transform: scaleX(1.5); } }
  /* mobile Product: on a page-change dash, each Cloudy leans FORWARD in the travel direction for a beat, then rights itself (applied to the INNER sprite so the outer positioning/scale is untouched; matches the desktop velocity-tilt sign: right→negative) */
  @keyframes pboostTiltR { 0% { transform: rotate(0deg); } 30% { transform: rotate(-15deg); } 100% { transform: rotate(0deg); } }
  @keyframes pboostTiltL { 0% { transform: rotate(0deg); } 30% { transform: rotate(15deg); } 100% { transform: rotate(0deg); } }
  .mfill .gbScreen:has(.pcarView) #uiCloudy.pboost[data-boost="r"] .uiCloudyInner,
  .mfill .gbScreen:has(.pcarView) .pcloudyExtra.pboost[data-boost="r"] canvas { animation: pboostTiltR 0.6s cubic-bezier(.3,.7,.3,1) both; }
  .mfill .gbScreen:has(.pcarView) #uiCloudy.pboost[data-boost="l"] .uiCloudyInner,
  .mfill .gbScreen:has(.pcarView) .pcloudyExtra.pboost[data-boost="l"] canvas { animation: pboostTiltL 0.6s cubic-bezier(.3,.7,.3,1) both; }
  /* Pricing evolution forms — bigger sprites over the baby canvas; Cloudy grows per tier. Glitch-in reuses the agents' avPixIn. */
  #uiCloudy .pCloudyForm { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); height: auto; image-rendering: pixelated; display: none; z-index: 1; }
  #uiCloudy.pstate-med .pCloudyMed { display: block; width: 138%; }
  #uiCloudy.pstate-mother .pCloudyMother { display: block; width: 188%; }
  #uiCloudy .uiCloudyInner { position: absolute; inset: 0; }   /* holds the tilt so uiCloudy stays untransformed for its clip-path */
  #uiCloudy.pstate-med .uiCloudyInner > canvas, #uiCloudy.pstate-mother .uiCloudyInner > canvas { visibility: hidden; }
  @keyframes avPixInDark { 0% { opacity: 0; filter: url(#px-lg-a); } 26% { opacity: 1; filter: url(#px-lg-a); } 55% { filter: url(#px-lg-b); } 82% { filter: url(#px-lg-c); } 100% { opacity: 1; filter: none; } }   /* Cloudy's palette-safe pixelate glitch (restored — a size-reduction pass wrongly deleted this keyframe while .pglitch still references it, which killed the evolve glitch) */
  #uiCloudy .pCloudyForm.pglitch { animation: avPixInDark .42s cubic-bezier(.32,.72,.35,1) both; }   /* palette-safe glitch: pixelate filters ONLY, no contrast/brightness boost (which was shifting Cloudy's colours outside his palette) */
  #uiCloudy .uiCloudyInner > canvas.pglitch { animation: avPixInDark .42s cubic-bezier(.32,.72,.35,1) both; }   /* palette-safe glitch: pixelate filters ONLY, no contrast/brightness boost (which was shifting Cloudy's colours outside his palette) */
  /* the two flanking baby Cloudys in the Enterprise (mothership) state */
  .gbScreen .pOrbit { position: absolute; width: 7.3%; aspect-ratio: 56/48; z-index: 7; pointer-events: none; transform: translate(-50%, -50%); image-rendering: pixelated; display: none; }   /* no drop-shadow — it isn't clipped by clip-path and would leak past the card */
  .gbScreen .pOrbit.on { display: block; }
  .gbScreen .pOrbit.pglitch { animation: avPixInDark .42s cubic-bezier(.32,.72,.35,1) both; }   /* palette-safe glitch: pixelate filters ONLY, no contrast/brightness boost (which was shifting Cloudy's colours outside his palette) */
  /* ---- Pixel-glitch: now scoped to CONVERSATIONAL ANALYTICS ONLY (per the user — removed from Data Apps / BI / Metrics Catalog / Embedding). It plays on the WHOLE .agentVisual BOX (not the inner .avis panel) so the white/platform background resolves in TOGETHER with the content — no bare-white frame flashing behind the windows while the panel fades from opacity 0. Fires on slide-in AND on each platform-tab pick (showAgentVisual). One .42s one-shot, standard remove→reflow→re-add retrigger; opacity/filter only, so .agentVisual's transform is untouched. ---- */
  .gbScreen .avis.pglitch { animation: avPixInDark .42s cubic-bezier(.32,.72,.35,1) both; }   /* glitch the CONTENT panel only — the .agentVisual white window CASING stays crisp/stable; only the Slack thread + chart pixel-resolve */
  /* MOBILE: NO glitch on tab switch (per the user — the mobile pixel effect never read right). .avis stays fully opaque (no feMorphology fade → no white-casing bleed) and the mask reveal is inert → the platform panel just swaps cleanly. Desktop keeps the avPixInDark glitch. */
  html.touch .gbScreen .avis.pglitch { animation: none; }   /* MOBILE: content stays opaque — clean swap, no white casing bleed */
  html.touch .gbScreen .agentVisual.mglitch { animation: none; }   /* MOBILE: mask reveal removed — showAgentVisual() still toggles .mglitch but it's a no-op */
  @keyframes avMaskGlitch {
    0%, 24%   { -webkit-mask-image: var(--avmask-1); mask-image: var(--avmask-1); }
    25%, 49%  { -webkit-mask-image: var(--avmask-2); mask-image: var(--avmask-2); }
    50%, 74%  { -webkit-mask-image: var(--avmask-3); mask-image: var(--avmask-3); }
    75%, 100% { -webkit-mask-image: none; mask-image: none; }
  }   /* mask-image is a discretely-animated property → it flips at the segment boundaries (duplicated keyframes pin the holds); ends on none so the settled window has NO mask at all */
  /* tractor beam: a green PIXEL cone from Cloudy's underside — a true green duotone over the box content + visible green rays, fading out at the bottom */
  .gbScreen #migBeam { position: absolute; z-index: 4; width: 38cqh; height: 62cqh; pointer-events: none; opacity: 0;
    transform: translate(-50%, 0) rotate(var(--brot, 0rad)); transform-origin: top center; transition: opacity .28s ease; }   /* banks with Cloudy (--brot set per-frame → instant) */
  .gbScreen #migBeam.on { opacity: 1; }
  .gbScreen #migBeam .mbInner { position: absolute; inset: 0; transform: scaleY(0.3); transform-origin: top center; transition: transform .4s cubic-bezier(.35,0,.2,1); }   /* the grow-in stays smooth here, independent of the tilt */
  .gbScreen #migBeam.on .mbInner { transform: scaleY(1); }
  /* both layers use the BAKED Bayer-dithered green cone (same aesthetic as the glare + tile shadow); translucent so content reads through */
  .gbScreen #migBeam .mbRay, .gbScreen #migBeam .mbDuo { position: absolute; inset: 0; background-image: var(--beam-tex); background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; }
  .gbScreen #migBeam .mbRay { mix-blend-mode: screen; opacity: 0.72; animation: beamGlow 2.6s ease-in-out infinite; }   /* visible green rays, gently glowing */
  .gbScreen #migBeam .mbDuo { mix-blend-mode: color; opacity: 0.6; }   /* green duotone remap of the content */
  @keyframes beamGlow { 0%, 100% { opacity: 0.6; } 50% { opacity: 0.85; } }   /* very subtle brightness pulse = a soft glow */
  #uiLoader {
    position: absolute; width: 3.7%; aspect-ratio: 1; z-index: 13;   /* desktop: a smaller load wheel — stays above Cloudy */
    pointer-events: none; transform: translate(-50%, -50%); display: none;
  }
  #uiLoader.on { display: none; }   /* LOAD WHEEL REMOVED (per the user): the dwell ring never renders even when code still toggles .on (the dwell trigger itself is disabled via OPEN_MS=Infinity) */
  #uiLoader canvas { width: 100%; height: 100%; image-rendering: pixelated; }

  /* ---- boot / title screen: shows once the cartridge is inserted (like a game powering on) ---- */
  #boot {
    position: fixed; left: 0; top: 0; width: 0; height: 0; overflow: hidden;
    border-radius: 6cqh;
    container-type: size; z-index: 6; pointer-events: none; opacity: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5.2cqh;
    transition: opacity .55s steps(7);   /* chunky pixel-style fade-in on insert */
  }
  /* (stars now render on the WebGL screen, not as a DOM layer) */
  #boot.on { opacity: 1; }   /* NOT pointer-events:auto — the boot must let drags fall through to the canvas so you can tilt; only START catches clicks */
  #boot::after {   /* pixel-dither texture over the whole title screen (matches the nav/customers pixel look) */
    content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none;
    background: var(--screen-tex); background-size: 4px 4px; background-repeat: repeat; image-rendering: pixelated; opacity: 0.42;
  }
  /* the corner glare ON TOP of the title content — a RECTANGULAR sibling layer (like the WebGL glare plane), mapped to the screen via matrix3d, NO rounded clip so the bloom never gets cut at the corner. WebGL glare is hidden here so it never doubles. */
  #bootGlare {
    position: fixed; left: 0; top: 0; z-index: 6; pointer-events: none; opacity: 0.42; display: none;
    background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; mix-blend-mode: screen;
  }
  #bootGlare.on { display: block; }
  /* all offsets in cqh/cqw (screen-relative) so START + hint stay BAKED to the screen when it scales/zooms — never px, which would drift as the projected screen size changes */
  #boot .boot-logo { position: absolute; top: calc(15.2cqh + var(--boot-logo-y, -3.17cqh)); left: calc(50% + var(--boot-logo-x, 0cqw)); transform: translateX(-50%); width: calc(76cqw * var(--boot-logo-s, 1)); height: auto; image-rendering: pixelated; z-index: -1; }
  #boot .boot-cloudy { position: relative; left: var(--boot-cloudy-x, 0cqw); top: var(--boot-cloudy-y, 9.9cqh); }
  #boot .boot-start { position: relative; left: var(--boot-start-x, 0cqw); top: var(--boot-start-y, 6.53cqh); }
  #boot .boot-hint { position: relative; left: var(--boot-hint-x, 0cqw); top: var(--boot-hint-y, 4.75cqh); }
  #boot .boot-cloudy {
    position: relative; width: calc(44% * var(--boot-cloudy-s, 1)); aspect-ratio: 865 / 715;
    animation: bootFloat 2.6s steps(7) infinite;   /* stepped = low-framerate, old-school bob */
    pointer-events: auto; cursor: pointer;   /* click Cloudy to START */
  }
  /* CRITICAL: #boot keeps its full-screen place()/matrix3d geometry after START (only opacity fades), so these two pointer-events:auto children stayed parked over the CENTRE of the nav/views (z-index 6, ABOVE the canvas + .gbScreen) and silently swallowed clicks — the "planet centre won't click / homepage tab opens my last button" bug. Kill their hit-testing whenever we're NOT on the title screen (#boot loses .on when bootShown flips false, line ~5408). */
  #boot:not(.on) .boot-cloudy, #boot:not(.on) .boot-start { pointer-events: none !important; cursor: default; }
  #boot .boot-ufo {
    width: 100%; height: 100%; object-fit: contain; image-rendering: pixelated;
  }
  #boot .boot-start {
    pointer-events: auto; cursor: pointer; font-family: 'Press Start 2P', monospace;
    font-size: 3.6cqh; color: #fff; letter-spacing: 1px; line-height: 1;
    background: none; border: none; padding: 0.4cqh; image-rendering: pixelated;
    text-shadow: 0 0 1.6cqh rgba(150,140,255,0.5);
    animation: bootStartBlink 1.05s steps(1) infinite;
  }
  @media (hover: hover) { #boot .boot-start:hover { animation: none; opacity: 1; color: #fff; text-decoration: underline; text-underline-offset: 0.4cqh; } }   /* desktop-only hover; on touch the START keeps its idle pulse (no sticky state) */
  #boot .boot-hint { margin-top: -3.2cqh; font-family: 'Press Start 2P', monospace; font-size: 2cqh; letter-spacing: 0.5px; color: #fff; text-align: center; }
  @keyframes bootFloat { 0%, 100% { transform: translateY(-2.6%); } 50% { transform: translateY(2.6%); } }
  @keyframes bootStartBlink { 0%, 59% { opacity: 1; } 60%, 100% { opacity: 0.25; } }
  /* narrower than ~7:5 (phone / tablet-portrait / 4:3): shrink Cloudy + lift the logo so he clears it (16:9 desktop untouched); the hint reads bigger + tighter under START */
  @media (max-width: 700px), (orientation: portrait) {   /* PHONE only (matches the mobile full-fill condition) — NOT landscape tablets. A 7/5 aspect query also caught ~1.33 tablets and slapped the phone-tuned Cloudy/START sizes onto the desktop console → the oversized/overlapping "middle" view. */
    #boot { --boot-cloudy-s: var(--bcsm, 1.17); --boot-cloudy-y: var(--bcym, 17cqh); --boot-logo-y: var(--bly, -4cqh); --boot-logo-s: var(--bls, 0.96); --boot-start-y: var(--bsy, 9cqh); }   /* mobile title-screen values (dialled in via the tuner; the fallbacks are the baked defaults, overridable by #titleMobPanel setting the vars on :root) */
    #boot .boot-start { font-size: calc(3.6cqh * var(--btsm, 1.35)); }
    #boot .boot-hint { font-size: calc(2.9cqh * var(--btsm, 1)); margin-top: -4cqh; }
  }
  /* (DOM glare removed — the single pixelated WebGL glare plane covers the bento too, through the transparent gaps) */
  /* (removed the one-time power-on shine flash) */

  /* ===== Data Apps: static mini-port of "Data App Creator" (light Inter app UI on the dark screen). Slides flip; Dashboards + PDF are static for size/perf. ===== */
  .gbScreen .daview { display: flex; flex-direction: column; align-items: center; }
  /* daGroup wraps the whole composed layout (creator prompt + tabs + visual stage + cloudy + CTA) so ONE dev control can move/scale it all together; the individual element positions stay baked underneath */
  .gbScreen .daGroup { flex: 1; min-height: 0; width: 100%; display: flex; flex-direction: column; align-items: center; transform: translate(var(--dg-x, -3.5cqh), var(--dg-y, -4cqh)) scale(var(--dg-s, 1.05)); transform-origin: center center; }
  .gbScreen .daPrompt, .gbScreen .daSeg, .gbScreen .daDeck, .gbScreen .daPdf { position: relative; }   /* host the pixel-stepped .tframe ring */
  .gbScreen .daview .tframe { background-color: var(--acc); z-index: 7; }   /* purple ridged pixel corners, matching the other pages */
  .gbScreen .daview .vhead { width: 100%; }   /* header full-width so the ✕ stays top-right */
  /* Data Apps title + ✕ inherit the standard header (no per-page nudges) so they sit exactly where every other page's title/✕ sit — desktop AND mobile. */
  .gbScreen .daview .daPrompt { margin-top: -4.5cqh; }                        /* Data Apps: pull the whole content block (prompt + tabs + stage + cloudy) up a bit */
  .gbScreen .daPrompt { width: 100%; }
  .gbScreen .daSeg { width: auto; max-width: 100%; }
  /* dev-tunable position/scale for the three Data Apps elements */
  .gbScreen .daPrompt { max-width: 50cqh; transform: translate(var(--cr-x, -15cqh), var(--cr-y, 9.5cqh)) scale(var(--cr-s, 1.06)); transform-origin: center top; }
  .gbScreen .daSeg    { transform: translate(var(--tb-x, 19.5cqh), var(--tb-y, 7cqh)) scale(var(--tb-s, 1.20)); transform-origin: center top; }
  .gbScreen .daStage  { transform: translate(var(--vs-x, 7cqh), var(--vs-y, 9cqh)) scale(var(--vs-s, 0.96)); transform-origin: center top; }
  /* prompt card — white card with icon+title header, divider, prompt text */
  .gbScreen .daPrompt { box-sizing: border-box; padding: 1.2cqh 2cqh 1.5cqh; background: #fff; border: 0; border-radius: 1.6cqh; box-shadow: 0 0.5cqh 2cqh rgba(8,6,20,0.35); }
  /* Cloudy tapping at the prompt (top-right) — 3 stacked frames; idle = frame 1, types = alternates 2/3 */
  .gbScreen .daCloudy { display: none; position: absolute; top: calc(var(--dc-y, -3cqh) + 1px); right: var(--dc-x, 6cqh); width: var(--dc-s, 15.5cqh); aspect-ratio: 660 / 408; z-index: 3; pointer-events: none; }   /* hidden for now (was the tapping Cloudy peeking over the prompt) */
  .gbScreen .daCloudy .dcf { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; image-rendering: pixelated; opacity: 0; }
  .gbScreen .daCloudy .dcf1 { opacity: 1; }
  .gbScreen .daPromptHead { display: flex; align-items: center; gap: 1.1cqh; padding-bottom: 1cqh; margin-bottom: 1.1cqh; border-bottom: 1px solid #e6e4ec; }
  .gbScreen .daPromptIcon { flex: 0 0 auto; width: 2.7cqh; height: 2.7cqh; border-radius: 0.8cqh; background: #5B5BD6; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 1.7cqh; line-height: 1; }
  .gbScreen .daPromptTitle { font-family: 'Inter', sans-serif; font-weight: 600; font-size: 2cqh; letter-spacing: -0.012em; color: #1a1830; }
  .gbScreen .daPromptText { display: block; margin: 0; font-family: 'Inter', sans-serif; font-weight: 500; font-size: 2.1cqh; line-height: 1.4; letter-spacing: -0.012em; color: #1a1830; height: 5.88cqh; overflow: hidden; }   /* locked to 2 lines so the card doesn't jump as the prompt types */
  .gbScreen .daCaret { display: inline-block; font-weight: 400; color: #1a1830; animation: daBlink 1s steps(1) infinite; }
  @keyframes daBlink { 50% { opacity: 0; } }
  /* segmented control — white pill, uppercase labels + icons, near-black active pill */
  .gbScreen .daSeg { box-sizing: border-box; display: flex; align-items: center; gap: 0.8cqh; padding: 0.45cqh; }   /* uniform padding → the purple sliding pill sits with EQUAL inset from the white frame on all sides (was 0.45/0.7 = more gap left/right than top/bottom) */
  .gbScreen .daSeg::before { content: ''; position: absolute; inset: 0; z-index: -1; box-sizing: border-box; background: #fff; border: 1px solid #e6e4ec; border-radius: 99cqh; pointer-events: none; }   /* the white pill face — BEHIND the tabs (so tabs stay plain static elements → real clicks hit them under the screen's matrix3d) */
  .gbScreen .daSeg::after { content: ''; position: absolute; z-index: -2; inset: -42%; transform: translate(0.3cqh, 0.5cqh); background-image: var(--seg-shadow); background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; pointer-events: none; }   /* Bayer-dithered soft dark bloom behind the pill — same technique as the agent-tile shadow */
  .gbScreen .daTab { flex: 0 1 auto; min-width: 0; display: flex; align-items: center; justify-content: center; padding: 1cqh 2.2cqh; border: 0; border-radius: 99cqh; background: transparent; cursor: pointer;
    font-family: 'Inter', sans-serif; font-size: 1.6cqh; line-height: 1; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase; color: #1b1926; white-space: nowrap;
    text-box-trim: trim-both; text-box-edge: cap alphabetic; text-box: trim-both cap alphabetic;   /* trim the line box to the caps so equal padding centres the text vertically (uppercase + line-height:1 otherwise sits high) */
    text-indent: 0.03em; }   /* nudge right to cancel the trailing letter-spacing so it reads horizontally centred too */
  .gbScreen .daTab svg { display: none; }   /* icons removed */
  .gbScreen .daTab.is-active { color: #fff; }   /* the PURPLE comes from the sliding .daSegPill behind it (not a per-tab bg → so it can slide) */
  .gbScreen .daSegPill { position: absolute; z-index: -1; left: 0; top: 0.45cqh; bottom: 0.45cqh; width: 0; background: #5B5BD6; border-radius: 99cqh; pointer-events: none;
    transition: left .3s cubic-bezier(.4, 0, .2, 1), width .3s cubic-bezier(.4, 0, .2, 1); }   /* ONE purple pill that daPlacePill() glides between the VARIABLE-width tabs (offsetLeft/offsetWidth-measured — no equal-column trick possible here). z:-1 = above the ::before white face (later tree order) but under the static tabs' text */
    /* VERTICAL anchoring is pure CSS (top/bottom = the daSeg's 0.45cqh padding) so the top and bottom gaps to the white frame are EQUAL BY CONSTRUCTION at full subpixel precision — daPlacePill's old offsetTop/offsetHeight writes were integer-rounded against the fractional cqh layout and left the pill up to ~1px off-centre (scaled ×1.2 on desktop) */
  .gbScreen .daSeg.caSeg { display: none; }   /* Conversational Analytics' MOBILE platform selector (slide 0) — a wholesale clone of this Data Apps segmented control: same classes, so the white ::before face, the ::after --seg-shadow Bayer dither and the sliding .daSegPill all apply verbatim. Hidden on desktop (the vertical .agentTabs column serves there); .mfill shows + positions it below */
  /* format stage: only the active panel shows */
  .gbScreen .daStage { flex: 1; min-height: 0; width: 100%; max-width: 88cqh; max-height: 56cqh; position: relative; }
  .gbScreen .daFmt { display: none; height: 100%; }
  .gbScreen .daFmt.is-active { display: flex; flex-direction: column; }
  .gbScreen .daHint { flex: 0 0 auto; text-align: center; font-family: 'Inter', sans-serif; font-size: 2cqh; font-weight: 500; color: rgba(255,255,255,0.6); }
  .gbScreen .daHint b { color: #fff; font-weight: 700; }
  /* ---- Dashboards: the Revenue Forecast card (static) ---- */
  .gbScreen .daCard { flex: 1; min-height: 0; box-sizing: border-box; padding: 1.8cqh 2.2cqh 2cqh; background: #fff; border: 1px solid #e6e4ec; border-radius: 1.8cqh; box-shadow: 0 0.6cqh 2.6cqh rgba(8,6,20,0.4);
    display: flex; flex-direction: column; color: #1a1830; overflow: hidden; }
  .gbScreen .daCardTitle { display: flex; align-items: baseline; gap: 0.9cqh; font-family: 'Inter', sans-serif; font-weight: 600; font-size: 2.3cqh; letter-spacing: -0.012em; color: #1a1830; }
  .gbScreen .daCardTitle .sub { font-weight: 400; color: #85828f; }
  /* forecast controls — two bordered readout cards; static track + dot (not interactive) */
  .gbScreen .daCtrls { display: grid; grid-template-columns: 1fr 1fr; gap: 1.4cqh; margin-top: 1.3cqh; }
  .gbScreen .daCtrl { border: 0; border-radius: 1.2cqh; padding: 1cqh 0 1.5cqh; min-width: 0; }
  .gbScreen .daCtrlHead { display: flex; align-items: baseline; justify-content: space-between; gap: 1cqh; margin-bottom: 1.5cqh; }
  .gbScreen .daCtrlLbl { font-family: 'Inter', sans-serif; font-size: 1.45cqh; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: #85828f; white-space: normal; }
  .gbScreen .daCtrlVal { font-family: 'Inter', sans-serif; font-size: 1.9cqh; font-weight: 600; color: #1a1830; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .gbScreen .daTrack { position: relative; height: 0.5cqh; border-radius: 99cqh; background: #e6e4ec; }
  .gbScreen .daThumb { position: absolute; top: 50%; left: var(--pos, 50%); width: 1.6cqh; height: 1.6cqh; margin: -0.8cqh 0 0 -0.8cqh; border-radius: 50%; background: #5B5BD6; }
  /* KPI chips — bordered cards: uppercase label / big number / sub */
  .gbScreen .daChips { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.4cqh; margin: 1.4cqh 0; }
  .gbScreen .daChip { display: flex; flex-direction: column; gap: 0.7cqh; padding: 1.3cqh 1.3cqh; border: 1px solid #e6e4ec; border-radius: 1.2cqh; min-width: 0; }
  .gbScreen .daChipLbl { font-family: 'Inter', sans-serif; font-size: 1.3cqh; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: #85828f; white-space: normal; min-height: 1.6cqh; line-height: 1.25; }
  .gbScreen .daChipVal { font-family: 'Inter', sans-serif; font-size: 2.6cqh; font-weight: 700; letter-spacing: -0.02em; color: #1a1830; font-variant-numeric: tabular-nums; line-height: 1.1; }
  .gbScreen .daChipSub { display: flex; align-items: center; gap: 0.4cqh; font-family: 'Inter', sans-serif; font-size: 1.5cqh; color: #5f5c6e; white-space: normal; }
  .gbScreen .daChipSub.is-up { color: #23a066; font-weight: 600; }
  .gbScreen .daChipSub svg { width: 1cqh; height: 1cqh; flex: 0 0 auto; }
  .gbScreen .daChipSub .dim { color: #5f5c6e; font-weight: 400; }
  /* chart row — main forecast chart (≈65%) + confidence mini-chart (≈35%) */
  .gbScreen .daChartRow { flex: 1; min-height: 0; display: grid; grid-template-columns: 1.7fr 1fr; gap: 1.4cqh; }
  .gbScreen .daChart { position: relative; min-width: 0; min-height: 0; box-sizing: border-box; border: 1px solid #e6e4ec; border-radius: 1.2cqh; padding: 3.2cqh 1.3cqh 1.2cqh 6.6cqh;
    display: grid; grid-template-rows: 1fr auto; row-gap: 0.9cqh; }
  .gbScreen .daToday { position: absolute; top: 0.9cqh; left: calc(6.6cqh + (100% - 7.9cqh) * var(--today, 0.6)); transform: translateX(-50%);
    font-family: 'Inter', sans-serif; font-size: 1.25cqh; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: #85828f; pointer-events: none; }
  .gbScreen .daSvg { display: block; width: 100%; height: 100%; min-height: 0; overflow: visible; }
  .gbScreen .daGrid line { stroke: #e2e0ea; stroke-width: 1; stroke-dasharray: 2 4; vector-effect: non-scaling-stroke; }
  .gbScreen .daDivider { stroke: #cfccd9; stroke-width: 1; stroke-dasharray: 2 3; vector-effect: non-scaling-stroke; }
  .gbScreen .daLine { fill: none; stroke: #5B5BD6; stroke-width: 1.8; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
  .gbScreen .daLine--dash { stroke-dasharray: 5 5; }
  .gbScreen .daY { position: absolute; left: 1.3cqh; top: 3.2cqh; bottom: 4cqh; pointer-events: none; }
  .gbScreen .daY span { position: absolute; left: 0; transform: translateY(-50%); font-family: 'Inter', sans-serif; font-size: 1.45cqh; font-weight: 500; color: #a5a2ae; font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; }
  .gbScreen .daY span:nth-child(1) { top: 0; } .gbScreen .daY span:nth-child(2) { top: 25%; } .gbScreen .daY span:nth-child(3) { top: 50%; } .gbScreen .daY span:nth-child(4) { top: 75%; } .gbScreen .daY span:nth-child(5) { top: 100%; }
  .gbScreen .daX { position: relative; height: 1.7cqh; }
  .gbScreen .daX span { position: absolute; top: 0; font-family: 'Inter', sans-serif; font-size: 1.35cqh; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: #a5a2ae; white-space: nowrap; }
  /* confidence mini-chart */
  .gbScreen .daMini { min-width: 0; min-height: 0; box-sizing: border-box; border: 1px solid #e6e4ec; border-radius: 1.2cqh; padding: 1.1cqh 1.3cqh 2.9cqh; display: flex; flex-direction: column; }
  .gbScreen .daMiniTitle { font-family: 'Inter', sans-serif; font-size: 1.3cqh; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: #85828f; white-space: normal; margin-bottom: 1.2cqh; }
  .gbScreen .daMiniPlot { position: relative; flex: 1; min-height: 0; }
  .gbScreen .daMiniSvg { display: block; width: 100%; height: 100%; overflow: visible; will-change: transform; transform: translateZ(0); }   /* own compositor layer → the non-scaling-stroke line is rasterised ONCE, so the prompt-typing repaints can't re-render it at a different thickness */
  .gbScreen .daMiniLine { fill: none; stroke: #5B5BD6; stroke-width: 1.6; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
  .gbScreen .daMiniDots { position: absolute; inset: 0; pointer-events: none; }
  .gbScreen .daMiniDots span { position: absolute; width: 1.25cqh; height: 1.25cqh; border-radius: 50%; background: #4d43cf; border: 0.3cqh solid #fff; box-sizing: border-box; transform: translate(-50%, -50%); }
  .gbScreen .daMiniY { position: absolute; left: 0; top: 0; height: 100%; pointer-events: none; }
  .gbScreen .daMiniY span { position: absolute; left: 0; transform: translateY(-50%); font: 500 1.3cqh 'Inter', sans-serif; color: #a5a2ae; font-variant-numeric: tabular-nums; }
  .gbScreen .daMiniX { position: absolute; left: 0; right: 0; bottom: -1.9cqh; height: 1.6cqh; pointer-events: none; }
  .gbScreen .daMiniX span { position: absolute; top: 0; transform: translateX(-50%); font: 500 1.25cqh 'Inter', sans-serif; letter-spacing: 0.06em; color: #a5a2ae; white-space: nowrap; }
  /* ---- Slide show: the dark annual-report deck (flips via arrows + D-pad) ---- */
  .gbScreen .daDeck { position: relative; flex: 1; min-height: 0; box-sizing: border-box; display: flex; flex-direction: column;
    background-color: #000; background-image: linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8)), url('../da-bg.webp'); background-size: cover; background-position: center; background-repeat: no-repeat; border: 1px solid #3a3747; border-radius: 1.8cqh; padding: 2cqh 2.4cqh 1.2cqh; color: #f6f5f9; overflow: hidden; box-shadow: 0 0.6cqh 2.6cqh rgba(0,0,0,0.5); }   /* da-bg.webp knocked back to ~20% over black on EVERY non-cover slide (was flat #000 on desktop — the missing backdrop); .daDeck.coverbg below overrides to the full crisp image on the cover slide */
  .gbScreen .daDeckArrows { position: absolute; top: 2cqh; right: 2.4cqh; display: flex; align-items: center; flex-wrap: nowrap; gap: 0.8cqh; z-index: 4; }   /* top/right inset matches the deck's own 2cqh/2.4cqh padding → the arrows sit with the SAME edge gap as the KPI cards below */
  .gbScreen .daArrow { width: 5.3cqh; height: 5.3cqh; border: 1px solid #4b4856; border-radius: 1cqh; background: #000; color: #b3b0bf; display: grid; place-items: center; cursor: pointer; padding: 0; }
  .gbScreen .daArrow svg { width: 2.3cqh; height: 2.3cqh; }
  .gbScreen .daArrow:disabled { opacity: 0.35; cursor: default; }
  /* the active "next slide" arrow gets a soft pulsing dithered lilac glow to signal it's interactive */
  /* first slide "start here" hint: BOTH arrows stay identical plain buttons (no per-button pseudos → no Safari mis-composite/overlap under the screen's matrix3d). The next arrow just gets a white outline + white glyph, and the dithered glow lives on the CONTAINER behind it. */
  .gbScreen .daDeckArrows.first .daArrow[data-dir="next"]:not(:disabled) { border-color: #fff; color: #fff; }
  .gbScreen .daDeckArrows.first::after { content: ''; position: absolute; z-index: -1; width: 11cqh; height: 11cqh; right: -2.85cqh; top: 50%; transform: translateY(-50%); background-image: var(--arrow-glow); background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; pointer-events: none; animation: arrowGlowPulse 3.2s ease-in-out infinite; }
  @keyframes arrowGlowPulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }
  .gbScreen .daSlides { flex: 1; min-height: 0; position: relative; }
  .gbScreen .daSlide { position: absolute; inset: 0; display: none; flex-direction: column; overflow: hidden; isolation: isolate; }
  .gbScreen .daSlide.is-active { display: flex; }
  /* backdrop lives on the DECK element (full-bleed → fills the whole card, through the padding + behind the period-nav, on EVERY slide). Blurred by default (chart/PDF slides); crisp pre-baked image on the cover. Pre-blurred file avoids the CSS filter + z-index issues that kept the old per-slide ::before confined inside the slide. */
  .gbScreen .daDeck.coverbg { background-image: url('../da-bg.webp'); background-size: cover; background-position: center; background-repeat: no-repeat; }   /* cover slide → crisp full-bleed backdrop (cover so it fills the whole card at any size) */
  .gbScreen .daSlideTitle { margin: 0 0 2.2cqh; font-family: 'Britti Medium', 'Inter', sans-serif; font-size: 3.4cqh; font-weight: 600; letter-spacing: -0.022em; line-height: 1.05; color: #f6f5f9; }
  .gbScreen .daSlideSub { margin: 0 0 1.3cqh; font-family: 'Inter', sans-serif; font-size: 1.8cqh; line-height: 1.4; letter-spacing: -0.012em; color: #b3b0bf; }
  /* cover slide — big stacked title + contents list left, right-aligned KPI grid right */
  .gbScreen .daCover { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 2.6cqh; }
  .gbScreen .daCoverMain { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
  .gbScreen .daCoverTitle { margin: 0 0 1.2cqh; display: flex; flex-direction: column; font-family: 'Britti Medium', 'Inter', sans-serif; font-size: 5.2cqh; font-weight: 600; letter-spacing: -0.035em; line-height: 0.98; color: #f6f5f9; }
  .gbScreen .daCoverLede { margin: 0 0 1.4cqh; font-family: 'Inter', sans-serif; font-size: 1.7cqh; line-height: 1.45; color: #b3b0bf; max-width: 38ch; }
  .gbScreen .daToc { list-style: none; margin: auto 0 0; padding: 0; }
  .gbScreen .daToc li { display: flex; align-items: baseline; gap: 1.4cqh; padding: 0.95cqh 0; border-top: 1px solid #413e4e; }
  .gbScreen .daToc .num { font: 500 1.4cqh 'Inter', sans-serif; color: #f6f5f9; font-variant-numeric: tabular-nums; }
  .gbScreen .daToc .name { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 600; font-size: 2cqh; letter-spacing: -0.012em; color: #f6f5f9; }
  .gbScreen .daKpis { display: grid; grid-template-columns: auto auto; justify-content: end; gap: 2.8cqh 4.4cqh; align-content: start; margin-top: auto; margin-bottom: 2.8cqh; min-width: 0; }
  .gbScreen .daKpi { display: flex; flex-direction: column; align-items: flex-end; text-align: right; gap: 0.9cqh; min-width: 0; }
  .gbScreen .daKpi .lbl { font: 600 1.8cqh 'Inter', sans-serif; letter-spacing: 0.1em; text-transform: uppercase; color: #f6f5f9; white-space: nowrap; }
  .gbScreen .daKpi .num { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 600; font-size: 4.1cqh; letter-spacing: -0.026em; line-height: 1; color: #f6f5f9; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .gbScreen .daKpi .sub { font: 400 1.8cqh 'Inter', sans-serif; color: #b3b0bf; line-height: 1.3; }
  /* KPI strip cells (Revenue Trend + Support) */
  .gbScreen .daStrip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.1cqh; }
  .gbScreen .daStrip .cell { display: flex; flex-direction: column; gap: 0.4cqh; padding: 1cqh 1.2cqh 1.1cqh; border: 1px solid #413e4e; border-radius: 1.2cqh; min-width: 0; }
  .gbScreen .daStrip .lbl { font: 600 1.25cqh 'Inter', sans-serif; letter-spacing: 0.12em; text-transform: uppercase; color: #fff; white-space: normal; }
  .gbScreen .daStrip .num { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 600; font-size: 2.2cqh; color: #f6f5f9; font-variant-numeric: tabular-nums; line-height: 1.1; white-space: normal; }
  .gbScreen .daStrip .good { color: #45b586; }
  .gbScreen .daStrip .sub { font: 400 1.35cqh 'Inter', sans-serif; color: #b3b0bf; white-space: normal; }
  /* Revenue Trend chart — purple revenue line + area, green profit line */
  .gbScreen .daTrend { position: relative; flex: 1; min-height: 0; margin-top: 1.2cqh; border: 1px solid #413e4e; border-radius: 1.2cqh; padding: 2.4cqh 2.4cqh 3.2cqh 6.4cqh; box-sizing: border-box; }
  .gbScreen .daTrend svg { display: block; width: 100%; height: 100%; overflow: visible; }
  .gbScreen .daTrendGrid line { stroke: #3a3747; stroke-width: 1; stroke-dasharray: 2 4; vector-effect: non-scaling-stroke; }
  .gbScreen .daTrendRev { fill: none; stroke: #8c86f2; stroke-width: 2; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
  .gbScreen .daTrendProfit { fill: none; stroke: #45b586; stroke-width: 1.4; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
  .gbScreen .daTrendY { position: absolute; left: 0; top: 2.4cqh; bottom: 3.2cqh; width: 5.6cqh; pointer-events: none; }
  .gbScreen .daTrendY span { position: absolute; right: 0.2cqh; transform: translateY(-50%); font: 500 1.35cqh 'Inter', sans-serif; color: #f6f5f9; font-variant-numeric: tabular-nums; white-space: nowrap; }   /* shifted toward the plot (was 0.9cqh) so the wide labels ($1.2M) get left-border breathing room — matching the x-axis labels' padding, while keeping ~1cqh clear of the plot */
  .gbScreen .daTrendY span:nth-child(1) { top: 0; } .gbScreen .daTrendY span:nth-child(2) { top: 25%; } .gbScreen .daTrendY span:nth-child(3) { top: 50%; } .gbScreen .daTrendY span:nth-child(4) { top: 75%; } .gbScreen .daTrendY span:nth-child(5) { top: 100%; }
  .gbScreen .daTrendX { position: absolute; left: 6cqh; right: 1.3cqh; bottom: 1cqh; display: flex; justify-content: space-between; font: 500 1.3cqh 'Inter', sans-serif; letter-spacing: 0.08em; text-transform: uppercase; color: #f6f5f9; font-variant-numeric: tabular-nums; }
  /* callouts */
  .gbScreen .daCallouts { display: grid; grid-template-columns: auto auto; justify-content: space-between; gap: 1.1cqh; margin: 0.2cqh 0 1.4cqh; }
  .gbScreen .daCallout { padding: 1cqh 1.3cqh; border: 1px solid #413e4e; border-radius: 1.2cqh; font: 400 1.4cqh/1.4 'Inter', sans-serif; color: #b3b0bf; white-space: nowrap; }   /* nowrap + auto columns → each insight sits on ONE line */
  .gbScreen .daCallout::before { content: none; }   /* arrow icon removed */
  .gbScreen .daCallout strong { color: #f6f5f9; font-weight: 600; }
  .gbScreen .daCallout--good { border-color: #2e6d52; background: transparent; color: #f6f5f9; }   /* black background (keeps the green border); slideshow is all-black now */
  .gbScreen .daCallout--good::before { color: #45b586; }
  /* Top Partners — vertical bar columns; #1 partner is green */
  .gbScreen .daPartners { position: relative; flex: 1; min-height: 0; border: 1px solid #413e4e; border-radius: 1.2cqh; box-sizing: border-box; }
  .gbScreen .daPartners svg { position: absolute; left: 2cqh; top: 1.6cqh; width: calc(100% - 4cqh); height: calc(100% - 5.6cqh); overflow: visible; }
  .gbScreen .daPCols { position: absolute; left: 2cqh; right: 2cqh; top: 1.6cqh; bottom: 4cqh; display: flex; gap: 2.2cqh; align-items: flex-end; }
  .gbScreen .daPCol { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 0.7cqh; }
  .gbScreen .daPVal { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 600; font-size: 1.9cqh; color: #f6f5f9; font-variant-numeric: tabular-nums; }
  .gbScreen .daPBar { width: 100%; max-width: 8cqh; height: var(--w, 50%); border-radius: 1cqh 1cqh 0 0; background: #b9b6c6; }
  .gbScreen .daPCol:first-child .daPBar { background: #2e9d6d; }
  .gbScreen .daPX { position: absolute; left: 2cqh; right: 2cqh; bottom: 1cqh; display: flex; gap: 2.2cqh; }
  .gbScreen .daPX span { flex: 1; text-align: center; font: 500 1.3cqh 'Inter', sans-serif; letter-spacing: 0.08em; text-transform: uppercase; color: #f6f5f9; white-space: normal; }
  /* Top Products — ranked table with twin bars + margin pill */
  .gbScreen .daProducts { flex: 1; min-height: 0; display: flex; flex-direction: column; margin-top: 0.2cqh; }
  .gbScreen .daProdHead, .gbScreen .daProdRow { display: grid; grid-template-columns: 2.6cqh 0.9fr 1.4fr auto; column-gap: 1.4cqh; align-items: center; border-bottom: 1px solid #413e4e; }
  .gbScreen .daProdHead { padding: 0.7cqh 0; font: 600 1.2cqh 'Inter', sans-serif; letter-spacing: 0.14em; text-transform: uppercase; color: #f6f5f9; }
  .gbScreen .daProdHead .right { text-align: right; }
  .gbScreen .daProdHead .dot { display: inline-block; width: 1.2cqh; height: 0.28cqh; border-radius: 99cqh; background: #5B5BD6; vertical-align: middle; margin: 0 0.5cqh 0.35cqh 0; }
  .gbScreen .daProdHead .dot--g { background: #45b586; }
  .gbScreen .daProdRow { flex: 1; min-height: 0; }
  .gbScreen .daProdRow:last-child { border-bottom: 0; }
  .gbScreen .daProdRank { font: 400 1.5cqh 'Inter', sans-serif; color: #f6f5f9; font-variant-numeric: tabular-nums; }
  .gbScreen .daProdName { font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 600; font-size: 1.9cqh; color: #f6f5f9; white-space: normal; }
  .gbScreen .daRBar { display: block; height: 0.3cqh; width: var(--w, 0%); border-radius: 99cqh; background: #5B5BD6; margin: 0.55cqh 0; }
  .gbScreen .daRBar--profit { background: #45b586; }
  .gbScreen .daProdVal { display: flex; flex-direction: row; align-items: baseline; justify-content: flex-end; gap: 0.9cqh; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .gbScreen .daProdVal .num { font: 600 1.7cqh 'Inter', sans-serif; color: #f6f5f9; }
  .gbScreen .daProdVal .row { display: inline-flex; align-items: baseline; gap: 0.6cqh; }
  .gbScreen .daProdVal .mut { font: 500 1.45cqh 'Inter', sans-serif; color: #45b586; }
  .gbScreen .daPill { padding: 0.15cqh 0.8cqh; border-radius: 99cqh; background: #1f4a38; color: #7fd6ab; font: 600 1.3cqh 'Inter', sans-serif; }
  /* Support — stat chips + donut + legend card */
  .gbScreen .daSupBody { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 1.25fr; gap: 1.1cqh; margin-top: 1.1cqh; }
  .gbScreen .daDonut { align-self: center; justify-self: center; width: min(100%, 20cqh); height: auto; max-height: 100%; display: block; }
  .gbScreen .daDonut .track { fill: none; stroke: #3a3747; stroke-width: 6; }
  .gbScreen .daDonut .seg { fill: none; stroke-width: 6; }
  .gbScreen .daLegend { border: 1px solid #413e4e; border-radius: 1.2cqh; padding: 1cqh 1.4cqh 0.4cqh; display: flex; flex-direction: column; min-height: 0; min-width: 0; }
  .gbScreen .daLegendTitle { font: 600 1.25cqh 'Inter', sans-serif; letter-spacing: 0.14em; text-transform: uppercase; color: #8b8798; margin-bottom: 0.4cqh; }
  .gbScreen .daLegend ul { list-style: none; margin: 0; padding: 0; flex: 1; display: flex; flex-direction: column; min-height: 0; }
  .gbScreen .daLegend li { flex: 1; display: grid; grid-template-columns: 1.2cqh 1fr auto auto; align-items: center; column-gap: 1cqh; border-top: 1px solid #413e4e; font: 400 1.55cqh 'Inter', sans-serif; color: #b3b0bf; min-height: 0; }
  .gbScreen .daLegend li:first-child { border-top: 0; }
  .gbScreen .daLegend .ldot { width: 1.1cqh; height: 1.1cqh; border-radius: 50%; }
  .gbScreen .daLegend .lname { color: #f6f5f9; font-weight: 500; white-space: normal; }
  .gbScreen .daLegend .lnum { color: #f6f5f9; font-weight: 700; font-variant-numeric: tabular-nums; }
  .gbScreen .daLegend .lpct { color: #b3b0bf; font-variant-numeric: tabular-nums; min-width: 4.6cqh; text-align: right; }
  /* deck bottom nav — period pills (dimmed, non-interactive) left, slide counter right */
  .gbScreen .daDeckNav { flex: 0 0 auto; display: flex; justify-content: space-between; align-items: center; gap: 1.2cqh; padding-top: 1.1cqh; }
  .gbScreen .daDeckNav .navL { display: flex; align-items: center; gap: 1.2cqh; min-width: 0; }
  .gbScreen .daPeriodLbl { font: 600 1.35cqh 'Inter', sans-serif; letter-spacing: 0.14em; text-transform: uppercase; color: #f6f5f9; white-space: nowrap; }
  .gbScreen .daPeriodBtn { border: 1px solid #4b4856; border-radius: 99cqh; padding: 0.9cqh 1.2cqh; font: 500 1.4cqh/1 'Inter', sans-serif; color: #b3b0bf; white-space: nowrap; opacity: 0.5; }   /* dimmed → reads as a static filter, not a button; line-height 1 + balanced padding so the text sits centred with even spacing */
  .gbScreen .daPeriodBtn.is-active { background: rgba(255,255,255,0.1); color: #fff; border-color: #fff; font-weight: 600; opacity: 1; }   /* active period → white outline + white text (opacity reset to 1 so the white reads true, not the dimmed 0.5 of the inactive pills) */
  .gbScreen .daDeckNav .counter { font: 500 1.4cqh 'Inter', sans-serif; letter-spacing: 0.04em; color: #b3b0bf; font-variant-numeric: tabular-nums; white-space: nowrap; }
  /* ---- PDF report: ONE static serif cover page in a dark gutter ---- */
  .gbScreen .daPdf { flex: 1; min-height: 0; background: #272835; border-radius: 1.8cqh; padding: 2cqh 2.4cqh 0; box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; }
  .gbScreen .daPdfBar { flex: 0 0 auto; display: flex; align-items: flex-end; justify-content: flex-end; gap: 0.8cqh; margin-bottom: 1.6cqh; }
  .gbScreen .daPdfDrop { display: flex; flex-direction: column; gap: 0.5cqh; }
  .gbScreen .daPdfDrop .dl { font-family: 'Inter', sans-serif; font-size: 1.15cqh; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #85828f; }
  .gbScreen .daPdfDrop .db { display: inline-flex; align-items: center; gap: 0.9cqh; padding: 0.85cqh 1.2cqh; border: 1px solid #4b4856; border-radius: 0.9cqh; background: #1c1d27; color: #d8d5e0; font-family: 'Inter', sans-serif; font-size: 1.45cqh; font-weight: 500; cursor: default; }
  .gbScreen .daPdfDrop .db svg { width: 1.3cqh; height: 1.3cqh; opacity: 0.7; }
  .gbScreen .daPdfExport { padding: 1.1cqh 1.8cqh; border: 1px solid #4b4856; border-radius: 0.9cqh; background: #1c1d27; color: #d8d5e0; font-family: 'Inter', sans-serif; font-size: 1.5cqh; font-weight: 600; cursor: default; }   /* matches the Partner/Time dropdowns beside it */
  .gbScreen .daPdfViewport { flex: 1; min-height: 0; overflow: hidden; display: flex; justify-content: center; }
  .gbScreen .daPdfRule { height: 1px; background: rgba(201,154,78,0.45); margin: 2.4cqh 0; }
  .gbScreen .daPdfPage { width: min(48cqh, 100%); flex: 0 0 auto; align-self: flex-start; background: #f7f0e3; color: #26303f; box-shadow: 0 1cqh 2.4cqh rgba(0,0,0,0.3); display: flex; flex-direction: column; font-family: Georgia, 'Times New Roman', serif; }
  .gbScreen .daPdfPage--cover { position: relative; background: #1d2a3e; color: #f7f0e3; padding: 4cqh 3cqh 3cqh; box-sizing: border-box; transform: scale(1.5); transform-origin: top center; margin-top: 1.4cqh; }   /* scaled up a touch more + moved higher (still clears the Partner/Period dropdowns) */
  .gbScreen .daPdfYear { position: absolute; bottom: 0.4cqh; right: -0.9cqh; font-size: 13cqh; font-weight: 600; letter-spacing: -0.02em; line-height: 1; color: rgba(247,240,227,0.07); pointer-events: none; }
  .gbScreen .daPdfCoverMeta { display: grid; grid-template-columns: auto auto; column-gap: 5cqh; justify-content: start; margin-bottom: 2.6cqh; position: relative; z-index: 1; }
  .gbScreen .daPdfCoverMeta .m { display: flex; flex-direction: column; gap: 0.7cqh; }
  .gbScreen .daPdfCoverMeta .mlbl { font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 1.2cqh; letter-spacing: 0.22em; text-transform: uppercase; color: #c99a4e; }
  .gbScreen .daPdfCoverMeta .mval { font-size: 1.7cqh; color: #f7f0e3; }
  .gbScreen .daPdfCoverTitle { margin: 0 0 1cqh; font-size: 4.2cqh; font-weight: 700; line-height: 1.04; letter-spacing: -0.02em; position: relative; z-index: 1; }
  .gbScreen .daPdfCoverLede { margin: 0 0 2.6cqh; max-width: 40ch; font-size: 1.8cqh; line-height: 1.5; color: #e4d9c2; position: relative; z-index: 1; }
  /* cover KPIs — 2×2, mono gold label / serif value / italic sub */
  .gbScreen .daPdfKpis { display: grid; grid-template-columns: 1fr 1fr; gap: 2cqh 2.6cqh; margin-top: 3.4cqh; position: relative; z-index: 1; }
  .gbScreen .daPdfKpi { display: flex; flex-direction: column; min-width: 0; }
  .gbScreen .daPdfKpi .klbl { font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 1.15cqh; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: #c99a4e; margin-bottom: 0.5cqh; }
  .gbScreen .daPdfKpi .kval { font-size: 2.3cqh; font-weight: 700; letter-spacing: -0.005em; line-height: 1.1; margin-bottom: 0.4cqh; color: #f7f0e3; font-variant-numeric: tabular-nums; }
  .gbScreen .daPdfKpi .ksub { font-size: 1.3cqh; font-style: italic; color: #b9ad93; }

  /* ===== Governance page: semantic-layer spine + four output cards + lilac connector wires ===== */
  /* type scale (cqh): 1.5 micro · 1.8 caption · 2.2 body · 2.8 title · 4.6 display — every gov font size is one of these */
  /* spacing rhythm (cqh): 0.9 base unit → 0.9 gaps · 1.8 insets (card bodies, header x-pad, Verify/badge insets all share it) */
  .gbScreen .govStage { position: relative; flex: 1; min-height: 0; width: 100%; }
  .gbScreen .govWires { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; overflow: visible; }
  .gbScreen .govDots { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 5; pointer-events: none; overflow: visible; }   /* dots layer ABOVE the cards (z 3) so a dot centred on the card edge half-overlays onto it */
  .gbScreen .govWire { fill: none; stroke: var(--acc); stroke-width: 1.9; vector-effect: non-scaling-stroke; opacity: 0.6; stroke-linecap: round; }
  /* dithered purple glow behind a wire once its connection is MADE — a chunky pixel-dot band (stroke uses the #wireDither pattern), echoing the dithered drop shadows used elsewhere */
  .gbScreen .govGlow { fill: none; stroke: url(#wireDither); stroke-width: 3.2; vector-effect: non-scaling-stroke; stroke-linecap: round; pointer-events: none; opacity: 0; transition: opacity .45s ease; }
  .gbScreen .govGlow.lit { opacity: 0.55; }   /* stays lit (constant dithered glow) once the connection is made — softened so the wire weight matches the card outline */
  /* forming cue: while Cloudy dwells, a small bright LIGHT SHOOTS hero → card along the wire (short dash + strong glow = a streak of light, not a solid moving segment) */
  /* traveling light = TWO stacked layers: a lilac glow (long, blurred) with a short bright-white core centred inside it → the streak reads white in the middle and gradients out to purple at BOTH ends, blending into the wire. Both share govTravel so they move together (dash offsets keep the core centred in the glow). */
  .gbScreen .govPulseGlow { fill: none; stroke: #b9a5ff; stroke-width: 5; vector-effect: non-scaling-stroke; stroke-linecap: round; pointer-events: none; opacity: 0; transition: opacity .4s ease; }
  .gbScreen .govPulseGlow.arming { opacity: 0.6; stroke-dasharray: 0.44 0.56; animation: govTravel 1.4s linear infinite; filter: blur(6px); }
  .gbScreen .govPulse { fill: none; stroke: #fff; stroke-width: 3; vector-effect: non-scaling-stroke; stroke-linecap: round; pointer-events: none; opacity: 0; transition: opacity .4s ease; }
  .gbScreen .govPulse.arming { opacity: 0.95; stroke-dasharray: 0 0.02 0.32 0.66; animation: govTravel 1.4s linear infinite; filter: blur(2.8px) drop-shadow(0 0 3px rgba(255,255,255,0.7)); }   /* long soft WHITE BAND with feathered (gradient-like) edges that flows down the wire into the card — one consistent gradient, not a short blob (blur + a wide dash read as white→transparent) */
  @keyframes govTravel { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
  .gbScreen .govWire.arming { opacity: 0.85; }   /* the wire itself brightens a touch while its connection is forming */
  /* a node dot at the card end of each wire once it's verifying / connected */
  .gbScreen .govDot { fill: #8f7bff; opacity: 0; transition: opacity .25s ease; filter: drop-shadow(0 0 3px rgba(150,130,255,0.9)); }
  .gbScreen .govDot.arming, .gbScreen .govDot.lit { opacity: 1; }
  /* the four surfaces — white product cards with a dark header, like the reference (rounded, soft-shadowed, NOT pixel-framed → reads as live product UI, matching the Data Apps deck) */
  /* all four cards share ONE fixed shape (same width + height) — a consistent block */
  .gbScreen .govCard { position: absolute; z-index: 3; width: 40cqw; height: 32cqh; box-sizing: border-box; border: 1px solid transparent; border-radius: 1.4cqh; overflow: hidden; background: #fff; box-shadow: 0 0.6cqh 2.4cqh rgba(8,6,20,0.34); transition: box-shadow .5s ease, border-color .5s ease; }
  .gbScreen .govCard.verified { border-color: rgba(150,132,255,0.75); box-shadow: 0 0.6cqh 2.4cqh rgba(8,6,20,0.34), 0 0 2.6cqh rgba(143,123,255,0.6), 0 0 6.5cqh rgba(143,123,255,0.3); }   /* once verified, the card copies the Revenue card's purple stroke + two-layer bloom (same as the wires) */
  .gbScreen .gcDash  { top: 0; left: 3cqw; }
  .gbScreen .gcChart { top: 0; right: 3cqw; }
  .gbScreen .gcAi    { bottom: 0; left: 3cqw; }
  .gbScreen .gcSlack { bottom: 0; right: 3cqw; }
  /* header matches the Data Apps prompt card (.daPromptHead): no dark bar — dark title on the white card with a hairline divider under it */
  .gbScreen .gcHead { color: #1a1830; text-align: left; font-family: 'Inter', sans-serif; font-weight: 700; font-size: 2.8cqh; letter-spacing: -0.012em; padding: 2cqh 0 1.3cqh; margin: 0 2.2cqh; border-bottom: 1px solid #e6e4ec; white-space: nowrap; }   /* title matches the Revenue hero (.gsName) size + weight */
  .gbScreen .gcBody { padding: 1.6cqh 2.2cqh 2cqh; color: #1a1830; }
  /* dashboard KPI — a 2×2 grid of abbreviated stats */
  .gbScreen .govCard.gcDash, .gbScreen .govCard.gcChart, .gbScreen .govCard.gcAi { display: flex; flex-direction: column; }   /* let each card's body grow to fill it (KPI grid / chart body / AI chat) */
  .gbScreen .gcDash .gcBody, .gbScreen .gcChart .gcBody, .gbScreen .gcAi .gcBody { flex: 1 1 auto; min-height: 0; }
  .gbScreen .gcDashBody { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 1.3cqh; }
  .gbScreen .gcMini { display: flex; flex-direction: column; justify-content: space-between; border: 1px solid #f0eff4; border-radius: 1cqh; padding: 1.1cqh 1.3cqh; min-width: 0; }   /* each KPI in a stroked tile: label top-left, value bottom-right */
  .gbScreen .gcMiniLbl { align-self: flex-start; font-family: 'Inter', sans-serif; font-size: 1.5cqh; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: #8b8798; white-space: nowrap; }
  .gbScreen .gcMiniNum { align-self: flex-end; font-family: 'Inter', sans-serif; font-weight: 800; font-size: 3.2cqh; line-height: 1; letter-spacing: -0.02em; color: #14121f; font-variant-numeric: tabular-nums; }
  /* embedded charts */
  .gbScreen .gcChartBody { display: flex; flex-direction: column; gap: 0.7cqh; }
  .gbScreen .gcBars { width: 100%; flex: 1 1 auto; min-height: 0; overflow: visible; }   /* stacked bars grow to fill; month labels stay pinned under them */
  .gbScreen .gcBars .p1 { fill: #5B5BD6; }   /* Product 1 (purple, base) */
  .gbScreen .gcBars .p3 { fill: #8ad6ac; }   /* Product 3 (green, middle) */
  .gbScreen .gcBars .p2 { fill: #c4b6f7; }   /* Product 2 (lilac, top) */
  .gbScreen .gcChartX { display: grid; grid-template-columns: repeat(6, 1fr); }
  .gbScreen .gcChartX span { text-align: center; font-family: 'Inter', sans-serif; font-size: 1.65cqh; font-weight: 500; color: #9a98a6; }   /* each label centered in its column, aligned to the bar centres */
  /* AI chatbot = a mini live chat: the question is a right-aligned "sent" bubble, the agent types for a beat, then replies on the left */
  .gbScreen .gcAiBody { display: flex; flex-direction: column; gap: 1.6cqh; justify-content: flex-start; }   /* conversation ALWAYS at the top, empty composer pinned to the bottom (persistent chat box). gap trimmed 2.6→1.6cqh so the question+answer+composer fit the card height without clipping the bottom "Ask a question" box (per the user) */
  .gbScreen .gcInput { display: none; align-items: center; gap: 0.2cqh; align-self: flex-end; max-width: 84%; padding: 1.2cqh 1.4cqh; border-radius: 0.9cqh; background: #f1f0f6; font-family: 'Inter', sans-serif; font-size: 2.7cqh; color: #45435a; }   /* the SENT question bubble — hidden until asked */
  .gbScreen .gcAi.aiasking .gcInput, .gbScreen .gcAi.aianswered .gcInput { display: flex; animation: gcPop .35s cubic-bezier(.2,1.4,.4,1); }
  .gbScreen .gcComposer { align-self: stretch; flex: none; box-sizing: border-box; margin-top: auto; display: flex; align-items: center; gap: 0.2cqh; border: 1px solid #e2e0ec; border-radius: 0.9cqh; padding: 0.9cqh 1.4cqh; min-height: 2.3cqh; font-family: 'Inter', sans-serif; font-size: 2.7cqh; }   /* persistent EMPTY chat box, pinned to the bottom of the card — flex:none so it NEVER shrinks/grows when the question+answer appear. Padding/min-height trimmed so it clears the card's bottom edge (per the user — it was clipping) */
  .gbScreen .gcComposerText { color: #9a98a6; }
  .gbScreen .gcCaret { width: 1px; height: 2.9cqh; background: #1a1830; opacity: 0; animation: gcBlink 1.05s step-end infinite; }
  .gbScreen .gcAi.aityping .gcComposerText { color: #1a1830; }   /* while typing the question, the composer text is dark (user input), not the grey placeholder */
  /* the "Ask a question…" composer PERSISTS after send (resets to the empty placeholder) so the chat box stays in the UI */
  @keyframes gcBlink { 50% { opacity: 1; } }
  .gbScreen .gcTyping { align-self: flex-start; display: none; align-items: center; gap: 0.6cqh; padding: 1.5cqh 1.6cqh; background: #f1f0f6; border-radius: 0.9cqh; }
  .gbScreen .gcAi.aiasking .gcTyping { display: flex; }   /* "agent is typing" beat */
  .gbScreen .gcTyping span { width: 0.85cqh; height: 0.85cqh; border-radius: 50%; background: #b3b0c2; animation: gcTypingDot 1s ease-in-out infinite; }
  .gbScreen .gcTyping span:nth-child(2) { animation-delay: .16s; }
  .gbScreen .gcTyping span:nth-child(3) { animation-delay: .32s; }
  @keyframes gcTypingDot { 0%, 70%, 100% { opacity: .35; transform: translateY(0); } 35% { opacity: 1; transform: translateY(-0.35cqh); } }
  .gbScreen .gcAnswer { align-self: flex-start; max-width: 100%; margin: 0; padding: 0.4cqh 0.2cqh; background: transparent; border-radius: 0; font-family: 'Inter', sans-serif; font-size: 2.7cqh; line-height: 1.3; color: #1a1830; }   /* agent reply → plain dark text, no box */
  .gbScreen .gcAnswer b { color: #1a1830; }
  /* slack — a mini thread: your question, then the Lightdash AI reply (revealed on verify) */
  .gbScreen .gcSlackBody { display: flex; flex-direction: column; gap: 0.7cqh; }
  .gbScreen .gcSlackRow { display: flex; gap: 1cqh; align-items: flex-start; }
  .gbScreen .gcYouAv { flex: 0 0 auto; width: 4.4cqh; height: 4.4cqh; border-radius: 1cqh; object-fit: cover; box-sizing: border-box; border: 1px solid #e6e4ec; margin-top: 0.2cqh; }   /* photo avatar, same size/frame as the Lightdash Slack avatar */
  .gbScreen .gcSlackIcon { width: 4.4cqh; height: 4.4cqh; flex: 0 0 auto; margin-top: 0.2cqh; box-sizing: border-box; border: 1px solid #e6e4ec; border-radius: 1cqh; object-fit: cover; }   /* Lightdash app avatar: black logo tile fills the rounded box edge-to-edge, light-gray stroke to match the "You" photo avatar */
  .gbScreen .gcSlackMsg { min-width: 0; font-family: 'Inter', sans-serif; }
  .gbScreen .gcSlackName { font-weight: 700; font-size: 2.28cqh; color: #14121f; }
  .gbScreen .gcApp { margin-left: 0.6cqh; font-size: 1.5cqh; font-weight: 600; letter-spacing: 0.04em; color: #8a8798; background: #efeef4; border-radius: 0.4cqh; padding: 0.1cqh 0.5cqh; vertical-align: middle; }
  .gbScreen .gcTime { margin-left: 0.5cqh; font-size: 1.8cqh; color: #9a98a6; }
  .gbScreen .gcSlackText { margin-top: 0.3cqh; font-size: 2.28cqh; color: #1a1830; line-height: 1.4; }
  .gbScreen .gcSlackStats { display: inline-block; white-space: normal; }   /* wraps at explicit .gcSep break points — the numbers pop in at their final value (no width-changing count-up), so this never reflows */
  .gbScreen .gcStat { white-space: nowrap; }   /* a metric ("Label: value") never splits mid-way */
  .gbScreen .gcSep--mid { display: block; width: 0; height: 0; overflow: hidden; }   /* desktop: middle separator becomes a line break (dot clipped) → 2 lines: Total·Orders / Avg·Profit */
  .gbScreen .gcSlackNum { display: inline-block; }
  .gbScreen .gcSlackNum.gcNumPop { animation: gcNumPop .3s cubic-bezier(.2,1.3,.4,1) both; }
  @keyframes gcNumPop { 0% { opacity: 0; transform: translateY(0.28em) scale(0.86); } 100% { opacity: 1; transform: none; } }
  .gbScreen .gcMention { color: #1264a3; background: #e7f3f8; border-radius: 0.4cqh; padding: 0 0.4cqh; font-weight: 600; }   /* Slack-style @-mention pill */
  /* the semantic layer — the single governed source of truth (hero spine) */
  /* the semantic-layer source is now a CARD the same size as the four surfaces (centred hub) */
  /* the Revenue hero is DARK — sets the governed source of truth apart from the four white surface cards */
  /* the Revenue hero — DARK compact vertical card: [# Revenue] / description / [✓ Verified · Sales · ▁▃▅ 16] */
  .gbScreen .govSpine { position: absolute; top: 50%; left: 50%; width: 36cqw; height: auto; box-sizing: border-box; transform: translate(-50%,-50%); z-index: 4; display: flex; flex-direction: column; justify-content: flex-start; gap: 1.6cqh; padding: 2cqh 2.2cqh; background: #1c1a28; border: 1px solid rgba(150,132,255,0.75); border-radius: 1.4cqh; box-shadow: 0 0.9cqh 3.2cqh rgba(0,0,0,0.55); }   /* purple stroke + a DITHERED purple glow (::before) = same gamey pixel aesthetic as the wires (gap 1.6cqh matches the white cards' gcBody top padding) */
  .gbScreen .govSpineGlow { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 40cqw; height: 24cqh; z-index: 3; opacity: 0.75; background-image: var(--card-glow); background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; pointer-events: none; }   /* pixel-dithered purple halo — a separate element BEHIND the Revenue card (z-3, below the card's z-4). Kept tight (just past the card) + dimmed so it doesn't bleed into the neighbouring cards */
  .gbScreen .gsMetric { display: flex; align-items: center; gap: 1cqh; padding-bottom: 1.3cqh; border-bottom: 1px solid rgba(255,255,255,0.12); }   /* padding-bottom 1.3cqh = same title→divider distance as the white cards' gcHead */
  .gbScreen .gsHash { display: flex; align-items: center; justify-content: center; width: 3.4cqh; height: 3.4cqh; flex: 0 0 auto; box-sizing: border-box; border: 1px solid rgba(255,255,255,0.2); border-radius: 0.8cqh; color: #c9c6d6; font-family: 'Inter', sans-serif; font-weight: 700; font-size: 2.2cqh; }
  .gbScreen .gsName { font-family: 'Inter', sans-serif; font-weight: 700; font-size: 2.8cqh; color: #f6f5f9; }   /* matches the white cards' title (.gcHead 2.8cqh) */
  .gbScreen .gsPills { margin-left: auto; display: flex; align-items: center; gap: 1cqh; }   /* Verified + Sales pills sit top-right of the title row, mirroring the white cards' header status pill */
  .gbScreen .gsDesc { font-family: 'Inter', sans-serif; font-size: 2.45cqh; line-height: 1.35; color: #b3b0bf; }   /* kept at 2.45cqh so it stays on one line in the narrow hero (the AI chat text is 2.7cqh, but that would wrap here) */
  .gbScreen .gsFoot { display: flex; align-items: center; gap: 0.8cqh; }   /* the 1.6cqh govSpine gap handles the spacing above it → consistent rhythm */
  .gbScreen .gsChip { display: inline-flex; align-items: center; gap: 0.6cqh; font-family: 'Inter', sans-serif; font-weight: 600; font-size: 1.75cqh; border-radius: 0.6cqh; padding: 0.55cqh 0.85cqh; white-space: nowrap; }   /* inline-flex → the tick is optically centred with the text; equal-feeling padding all round */
  .gbScreen .gsVerified { color: #4bd68a; background: rgba(18,161,95,0.22); }
  /* pixel-art checkmark (blocky, on-theme) — masked so it tints to the surrounding text colour (currentColor); crispEdges keeps the pixels hard */
  .gbScreen .pxTick { display: block; flex: 0 0 auto; width: 1.75cqh; height: 1.3cqh; background-color: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3 5 19 14' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5l10 -10'/%3E%3C/svg%3E") center/contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3 5 19 14' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5l10 -10'/%3E%3C/svg%3E") center/contain no-repeat; }
  .gbScreen .gsDomain { color: #8fb0ff; background: rgba(63,106,224,0.22); }
  .gbScreen .gsCount { display: flex; align-items: center; gap: 0.5cqh; margin-left: auto; color: #b3b0bf; font-family: 'Inter', sans-serif; font-weight: 600; font-size: 1.9cqh; white-space: nowrap; }
  .gbScreen .gsCountIco { display: block; width: 2.35cqh; height: 2.1cqh; flex: 0 0 auto; }
  /* verification lives in the TOP-RIGHT status (no bottom button): idle "Verify" → while Cloudy dwells "Verifying" + three loading dots → "✓ Verified" with a tick */
  .gbScreen .govCard { transform-origin: center; }
  .gbScreen .govCard .gcHead { position: relative; }
  .gbScreen .gcStatus { position: absolute; right: 0; top: 1.4cqh; width: 12.8cqh; box-sizing: border-box; text-align: center; font-family: 'Inter', sans-serif; font-size: 1.75cqh; font-weight: 600; letter-spacing: 0.02em; white-space: nowrap; transform-origin: right center; border-radius: 0.6cqh; padding: 0.5cqh 0.9cqh; background: #f1f0f6; }   /* fixed-width PILL (centred text → no width snapping between states), same font size as the Revenue hero's .gsVerified chip (1.75cqh) */
  body:not(.mfill) .gbScreen .gcHead { display: flex; align-items: center; }   /* DESKTOP: vertically centre the Verified pill with the card title (per the user) — the absolute top:1.4cqh above sat it slightly high; flex align-items:center matches the pill's centre to the title's */
  body:not(.mfill) .gbScreen .gcStatus { position: static; margin-left: auto; }   /* on desktop the pill becomes a flex item pushed to the right, centred with the title (mobile keeps the absolute placement) */
  .gbScreen .gcStatus::before { content: 'Verify'; color: #8b8798; }                                              /* idle affordance */
  .gbScreen .govCard.arming .gcStatus { background: #ece9fb; }
  .gbScreen .govCard.arming .gcStatus::before { content: 'Verifying'; color: #5b5bd6; }                           /* dwell in progress */
  .gbScreen .govCard.arming .gcStatus::after { content: '.'; display: inline-block; width: 1.5cqh; text-align: left; vertical-align: bottom; color: #5b5bd6; animation: gcDots 1.05s steps(1, end) infinite; }   /* fixed-width, left-aligned → the dots fill out without shifting the "Verifying" text */
  .gbScreen .govCard.verified .gcStatus { background: #e4f7ec; animation: govStamp .44s cubic-bezier(.2,1.5,.4,1); }
  .gbScreen .govCard.verified .gcStatus::before { content: 'Verified'; color: #12a15f; padding-left: 2.15cqh; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3 5 19 14' fill='none' stroke='%2312a15f' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5l10 -10'/%3E%3C/svg%3E") left center / auto 1.3cqh no-repeat; }
  .gbScreen .govCard.verified .gcStatus::after { content: none; }
  @keyframes gcDots { 0% { content: '.'; } 33% { content: '..'; } 66%, 100% { content: '...'; } }
  @keyframes govStamp { 0% { transform: scale(2.4); opacity: 0; } 55% { transform: scale(1); opacity: 1; } }
  /* a short green flash on verify */
  .gbScreen .govCard::before { content: ''; position: absolute; inset: 0; z-index: 5; background: radial-gradient(circle at 50% 40%, rgba(126,224,168,0.5), rgba(126,224,168,0) 68%); opacity: 0; pointer-events: none; }
  .gbScreen .govCard.stamped::before { animation: govFlash .55s ease; }
  @keyframes govFlash { 0% { opacity: 0; } 18% { opacity: 1; } 100% { opacity: 0; } }
  /* per-surface reveal: chart unfolds from the baseline · AI answer fades in · Slack reply slides in */
  .gbScreen .gcBars { transition: transform .7s cubic-bezier(.22,1,.36,1); transform-origin: 50% 100%; }
  .gbScreen .gcChart:not(.verified) .gcBars { transform: scaleY(0.02); }   /* bars start FLAT (a baseline sliver), then unfold up to full on verify; the month labels stay put */
  .gbScreen .gcAi:not(.aianswered) .gcAnswer { display: none; }
  .gbScreen .gcAi.aianswered .gcAnswer { animation: gcPop .38s cubic-bezier(.2,1.5,.4,1); }
  @keyframes gcPop { 0% { opacity: 0; transform: translateY(0.6cqh) scale(0.92); } 100% { opacity: 1; transform: none; } }
  /* Slack: idle shows the "You" question; on verify it's REPLACED by the Lightdash AI reply sliding in */
  .gbScreen .gcSlackA { transition: opacity .4s ease, transform .4s ease; }
  .gbScreen .gcSlack:not(.verified) .gcSlackA { display: none; }   /* idle: only the "You" question · verified: the reply slides in BELOW it → both parts of the thread show */
  .gbScreen .gcSlack.verified .gcSlackA { animation: gcSlackIn .45s cubic-bezier(.2,1.2,.4,1); }
  @keyframes gcSlackIn { 0% { opacity: 0; transform: translateY(1cqh); } 100% { opacity: 1; transform: none; } }
  /* each wire lights once its surface is verified */
  .gbScreen .govWire { transition: opacity .3s ease; }
  .gbScreen .govWire.lit { opacity: 0.8; stroke-width: 1.8; filter: drop-shadow(0 0 1.1px rgba(143,123,255,0.55)) drop-shadow(0 0 2.4px rgba(143,123,255,0.28)); }   /* softened constant purple bloom once connected (pairs with the dithered .govGlow band) */
  #uiCloudy.govpilot { filter: drop-shadow(0 0.2cqh 0.7cqh rgba(126,224,168,0.6)); }

  /* ---- mobile breakpoint: slim the controls into a bottom bar, respect the iPhone safe area ---- */
  @media (max-width: 700px), (orientation: portrait) {
    .hint { display: none; }                       /* the top-right WASD hint is desktop-only */
    .gbScreen .daLine { stroke-width: 2.2; }        /* keep the forecast line readable on the smaller mobile chart (desktop is thinner) */
    .gbScreen .biLine { stroke-width: 2.2; }        /* same bump for the BI-as-code growth lines (non-scaling-stroke → px-based) */
    /* Data Apps on mobile: the portrait screen is TALLER, so the desktop scale(1.05) + -4cqh lift pushes the composed visual too far down and eats the bottom breathing room that desktop has. Scale the whole group down + lift it a touch so it keeps the same proportional daylight beneath the Book-a-Demo CTA. Declaring --dg-y/--dg-s here on .daGroup overrides the inline values the dev sliders set on .gbScreen (own value beats inherited). */
    .gbScreen .daGroup { --dg-y: -7cqh; --dg-s: 0.86; }
    /* mobile governance: keep the cards at the desktop 32cqh height so the centre Revenue hero stays in the row gap (only its edges touch the cards, the hub look) instead of covering the Slack/AI titles — a taller card here squeezes the gap and the hero overlaps. Trim the Slack thread text a touch so the verified reply still fits the 32cqh card at small px. */
    /* Slack thread on mobile: NO overrides — it uses the exact desktop layout (all cqh-based), so it just scales down proportionally with the screen. Any mobile-specific font/gap/avatar tweaks knocked the avatars out of place, so they're removed. */
    #panel, #daPanel { display: none !important; }            /* dev control panels are desktop-only — hide on mobile */
    #uiCloudy { width: 15.5%; }                       /* Cloudy reads bigger on the small mobile screen (matches the nav Cloudy's portrait size) — smaller than before */
    #uiLoader { width: 5%; }                         /* keep the load wheel bigger on mobile (desktop is smaller) */
    .gbScreen .preview.withstat .clogo { max-width: calc(35% * var(--ls,1)); max-height: calc(60% * var(--ls,1)); }   /* fal / OakNorth logos a touch bigger on mobile */
    /* bigger, more tappable/legible story content on mobile */
    .gbScreen .cta:not(.daCta):not(.agentCta):not(.mcCta):not(.vlearn):not(.priceCta):not([data-act="story"]) { padding: 2.8cqh 3.2cqh; font-size: 2cqh; }   /* generic mobile touch bump — now EXCLUDES the customers "Read story" CTA too (per the user: it should match the compact product SEE-MORE size, sized on the .custview .tile.active .cta rule below), plus the Data Apps/Agents/Metrics/pricing CTAs sized deliberately elsewhere */
    /* Book a Demo is now tied inside the card wireframe (scales with --vs-s), so it stays proportional on mobile — no separate mobile size/position needed */
    .gbScreen .wn { font-size: 3.6cqh; }
    .gbScreen .wr { font-size: 2.8cqh; }
    .gbScreen .avatar { width: 11.2cqh; font-size: 3.6cqh; }
    .gbScreen .dlogo { max-width: 26%; max-height: 6.4cqh; }
    /* Migration on mobile: 3 columns, but each card packs its logos into the SAME 2×2 grid as desktop so the cards stay SHORT. Card height is content-driven (no tall min-height), so the card row can never grow up into the title. */
    .gbScreen .vsub { font-size: 3.6cqh; }
    .gbScreen .migRow { bottom: 4cqh; }
    .gbScreen .migBoxes { align-items: stretch; gap: 2cqh; }               /* 3 columns still, equal heights */
    .gbScreen .migBox { aspect-ratio: auto; min-height: 56cqh; display: flex; flex-direction: column; padding: 2.8cqh; }   /* taller cards that fill the space below the title; footer stays pinned to the bottom via margin-top:auto */
    .gbScreen .migHead { position: static; gap: 2.2cqh; }   /* breathing room between the tool title and its support copy on mobile */
    .gbScreen .migTool { font-size: 3.2cqh; gap: 1.2cqh; }
    .gbScreen .priceview .migTool.priceTitle { font-size: 4.6cqh; }   /* pricing tier titles ("Self-hosted" / "$3000" / "Let's talk") read bigger on mobile than the Migration tool names */
    .gbScreen .mtoolLogo { height: 3.2cqh; }
    .gbScreen .mtoolLogo.wordmark { height: 3.8cqh; }
    .gbScreen .migCopy { font-size: 2.5cqh; line-height: 1.38; max-width: 100%; }
    .gbScreen .migFoot { position: static; margin-top: auto; padding-top: 2cqh; align-items: stretch; }   /* stretch children (eyebrow + pill grid) to the full card width */
    .gbScreen .migEyebrow { gap: 1.2cqh; margin-bottom: 0.6cqh; }
    .gbScreen .migEyebrow .mel { font-size: 1.8cqh; }
    /* keep the 2×2 grid on mobile (two rows of two → half the height of a 4-tall column) */
    .gbScreen .migLogos--grid { flex-direction: row; flex-wrap: wrap; align-items: flex-start; align-content: center; justify-content: center; gap: 2cqh 1.8cqh; }
    .gbScreen .migLogos--grid .mpill { width: 44%; gap: 0.6cqh; }
    .gbScreen .migLogos--grid .mpillLogo { height: 6cqh; }
    .gbScreen .migLogos--grid .mpillLogo.wordmark { height: 4cqh; }
    .gbScreen .migLogos--grid .mpillName { font-size: 1.5cqh; white-space: normal; line-height: 1.3; }
    .gbScreen .statl-sm { font-size: 2cqh; }   /* stat caption bigger + more legible on mobile */
    .gbScreen .preview .clogo { max-width: calc(72% * var(--ls,1)); max-height: calc(64% * var(--ls,1)); }   /* logo-only tiles (Workday wide bar / square) read bigger on mobile */
    .gbScreen .tile[data-cust="2"] .preview .clogo { max-width: calc(96% * var(--ls,1)); max-height: calc(88% * var(--ls,1)); }   /* Workday's tall arc+wordmark is height-constrained in the wide bar — let it fill much more on mobile */
    .gbScreen .priceview .migCopy { font-size: 2.9cqh; }   /* pricing tier supporting text bumped up on mobile */
    .gbScreen .brow.activerow .tile:not(.active) .clogo { max-width: 92%; max-height: 54%; }   /* when a story is open, keep the squeezed sibling logos (esp. Workday) readable on mobile */
    /* fal / OakNorth square stat tiles: tighten the corner padding on mobile so the top-left logo + bottom-right stat sit closer to the edges */
    .gbScreen .brow.activerow .tile:not(.active) .preview.withstat .clogo { top: 1.2cqh; left: 1.4cqh; max-width: 68%; max-height: 32%; }
    .gbScreen .brow.activerow .tile:not(.active) .statmini { bottom: 1.2cqh; right: 1.4cqh; }
    .gbScreen .tile.active { padding: 5.2cqh 5.6cqh; }   /* a little more breathing room inside an open story on mobile */
    /* mobile: quote up top, speaker + Read-story row dropped to the card bottom (logo stays pinned top-left) */
    .gbScreen .tdetail { justify-content: space-between; }
    .gbScreen .tile.active .quote { max-width: 100%; }
    .gbScreen .vclose { width: 13.4cqh; height: 13.4cqh; font-size: 6.8cqh; margin-top: 1cqh; margin-right: 1.4cqh; }   /* bigger ✕ on mobile; inset diagonally so it clears the rounded corner */
    .gbScreen .vhead::before { flex: 0 0 13.4cqh; }        /* keep the title centred against the bigger ✕ */
    .gbScreen .vhead { padding-top: 0.8cqh; }              /* title stays up near the top */
    /* title screen: use the DESKTOP proportions on mobile (pure scale-down) — the old overrides pushed Cloudy up + bigger so he overlapped the Lightdash logo */
    #panel {
      left: 8px; right: 8px; width: auto; bottom: auto; top: calc(8px + env(safe-area-inset-top));
      padding: 8px 10px; border-radius: 12px;
    }
    #panel h1 { display: none; }                   /* drop the title/subtitle to save vertical space */
    #panel .sub { display: none; }
    .row { margin: 4px 0; }
    .row label { font-size: 9px; }
    .btns { gap: 6px; margin-top: 7px; justify-content: center; }
    .btns button { padding: 8px 11px; font-size: 10px; }   /* bigger tap targets */
    input[type=range] { height: 5px; }
    input[type=range]::-webkit-slider-thumb { width: 20px; height: 20px; }   /* fat thumb for touch */
  }

  /* ================= MOBILE FULL-FILL (.mfill only — mfill is ONLY true on mobile after boot+START; desktop never gets this class) ================= */
  /* #mfillBg — the drifting CSS starfield. Now shown on DESKTOP too (z-index:-1, behind the transparent 3D .gbStage) so the same mobile starry backdrop appears behind the Game Boy; on mobile it's the full-screen backdrop. */
  #mfillBg {
    display: block; position: fixed; inset: 0; z-index: -1; pointer-events: none;
    background-color: #07060f;   /* matches the site's space backdrop */
    /* CSS starfield: many dot layers at different scales/opacities, drifting slowly (parallax) — denser field */
    background-image:
      radial-gradient(1.7px 1.7px at 22px 34px, rgba(236,233,255,0.95) 50%, transparent 52%),
      radial-gradient(1.3px 1.3px at 68px 12px, rgba(185,166,255,0.75) 50%, transparent 52%),
      radial-gradient(1.4px 1.4px at 128px 92px, rgba(236,233,255,0.8) 50%, transparent 52%),
      radial-gradient(1.1px 1.1px at 41px 63px, rgba(236,233,255,0.55) 50%, transparent 52%),
      radial-gradient(1.1px 1.1px at 152px 40px, rgba(205,195,255,0.6) 50%, transparent 52%),
      radial-gradient(1px 1px at 11px 11px, rgba(157,141,255,0.4) 50%, transparent 52%),
      radial-gradient(1px 1px at 96px 150px, rgba(236,233,255,0.5) 50%, transparent 52%),
      radial-gradient(0.9px 0.9px at 205px 176px, rgba(185,166,255,0.45) 50%, transparent 52%),
      radial-gradient(0.9px 0.9px at 168px 118px, rgba(236,233,255,0.42) 50%, transparent 52%),
      radial-gradient(0.8px 0.8px at 54px 128px, rgba(200,190,255,0.4) 50%, transparent 52%),
      radial-gradient(1.2px 1.2px at 88px 200px, rgba(236,233,255,0.7) 50%, transparent 52%),
      radial-gradient(1px 1px at 240px 60px, rgba(185,166,255,0.55) 50%, transparent 52%),
      radial-gradient(1.3px 1.3px at 300px 30px, rgba(236,233,255,0.62) 50%, transparent 52%),
      radial-gradient(0.9px 0.9px at 130px 250px, rgba(205,195,255,0.45) 50%, transparent 52%),
      radial-gradient(1.1px 1.1px at 20px 180px, rgba(236,233,255,0.5) 50%, transparent 52%),
      radial-gradient(0.8px 0.8px at 270px 140px, rgba(157,141,255,0.4) 50%, transparent 52%),
      radial-gradient(1px 1px at 190px 220px, rgba(236,233,255,0.55) 50%, transparent 52%),
      radial-gradient(0.9px 0.9px at 75px 96px, rgba(200,190,255,0.42) 50%, transparent 52%);
    background-size: 230px 230px, 175px 175px, 300px 300px, 125px 125px, 205px 205px, 85px 85px, 150px 150px, 255px 255px, 195px 195px, 110px 110px, 165px 165px, 120px 120px, 280px 280px, 145px 145px, 90px 90px, 210px 210px, 135px 135px, 175px 175px;
    background-repeat: repeat;
    animation: mfillDrift 240s linear infinite;
  }
  @keyframes mfillDrift {
    to { background-position: -520px 260px, -380px 190px, -280px 140px, -180px 90px; }
  }
  .mfill #glcanvas { display: none; }   /* WebGL hidden after boot on mobile (saves GPU) */
  .mfill #boot { display: none; }
  .mfill .gbScreen {
    display: block; opacity: 1; pointer-events: auto;
    /* !important beats any stale inline geometry left by place() if the viewport crossed into mfill mid-session (e.g. tablet rotation) */
    left: 0 !important; top: var(--nav-h, 0px) !important; width: 100vw !important; height: calc(var(--avail-h, 100dvh) - var(--nav-h, 0px)) !important;   /* --nav-h = the overlaying Framer nav's height (0 standalone). --avail-h = the true visible viewport height when embedded (Framer supplies it via ?availh= or postMessage) — falls back to 100dvh standalone. Inside an iframe 100dvh = the iframe box (can be taller than the fold), so --avail-h is what keeps "one screen" == the phone's real visible area. */
    transform: none !important;              /* override the matrix3d — flat, full-viewport (below the nav) */
    border-radius: 0;
    overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch;   /* Product may scroll; the one-screen views clip. overflow-x hidden: overflow-y:auto alone would compute overflow-x to auto → any off-screen absolute prop (e.g. the desktop #migBeam) makes the whole section PANNABLE sideways on touch, shifting/clipping card text at the left edge */
    /* container-type: size is inherited from the base rule → 1cqh ≈ 1vh, 1cqw ≈ 1vw in mfill */
  }
  .mfill #uiCloudy { display: none !important; }   /* no D-pad on mobile → hide the free-roam cursor everywhere (per-page Cloudy behaviours are a later pass) */
  .mfill .gbScreen #migBeam { display: none !important; }   /* the desktop abduction beam (38cqh-wide box tracking the hidden Cloudy) sticks ~60px past the right edge on Migration → phantom horizontal overflow; Cloudy never pilots on mobile so the beam is dead weight */
  /* ---- full-screen DOM planet nav grid (2×3) ---- */
  .mfill .gbScreen .mnav {
    box-sizing: border-box; min-height: 100%;
    display: grid; grid-template-columns: 1fr 1fr; gap: 2cqh 4cqw;
    align-content: space-evenly; justify-items: center;
    padding: 2cqh 3cqw;
    transform-origin: center center;
    animation: mnavCrashIn 0.26s cubic-bezier(0.2, 0.86, 0.3, 1.02) both;   /* fast "crash-zoom into the Game Boy" — the planets slam in from close as you enter the nav */
  }
  @keyframes mnavCrashIn { 0% { transform: scale(1.9); opacity: 0; filter: blur(1.4px); } 40% { opacity: 1; } 82% { transform: scale(0.985); filter: none; } 100% { transform: scale(1); opacity: 1; filter: none; } }
  .mfill .gbScreen .mnavHead { grid-column: 1 / -1; text-align: center; margin-bottom: 0.6cqh; }
  .mfill .gbScreen .mnavEyebrow { font-family: 'Press Start 2P', monospace; font-size: 1.42cqh; letter-spacing: 0.14em; color: var(--acc); text-transform: uppercase; }   /* 4px-rhythm: 12px @390×844 (currently unrendered — navGridHTML mounts tiles only) */
  .mfill .gbScreen .mnavTitle { margin-top: 1.6cqh; font-family: 'Britti Medium', 'Inter', sans-serif; font-weight: 500; font-size: 3.32cqh; letter-spacing: -0.01em; color: #fff; }   /* 4px-rhythm: 28px @390×844 (currently unrendered) */
  .mfill .gbScreen .mnavTile {
    position: relative; display: block;
    background: transparent; border: 0; padding: 0; cursor: pointer;
    -webkit-tap-highlight-color: transparent; touch-action: manipulation;
    transition: transform .12s ease;
  }
  .mfill .gbScreen .mnavTile:active { transform: scale(0.93); }   /* tap feedback */
  .mfill .gbScreen .mnavTile img, .mfill .gbScreen .mnavTile span { touch-action: manipulation; }   /* touch-action is NOT inherited — the tap lands on the img/label, so without this they default to `auto` and iOS reintroduces the tap-delay / gesture ambiguity (pointercancel instead of pointerup) that made a light tap flash :active but not navigate */
  .mfill .gbScreen .mnavTile img { display: block; width: min(42cqw, 29cqh); height: auto; image-rendering: pixelated; }   /* bigger planets → the 2×3 grid fills the mobile space more evenly (space-evenly distributes the rows). HEIGHT CAP (29cqh): planets were sized by WIDTH only, so when the available height shrinks (nav offset / shorter viewport) the 3 rows couldn't fit and the bottom row clipped. min() lets height bind on short viewports so all 3 rows always fit; on a normal tall phone 42cqw still wins (unchanged). */
  .mfill .gbScreen .mnavTile span {   /* label OVERLAYS the planet (across its centre), same as the 3D nav — not below it */
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-family: 'Press Start 2P', monospace; font-size: 1.42cqh; line-height: 1.35; text-transform: uppercase; white-space: nowrap;   /* 4px-rhythm: 12px @390×844; nowrap so "BOOK A DEMO" stays on one line */
    color: #fff; text-align: center; padding: 0 1cqw;
    text-shadow: 0 0.3cqh 0.6cqh rgba(0,0,0,0.55), 0 0 0.4cqh rgba(0,0,0,0.65);   /* legible over the varied planet colours */
  }
  /* ---- PRICING (mfill): header + 3 tier cards FLEX-SHARE exactly one screen (100dvh, overflow hidden — no scroll) ---- */
  .mfill .gbScreen .priceview { position: relative; inset: auto; height: 100%; max-height: 100%; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column; }   /* fixed-height clip; the base .view already flexes column */
  .mfill .gbScreen .priceview .vhead { flex: none; padding-left: 0; padding-top: 0.8cqh; }   /* fixed-size header (padding-left 0 beats the desktop .migview card-edge calc); the cards split whatever's left */
  .mfill .gbScreen .priceview .ptMain { white-space: normal; font-size: 4.27cqh; line-height: 1.1; }   /* 4px-rhythm: 36px @390×844 — bumped 32→36 per the user so ALL nav-page titles share one size */
  .gbScreen .pmMobBr, .gbScreen .migMobBr { display: none; }
  .mfill .gbScreen .priceview .pmMobBr { display: block; }   /* MOBILE: force the pricing hero to break after "to" → "Stop paying per seat to" / "look at your own data" (per the user) */
  .mfill .gbScreen .migview .migMobBr { display: block; }   /* MOBILE: break before "off" so it drops to line 2 → "We'll help you move" / "off legacy BI, without the rebuild." (per the user) */
  .mfill .gbScreen .migview .migDeskBr { display: none; }   /* MOBILE: drop the desktop break before "without" so "without the rebuild" joins line 2 instead of forcing a 3rd line (per the user); desktop keeps it */
  .gbScreen .custMobBr { display: none; }
  .mfill .gbScreen .custview .custMobBr { display: block; }   /* MOBILE: break the Customers title after "leading" → "Trusted by leading" / "data teams" (per the user) */
  .mfill .gbScreen .priceview .priceLede { font-size: 2.37cqh; line-height: 1.25; margin-top: 0.6cqh; }   /* 4px-rhythm: 20px @390×844 */
  .mfill .gbScreen .priceview .migRow { position: static; left: auto; bottom: auto; width: 100%; margin-top: 0.2cqh; flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; }   /* un-pin + take the remaining height */
  .mfill .gbScreen .priceview .migBoxes { flex: 1 1 0; min-height: 0; flex-direction: column; align-items: stretch; gap: 1.4cqh; padding: 0; }   /* full-width cards; no scroll padding — the view's own --pad bottoms it out */
  .mfill .gbScreen .priceview .migBox { aspect-ratio: auto; width: 100%; flex: 1 1 0; min-height: 0; padding: 1.7cqh 2.4cqh 2.4cqh; }   /* the three cards SHARE the column height equally; bottom padding = the 2.4cqh side padding so the CTA has EQUAL gap below it as at its sides */
  /* tier card type scaled to the short flexed card */
  .mfill .gbScreen .priceview .migEyebrow { margin-bottom: 0.8cqh; }
  .mfill .gbScreen .priceview .migEyebrow .mel { font-size: 1.42cqh; }   /* 4px-rhythm: 12px @390×844 — matches the Migration card eyebrow */
  .mfill .gbScreen .priceview .migTool.priceTitle { font-size: 3.32cqh; }   /* 4px-rhythm: 28px @390×844 (down a step, NOT up to 32 — keeps the tier titles clearly under the 32px page hero) */
  .mfill .gbScreen .priceview .priceUnit { font-size: 1.9cqh; }   /* 4px-rhythm: 16px @390×844 */
  .mfill .gbScreen .priceview .migCopy { font-size: 1.9cqh; line-height: 1.3; max-width: 82%; }   /* 4px-rhythm: 16px @390×844; width cap keeps the copy clear of the top-right floating Cloudy */
  .mfill .gbScreen .priceview .cta.priceCta { padding: 1.8cqh 2.8cqh; font-size: 1.42cqh; width: auto; max-width: none; min-width: 25cqh; justify-content: center; }   /* compact button matching the product SEE MORE CTA (1.8cqh × 2.8cqh / 1.42cqh); min-width 25cqh makes all THREE tier CTAs (Go to repo / Dive deeper / Get in touch) the SAME width for consistency (per the user), text centred; stays white; right-aligned via the migFoot flex-end */
  /* re-engage the desktop stacking inside the card (the shared mobile block made these static, muting their z-indexes): text (z-15) and CTA (z-6) always paint ABOVE the floating Cloudy (z-5); offsets reset so relative doesn't shift them */
  .mfill .gbScreen .priceview .migHead { position: relative; top: auto; left: auto; right: auto; gap: 0.8cqh; }
  .mfill .gbScreen .priceview .migFoot { position: relative; bottom: auto; left: auto; right: auto; padding-top: 0.8cqh; display: flex; align-items: flex-end; }   /* margin-top:auto (shared mobile) pins the CTA to the card BOTTOM; align-items:flex-end pushes the now auto-width CTA to the RIGHT → bottom-right of each card (per the user), matching the product SEE MORE placement */
  /* ---- per-card auto-floating Cloudy at that tier's evolution phase (replaces the piloted #uiCloudy, which mfill hides) ---- */
  .gbScreen .pmCloudy { display: none; }   /* inert on desktop — the piloted #uiCloudy handles the evolution there */
  .mfill .gbScreen .priceBox .pmCloudy {
    display: block; position: absolute; left: auto; right: 5cqw; top: 2.6cqh; bottom: auto; z-index: 5;   /* tucked IN from the top-right corner, beside the left-aligned eyebrow/title — z-5: UNDER the CTA (migFoot z-6), the tframe (z-13) and the tier text, so he can never cover them */
    height: auto; image-rendering: pixelated; pointer-events: none;
    transform: none;
    animation: pmBob 3.4s steps(4, end) infinite;   /* stepped retro bob, matching the site's old-game motion cadence */
  }
  .mfill .gbScreen .priceBox .pmCloudy--baby   { width: 10cqw; }                            /* Open Source → baby (smallest): a cloudyCanvas.toDataURL() snapshot of the desktop nav-cursor sprite (set in pricingHTML), NOT the boot ufo webp */
  .mfill .gbScreen .priceBox .pmCloudy--med    { width: 13cqw; animation-delay: -1.2s; }    /* Cloud Pro → medium: a touch smaller per the user (was 15cqw), still clearly bigger than the baby */
  .mfill .gbScreen .priceBox .pmCloudy--mother { width: calc(22.89cqw * var(--pcm-s, 1)); right: calc(4cqw - var(--pcm-x, 0cqw)); top: calc(3.6cqh + var(--pcm-y, 0cqh)); animation-delay: -2.3s; }    /* Enterprise → mothership (largest); negative delays de-sync the bobs; z-5 keeps them all UNDER title/copy/CTA. X/Y/Scale live-tunable via #priceCloudyPanel (--pcm-*): OFFSETS from the baked pose (x=0cqw,y=0cqh,s=1 = exactly here); +X moves RIGHT (shrinks the right offset), +Y moves DOWN; scale via width (NOT transform — pmBob owns transform) */
  /* the mothership's two escort babies (mirrors the desktop pOrbitA/pOrbitB flankers) — same cloudyCanvas snapshot sprite, one on each side, own bob phases; card overflow:hidden keeps them inside. Same offset-var scheme as the mothership: escort A = --pce1-*, escort B = --pce2-* (defaults 0/0/1 = the baked pose) */
  .mfill .gbScreen .priceBox .pmEscort--a { width: calc(7.15cqw * var(--pce1-s, 1)); right: calc(20cqw - var(--pce1-x, 0cqw)); top: calc(1.5cqh + var(--pce1-y, 0cqh)); animation-delay: -0.7s; }
  .mfill .gbScreen .priceBox .pmEscort--b { width: calc(7.15cqw * var(--pce2-s, 1)); right: calc(3.8cqw - var(--pce2-x, 0cqw)); top: calc(1.5cqh + var(--pce2-y, 0cqh)); animation-delay: -1.8s; }
  /* echo the desktop "Cloudy inside the frame" green dithered glow — static + subtle, centred behind each floating Cloudy (priceGlow self-centres via translate(-50%,-50%)) */
  .mfill .gbScreen .priceBox .priceGlow { opacity: 1; width: 40cqw; left: calc(100% - 9cqw) !important; top: 5cqh !important; }   /* BIGGER + full-strength dithered halo (was 0.5 × 26cqw — read as barely-there on the dark card) */   /* !important beats any stale inline left/top from a desktop→mfill crossover (tablet rotation); base position = fallback, overridden per tier below */
  /* per-tier CONCENTRIC halo: one shared position can't centre three different sprites — each Cloudy is right-anchored (right:R, width:W → centre-x = 100% − R − W/2) at top:2.6cqh, and his height follows his own source aspect (baby snapshot 56:48, cloudy-medium.webp 443:310, cloudy-mothership.webp 637:294 → centre-y = 2.6cqh + (W·h/w)/2 in cqw). Card order = PRICING order: 1 Open Source/baby · 2 Cloud Pro/med · 3 Enterprise/mother */
  .mfill .gbScreen .migBoxes .priceBox:nth-child(1) .priceGlow { left: calc(100% - 10cqw) !important; top: calc(2.6cqh + 4.3cqw) !important; }
  .mfill .gbScreen .migBoxes .priceBox:nth-child(2) .priceGlow { left: calc(100% - 12.5cqw) !important; top: calc(2.6cqh + 5.25cqw) !important; }
  .mfill .gbScreen .migBoxes .priceBox:nth-child(3) .priceGlow { left: calc(100% - 15cqw) !important; top: calc(2.6cqh + 4.85cqw) !important; }
  @keyframes pmBob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translate(0.5cqw, -1cqh); }   /* slow rise with a hair of drift; steps() chunks it into discrete frames per cycle (no -50% shift — the mfill Cloudy is right-anchored, not centre-anchored) */
  }

  /* ================= MOBILE FULL-FILL: remaining sections (first-pass portrait reflow — every rule .mfill-scoped, desktop untouched) ================= */
  /* ---- A. TITLES SWEEP: titles wrap + read large; the ✕ shrinks from the shared-mobile 13.4cqh (sized for the tiny 3D screen) so wrapped titles get the width ---- */
  .mfill .gbScreen .vtitle { white-space: normal; }
  .mfill .gbScreen .vclose { width: 10.5cqh; height: 10.5cqh; font-size: 5.6cqh; position: absolute; top: 1.2cqh; right: 1.6cqh; margin: 0; z-index: 20; }   /* BIGGER back-✕ tap target (9→10.5cqh per the user — no content clash now). Centered-title pages keep their ::before/::after spacers (title stays mid-header, clear of the corner); the left-aligned custview padding-right below is bumped to match so its title still clears */
  .mfill .gbScreen .vclose.arming { background: transparent; color: #fff; transform: none; }   /* kill the desktop Cloudy-dwell "hover to go back" glow/scale on mobile (there's no cursor — tapping the ✕ is the only back action) */
  /* LEARN MORE CTA (mobile only): flex SIBLING of .pcarTitles in the .vhead row — [title+sub column, flex:1, on the LEFT] … [button, flex:none, on the RIGHT] with the absolute ✕ pinned in the very top-right corner above it. Because the text column is flex:1 beside the fixed-width button, the title/subtitle wrap within the remaining left width and can NEVER run under the button (long titles like "Conversational Analytics" wrap to 2 lines in the narrower column — expected). align-self:flex-end sits the button BOTTOM flush with the text block's bottom (per the user's red line) — the text column is always taller than the 34px button so flex-end keeps it well below the ✕ on every slide (shortest block, Data Apps, still clears the ✕ by ~90px). margin-right:0 makes the gap from the button's right edge to the screen edge (~39px) equal the text block's left inset (~39px), per the user's equal-padding note. Purple pixel button matching the in-visual CTAs; no data-act → tapping it is a no-op for now (link wired later). */
  .mfill .gbScreen .pcarView .vlearn { display: inline-flex; align-items: center; position: absolute; right: 4cqh; bottom: 7cqh; left: auto; z-index: 12; margin: 0; opacity: 1; background: #5B5BD6; color: #fff; padding: 1.8cqh 2.8cqh; font-size: 1.42cqh; line-height: 1; white-space: nowrap; }   /* padding 1.8cqh × 2.8cqh (horizontal-weighted, vertical nudged up a touch per the user) per the button-padding refs — pixel CTAs read cramped with near-square padding; extra L/R gives the label breathing room. moved OUT of the header to a direct child of .pcarView → pinned to the BOTTOM-RIGHT of the whole slide, just above the pagination dots (per the user). right:4cqh mirrors the title's 4cqh left inset (view --pad 3.6cqh + vhead pad 0.4cqh); absolute children of .view anchor to the screen edges, same as the dots. One shared .vlearn → applies to every product slide */
  @media (hover: hover) { .mfill .gbScreen .vlearn:hover { background: #4a3fc9; color: #fff; } }
  .mfill .gbScreen .govCard .gcStatus { display: none; }   /* remove the "Verified" pill from the white governance cards on mobile */
  .mfill .gbScreen .govCard.stamped::before { animation: none; }   /* MOBILE: no green flash when a card gets its light from the Revenue card (per the user) — the ::before stays at opacity 0 */
  /* Data Apps deck internals (arrows / padding): NO mobile overrides — the mobile .daStage is now the exact desktop design box (88×56cqh, see the .pslide1 .daStage rule), so the desktop arrow size (5.3cqh), arrow position and deck padding render pixel-proportional to desktop once the box is scaled down. */
  .mfill .gbScreen .pcarTrack > .pslide { overflow: hidden; }   /* mobile: each carousel slide clips its own overflow — slide 2's BI-as-code terminal panel overhangs ~10cqh LEFT of its slide box and was painting OVER the Data Apps deck's right edge on slide 1. When a slide is active its own overhang is off-viewport anyway (the slide box IS the viewport), so this only removes cross-slide bleed; desktop untouched. */
  /* ---- Embedded-analytics chart polish (MOBILE only; desktop embVisual unchanged) ---- */
  .mfill .gbScreen .embChartArea { margin-top: 2.6cqh; }   /* more air between the "Total orders / 12,847" header and the graph below */
  .mfill .gbScreen .embYAxis { margin: 0; }   /* drop the box-expanding −0.5cqh half-line margins → the 1,200/800/400/0 labels span the SAME height as the 4 gridlines, so they're evenly spaced and line up with the chart */
  .mfill .gbScreen .embLegend { flex: 1; justify-content: center; }   /* centre the Current/Previous-period legend instead of pinning it hard-right */
  .mfill .gbScreen .vhead::before { flex: 0 0 8cqh; }   /* LEFT spacer — balances the RIGHT spacer below so the title/subtitle centre across the full header (the ✕ is position:absolute → out of flow → no longer balances the row itself). Trimmed from 11.5→8cqh: cqh units are HUGE horizontally on a tall phone, so 11.5cqh squeezed the title column and wrapped "Metrics Catalog" — 8cqh (paired with the smaller ✕) widens the column so it fits one line */
  .mfill .gbScreen .vhead::after { content: ''; flex: 0 0 9.4cqh; }   /* RIGHT spacer = the ✕ clearance → the centred title clears the absolute top-right ✕ (bumped 8→9.4cqh for the bigger ✕) */
  .mfill .gbScreen .migview .vhead::after, .mfill .gbScreen .custview .vhead::after { display: none; }   /* migration + customers keep their LEFT-aligned titles (no right spacer) */
  .mfill .gbScreen .daview .vtitle, .mfill .gbScreen .govview .vtitle, .mfill .gbScreen .custview .vtitle { font-size: 4.27cqh; margin-top: 0; }   /* 4px-rhythm: 36px @390×844 — ALL mobile page heroes now share the Migration hero size; the desktop "single-line hero" bump assumed one line, portrait wraps, so drop the negative nudge */
  .mfill .gbScreen .custview .vtitle { padding-right: 8.3cqh; }   /* left-aligned hero; reserve the bigger 10.5cqh ✕'s overlap zone (bumped 6.8→8.3 to match the bigger ✕) so "Trusted by leading data teams" wraps clear of it */
  .mfill .gbScreen .pcarSubRow { display: flex; align-items: center; gap: 1.2cqh; width: 100%; }   /* mobile: subtitle (left, flexes) + SEE MORE (right) on one row, so the title above spans full width */
  .mfill .gbScreen .pcarSub { flex: 1; min-width: 0; font-family: 'Inter', sans-serif; font-size: 2.37cqh; font-weight: 400; color: #F8FAFB; }   /* 4px-rhythm: 20px @390×844; regular-weight Inter (title keeps its own font). flex:1 → takes the row width left of the SEE MORE button */
  .mfill .gbScreen .pcarTitle { font-size: min(4.27cqh, 6.8cqw); white-space: nowrap; letter-spacing: -0.02em; padding-right: 7.1cqh; }   /* ONE LINE always (per the user): 36px (4.27cqh) where there's room, but auto-shrinks on narrow phones (7.4cqw cap) so "Conversational Analytics" (~381px @36px) never wraps or overflows the 390px width. white-space:nowrap forbids the 2-line wrap; -0.02em tightens tracking a hair; padding-right trimmed 8.5→4cqh to match the shrunk pcarView ✕ below. On a 390px iPhone this lands ~29px — 36px physically can't fit this title on one line at that width even edge-to-edge. */
  .mfill .gbScreen .pcarView .vclose { width: 10.5cqh; height: 10.5cqh; font-size: 5.8cqh; right: 0cqh; }   /* bigger back-✕ on the product carousel (9→10.5cqh per the user) + right:0 so its centred glyph lines up under the Framer nav's hamburger ☰ (which sits at the nav's right padding); the one-line title's padding-right + auto-fit cap below keep clearing it */
  /* LEFT-ALIGN the Product carousel + Governance titles/subtitles (drop the centering + ✕-clearance spacers, flush the column left — same treatment as Migration/Customers) */
  .mfill .gbScreen .pcarView .vhead::before, .mfill .gbScreen .pcarView .vhead::after,
  .mfill .gbScreen .govview .vhead::before, .mfill .gbScreen .govview .vhead::after { display: none; }
  .mfill .gbScreen .pcarTitles { align-items: flex-start; }
  .mfill .gbScreen .pcarTitle, .mfill .gbScreen .pcarSub { text-align: left; }
  .gbScreen .pcarBr { display: none; }   /* desktop: the '|' break spot collapses → title/sub wrap naturally at their own width */
  .mfill .gbScreen .pcarBr { display: block; }   /* mobile: empty block box forces the tuned 2nd line (per-slide break points baked into PRODUCT_SLIDES) */
  .gbScreen .priceBr { display: none; }   /* desktop: no forced break in the pricing tier copy — it wraps naturally (span collapses, word gap preserved) */
  .mfill .gbScreen .priceview .priceBr { display: block; }   /* MOBILE pricing copy: force the tuned 2nd line — Cloud Pro breaks before "in a…", Enterprise before "for…" (per the user) */
  .mfill .gbScreen .govview .vtitle, .mfill .gbScreen .govview .vsub { text-align: left; }
  .mfill .gbScreen .govview .vsub { font-size: 2.37cqh; line-height: 1.25; letter-spacing: -0.015em; font-weight: 400; color: #F8FAFB; margin: -1.6cqh 0 0; max-width: 74%; }   /* mobile governance sub: match the pricing sub-lede EXACTLY (2.37cqh / 1.25 / -0.015em / weight 400 / #F8FAFB) per the user; margin-top -1.6cqh keeps the title→sub spacing tight. max-width 74% ≈ the width of the forced line-1 "…powered" so no wrapped line runs longer than it (per the user) */
  .mfill .gbScreen .govview .vtitle span { white-space: nowrap; display: block; }   /* MOBILE: force the 2nd-colour span onto its own line → line 1 "Metrics you can" / line 2 "actually trust" (per the user); nowrap keeps "actually trust" together */
  .mfill .gbScreen .govview .vtitle .tdot { display: inline; }   /* the rule above also matched the nested full-stop span → it became its own block on line 3; force it inline so it sits right after "trust" (per the user) */

  /* ---- B. CUSTOMERS BENTO: the desktop row-bento restored, FLEXED to exactly one screen (100dvh, no scroll). Rows share the height like desktop (the 100cqh vertical budget is the same — only the width is narrow, and the %-based logos shrink with it) ---- */
  .mfill .gbScreen .custview { position: relative; inset: auto; height: 100%; max-height: 100%; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column; }   /* fixed-height clip — no growth, no scroll */
  .mfill .gbScreen .custview .vhead { flex: none; }
  .mfill .gbScreen .custview .bento { flex: 1 1 0; min-height: 0; justify-content: center; margin-top: 0.6cqh; padding-bottom: 0; }   /* bento takes exactly the remaining height (drops the old grow+scroll padding) */
  .mfill .gbScreen .custview .brow { flex: 1 1 0; min-height: 0; flex-wrap: nowrap; }   /* rows share the height (desktop max-height caps still apply and just cap them) */
  /* story open: the activerow becomes a COLUMN — active quote card on top FLEXES to the leftover height; siblings become short full-width bars below (corner logo/stat layout). The :has() selector out-specifies the desktop 3-tile grid rule */
  .mfill .gbScreen .bento > .brow.activerow,
  .mfill .gbScreen .bento > .brow.activerow:has(> .tile:nth-child(3)) { display: flex; flex-direction: column; flex-wrap: nowrap; flex: 1 1 0; min-height: 0; max-height: none; gap: var(--gap); }
  .mfill .gbScreen .custview .bento:has(.tile.active) .brow:not(.activerow) { flex: 0 0 6.6cqh; min-height: 0; }   /* non-active rows compress to slim strips so the open story gets the height (6.6cqh: with the bento now FOUR rows there are 3 compressed strips — trimmed from 8cqh so the story card keeps its speaker/CTA clearance) */
  .mfill .gbScreen .custview .brow.activerow .tile.active { flex: 1 1 0; min-height: 0; height: auto; padding: 2.4cqh 2.6cqh; }   /* the quote card takes whatever the siblings/rows don't (no fixed min-height). NO order override → tiles expand/collapse IN PLACE (tapping fal no longer yanks it to the top; Workday just collapses where it sits) */
  .mfill .gbScreen .custview .brow.activerow .tile:not(.active) { flex: 0 0 7.2cqh; height: auto; min-height: 0; }   /* squeezed siblings: short full-width bars */
  .mfill .gbScreen .custview .brow.activerow .tile[data-cust="5"] { order: 1; }   /* row 0 is DOM [fal(5), Workday(2)] — pin fal BELOW Workday so the default-open Workday story sits on top, and tapping fal expands it in place underneath the now-collapsed Workday bar (per the user: Workday stays collapsed on top of an expanded fal, no swap) */
  .mfill .gbScreen .custview .bento:has(.tile.active) .tile:not(.active) { padding: 1.4cqh 2.2cqh; }   /* slim-strip tiles can't afford the full 3.6cqh --pad */
  /* compressed-state stat/logo internals sized for the 8cqh strips */
  .mfill .gbScreen .custview .bento:has(.tile.active) .brow .tile:not(.active) .stat-sm { font-size: 2.37cqh; }   /* 4px-rhythm: 20px @390×844 */
  .mfill .gbScreen .custview .bento:has(.tile.active) .brow .tile:not(.active) .statl-sm { font-size: 0.95cqh; line-height: 1.25; }   /* 4px-rhythm: 8px @390×844 */
  .mfill .gbScreen .custview .bento:has(.tile.active) .brow .tile:not(.active) .statmini { gap: 0.4cqh; }
  /* squeezed STAT sibling in the activerow (fal under an open Workday story): drop the desktop corner-diagonal and lay it out HORIZONTALLY like JustWatch's bar — logo left, stat + caption grouped right, space-between for even daylight */
  .mfill .gbScreen .custview .brow.activerow .tile:not(.active) .preview.withstat { position: relative; display: flex; align-items: center; justify-content: space-between; padding: 0 4.2cqh 0 0; gap: 1.2cqh; }   /* right-pad so the caption clears the ↗ arrow (which sits mid-height in the short bar) */
  .mfill .gbScreen .custview .brow.activerow .tile:not(.active) .preview.withstat .clogo { position: static; min-width: 0; max-width: calc(30% * var(--ls,1)); max-height: 58%; }
  .mfill .gbScreen .custview .tile[data-cust="0"] .preview .clogo, .mfill .gbScreen .custview .tile[data-cust="3"] .preview .clogo, .mfill .gbScreen .custview .tile[data-cust="4"] .preview .clogo { max-width: calc(52% * var(--ls,1)); max-height: calc(48% * var(--ls,1)); }   /* Kraken / Adobe / ServiceNow logos smaller on mobile (down from the 72%×ls logo-only size) — this is the CLOSED-state size; the story-OPEN rule below handles the default-open view */
  .mfill .gbScreen .custview .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="0"] .preview:not(.withstat) .clogo, .mfill .gbScreen .custview .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="3"] .preview:not(.withstat) .clogo, .mfill .gbScreen .custview .bento:has(.tile.active) .brow:not(.activerow) .tile[data-cust="4"] .preview:not(.withstat) .clogo { max-width: 90%; max-height: 66%; }   /* STORY-OPEN state (the default-open Customers view): Kraken/Adobe/ServiceNow read too big beside Octopus's wide-but-short wordmark — HEIGHT-cap them to 66% ≈ 18px @390×844, a hair under Octopus's 19px (they're bolder so equal height reads heavier). maxWidth 90% stays loose so height is the binding constraint and all three land at the same cap-height. Higher specificity than line 484 (:has(.tile.active) 80%/92%), which otherwise sizes every non-withstat logo in the compressed rows and blew these three back up regardless of the rule above */
  .mfill .gbScreen .custview .brow.activerow .tile:not(.active) .preview.withstat .statmini { position: static; flex-direction: row; align-items: center; justify-content: flex-end; text-align: left; gap: 1cqh; }
  .mfill .gbScreen .custview .brow.activerow .tile:not(.active) .preview.withstat .statl-sm { text-align: left; }
  /* open-story internals: shrink the QUOTE TYPE to fit the flexed card instead of overflowing it */
  .mfill .gbScreen .custview .tile.active .tdetail { padding-bottom: 7cqh; gap: 1.6cqh; justify-content: flex-start; align-items: flex-start; }   /* LEFT-aligned (per the user): logo top-left; the quote+speaker are LEFT-aligned but VERTICALLY CENTRED between the logo and the bottom CTA via auto margins below. The Read-story CTA stays absolute bottom-right. */
  .mfill .gbScreen .custview .quote { font-size: 3.1cqh; max-width: 100%; line-height: 1.2; text-align: left; margin-top: auto; }   /* FIXED size so every expanded card's quote renders identically (per the user). Bumped 2.8→3.1cqh (per the user — a little bigger). LEFT-aligned. margin-top:auto + the .who margin-bottom:auto below split the free space → the quote+speaker block sits vertically CENTRED between the logo and the CTA */
  .mfill .gbScreen .custview .tile.active .who { margin-top: -0.4cqh; margin-bottom: auto; align-self: flex-start; }   /* speaker LEFT-aligned + close under the quote; margin-bottom:auto pairs with the quote's margin-top:auto to centre the block vertically */
  .mfill .gbScreen .custview .dlogo { max-width: 19%; max-height: 4cqh; margin-bottom: 1.2cqh; align-self: flex-start; }   /* slightly smaller expanded-card logo on mobile (per the user); LEFT-aligned at the top (align-self:flex-start) */
  .mfill .gbScreen .custview .avatar { width: 5.4cqh; font-size: 1.7cqh; }   /* speaker photo/initials slightly smaller (per the user) */
  .mfill .gbScreen .custview .wn { font-size: 2.37cqh; }   /* 4px-rhythm: 20px @390×844 */
  .mfill .gbScreen .custview .wr { font-size: 1.9cqh; }   /* 4px-rhythm: 16px @390×844 */
  /* FIXED speaker name/role sizes so every expanded card matches (per the user — no more per-word scaling). Sized to the LONGEST speaker "Aleksandr Zolotukhin / VP of Data & Analytics · JustWatch" (18-word story): the old formula rendered it at ~1.98cqh / ~1.58cqh on ONE line each, so 1.95cqh / 1.58cqh stays at or under that and keeps every name/role on one line without wrapping under the Read-story CTA */
  .mfill .gbScreen .custview .tile.active .wn { font-size: 1.95cqh; }
  .mfill .gbScreen .custview .tile.active .wr { font-size: 1.58cqh; }
  .mfill .gbScreen .custview .statl-sm { font-size: 1.9cqh; }   /* 4px-rhythm: 16px @390×844 — snaps the shared-mobile 2cqh (16.9px) stat caption in the CLOSED grid; the squeezed-state 0.95cqh rule above out-specifies this when a story is open */
  .mfill .gbScreen .custview .cta { font-size: 1.9cqh; }   /* 4px-rhythm: 16px @390×844 — snaps the closed-grid Read-story button off the shared-mobile 2cqh (16.9px); .tile.active .cta stays 12px via higher specificity */
  .mfill .gbScreen .custview .bento:not(:has(.tile.active)) .preview.withstat .statl-sm { font-size: 1.25cqh; line-height: 1.15; }   /* all-closed square tiles: shrink the fal/JustWatch stat caption so "DATA REQUESTS ANSWERED IN 24 HRS" wraps and fits without clipping */
  .mfill .gbScreen .custview .bento:not(:has(.tile.active)) .preview.withstat .stat-sm { font-size: 1.9cqh; }   /* match the caption down a notch so the horizontal stat block fits the narrow closed tile */
  .mfill .gbScreen .custview .tile.active .cta { padding: 1.8cqh 2.8cqh; font-size: 1.42cqh; }   /* READ STORY sized to EXACTLY match the products-page SEE MORE CTA (.vlearn: 1.8cqh 2.8cqh / 1.42cqh) — compact, stays white, same "Read story" text (per the user: it was too big). Now wins because the generic touch bump above excludes [data-act="story"] */

  /* ---- C. PRODUCT CAROUSEL: per-slide group transforms retuned for portrait width (the --dg mobile precedent); paging on mobile is SWIPE (window touchmove handler) — the side arrows are hidden here (desktop keeps them + their click wiring untouched); a Cloudy "swipe →" bubble + 5 pagination dots replace them (rules down by the parked-Cloudy block) ---- */
  .mfill .gbScreen .pcarArrow { display: none; }
  /* slide 0 (Conversational Analytics): restack — square visual fits the width under the title, CTA rides its bottom edge (via the shared --avis vars), tabs become a centred wrap row below */
  .mfill .gbScreen .pslide2 .agentGroup { --ag-x: -0.5cqh; --ag-y: -7.5cqh; --ag-s: 0.82; --avis-x: 50%; --avis-y: 27cqh; --avis-size: 94cqw; }   /* own values beat any dev-slider inline vars on .gbScreen; visual BIGGER (94cqw, near full width) and pushed down so the tabs fit ABOVE it. -7.5cqh: BAKED from the prodMobPanel CONV. ANALYTICS tuner (was -2) — lifts the visual so the tab bar clears the 2-line title + in-flow LEARN MORE button */
  .mfill .gbScreen .pslide2 .agentTabs { display: none; }   /* the desktop vertical tab column (and its base dark box-shadow) is fully retired on mobile — the platform selector is the .caSeg below, a WHOLESALE .daSeg clone, so no .agentTabs styling (smooth stepped shadow included) can reach the mobile bar */
  .mfill .gbScreen .pslide2 .caSeg { display: flex; position: absolute; z-index: 9; left: calc(50% + var(--m-at-x, 0cqh)); top: calc(16.5cqh + var(--m-at-y, 4cqh)); transform: translateX(-50%) scale(var(--m-at-s, 1.05)); transform-origin: 50% 0; max-width: 94cqw; }   /* centred on the visual box (x 0) + lifted (y 7→4cqh) so the pill + its dither shadow clear the top of the white window below (was overlapping) */   /* ONLY placement lives here — the face/pill/tabs/dither-shadow all come verbatim from the .daSeg/.daTab/.daSegPill rules. Sits where the old bar sat (same --m-at-* tuner vars); S 1.05 × group --ag-s 0.82 ≈ 0.864 = the Data Apps seg's rendered scale (--m-pdg-s 0.54 × --m-tb-s 1.60) → both bars render the SAME on-screen size, so the shared --seg-shadow dither rasterizes at the same texel pitch */
  .mfill .gbScreen .pslide2 .agentTile { display: flex; --atile-x: var(--m-tile-x, 96%); --atile-y: var(--m-tile-y, 66cqh); --atile-size: var(--m-tile-s, 10.25cqh); }   /* platform icon back on mobile; display:flex (NOT block) so the base's align/justify-center keeps the icon centred; X/Y/Size live-tunable via #cloudyMobPanel (the TILE ICON tuner, top-left) */
  .mfill .gbScreen .pslide2 .agentTile::before { clip-path: polygon(22% 0, 78% 0, 86.2% 0, 86.2% 8.3%, 91.7% 8.3%, 91.7% 13.8%, 100% 13.8%, 100% 22%, 100% 78%, 100% 86.2%, 91.7% 86.2%, 91.7% 91.7%, 86.2% 91.7%, 86.2% 100%, 78% 100%, 22% 100%, 13.8% 100%, 13.8% 91.7%, 8.3% 91.7%, 8.3% 86.2%, 0 86.2%, 0 78%, 0 22%, 0 13.8%, 8.3% 13.8%, 8.3% 8.3%, 13.8% 8.3%, 13.8% 0); }   /* mobile: the pixel corner-steps in % of the tile (not fixed cqh) → the stepped shape scales cleanly with --atile-size instead of distorting at small sizes */
  .mfill .gbScreen .agentCta { display: none; }   /* the visual-edge CTA is replaced by the slide-level .pcarCta (uniform bottom-centre, between the arrows) */
  /* slide 1 (Data Apps): neutralise the landscape collage offsets (--cr/--tb/--vs) → a simple centred column under the title */
  /* Data Apps (slide 1) — the mobile composition is fully DEV-TUNABLE via #daMobPanel: the group + each element (creator/tabs/visual) reads its own --m-* x/y/scale (defaults = a clean centred stack, group scaled to fit). */
  .mfill .gbScreen .pslide1 .daGroup  { transform: translate(var(--m-pdg-x, 0cqh), var(--m-pdg-y, 7.5cqh)) scale(var(--m-pdg-s, 0.54)); }
  .mfill .gbScreen .pslide1 .daPrompt { transform: translate(var(--m-cr-x, 0cqh), var(--m-cr-y, 0cqh)) scale(var(--m-cr-s, 1)); }
  .mfill .gbScreen .pslide1 .daSeg    { transform: translate(var(--m-tb-x, 0cqh), var(--m-tb-y, 0cqh)) scale(var(--m-tb-s, 1)); z-index: 5; }   /* z-index 5 (flex-item, no position needed) keeps the Slide-show/Dashboards/PDF tab bar ALWAYS above the creator card + stage — on load the creator window's reveal briefly painted over the tabs; this pins them to the forefront */
  .mfill .gbScreen .pslide1 .daStage  { transform: translate(var(--m-vs-x, 0cqh), var(--m-vs-y, 0cqh)) scale(var(--m-vs-s, 1.06)); width: var(--m-vs-w, 88cqh); height: calc(var(--m-vs-w, 88cqh) * 0.636); flex: none; max-width: none; max-height: none; }   /* FIXED width AND height at the DESKTOP deck design box (88×56cqh → ×0.636) so the dashboard is a straight DOWN-SCALED desktop — same internal layout, not a taller reflow. SCALE (--m-vs-s × group --m-pdg-s) shrinks it to fit the phone width (88×0.47×1.06 ≈ 43.8cqh — inside even a tall 2.2:1 phone). Fallbacks mirror the DA_MOB baked defaults. */
  #daMobPanel, #cloudyMobPanel { display: none; position: fixed; right: 0; top: 0; z-index: 999; width: 54vw; max-width: 260px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box;
    background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-left: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #daMobPanel .dmpTitle, #cloudyMobPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; }
  #daMobPanel .dmpSec, #cloudyMobPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #daMobPanel .dmpRow, #cloudyMobPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #daMobPanel .dmpRow label, #cloudyMobPanel .dmpRow label { width: 10px; color: #9990c4; }
  #daMobPanel .dmpRow input, #cloudyMobPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #daMobPanel .dmpRow b, #cloudyMobPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  #prodMobPanel { display: none; position: fixed; left: 0; right: 0; bottom: 11cqh; z-index: 999; box-sizing: border-box; align-items: center; gap: 8px; flex-wrap: wrap;
    background: rgba(10,8,22,0.94); color: #ece9ff; font: 500 11px/1.2 'Inter', system-ui, sans-serif; padding: 6px 10px; border-top: 1px solid rgba(158,146,255,0.4); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }   /* product-visual X/Y/Scale tuner — a bottom strip; JS sets display:flex on the mobile Product view */
  #prodMobPanel .pmpLabel { font-weight: 700; font-size: 10px; letter-spacing: 0.05em; color: #b9abff; flex: 0 0 100%; margin-bottom: 1px; }
  #prodMobPanel .pmpRow { display: flex; align-items: center; gap: 4px; flex: 1 1 28%; min-width: 92px; }
  #prodMobPanel .pmpRow label { color: #9990c4; width: 10px; }
  #prodMobPanel .pmpRow input { flex: 1; min-width: 0; height: 16px; }
  #prodMobPanel .pmpRow b { width: 32px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  /* collapse toggles — tap the panel's title/label to fold it away so the visual behind is fully visible */
  #daMobPanel .dmpTitle, #prodMobPanel .pmpLabel { cursor: pointer; user-select: none; }
  #daMobPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #daMobPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #daMobPanel.collapsed .dmpSec, #daMobPanel.collapsed .dmpRow { display: none; }
  #prodMobPanel .pmpLabel::after { content: ' ▾'; opacity: 0.7; }
  #prodMobPanel.collapsed .pmpLabel::after { content: ' ▸'; }
  #prodMobPanel.collapsed .pmpRow { display: none; }
  #cloudyMobPanel { left: 0; right: auto; width: 44vw; max-width: 200px; border-left: none; border-right: 1px solid rgba(158,146,255,0.35); }   /* Cloudy tuner sits TOP-LEFT (overrides the shared right-side position) */
  #cloudyMobPanel.collapsed .dmpSec, #cloudyMobPanel.collapsed .dmpRow { display: none; }
  #titleMobPanel { display: none; position: fixed; right: 0; top: 0; z-index: 999; width: 54vw; max-width: 260px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box;
    background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-left: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #titleMobPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; cursor: pointer; user-select: none; }
  #titleMobPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #titleMobPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #titleMobPanel.collapsed .dmpSec, #titleMobPanel.collapsed .dmpRow { display: none; }
  #titleMobPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #titleMobPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #titleMobPanel .dmpRow label { width: 10px; color: #9990c4; }
  #titleMobPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #titleMobPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  /* BI-as-code (mobile): the two windows offsettable via vars set by #biMobPanel */
  .mfill .gbScreen .pslide0 .biTerm { left: var(--m-bit-x, 0cqh); top: calc(-1cqh + var(--m-bit-y, 0cqh)); transform: scale(var(--m-bit-s, 1.05)); transform-origin: 0 0; }   /* per-window SCALE (unitless, #biMobPanel S) — origin top-left so the X/Y anchor holds while it scales; the window has no other transform to clobber */
  .mfill .gbScreen .pslide0 .biDash { left: calc(38.2cqh + var(--m-bid-x, 0cqh)); top: calc(36.1cqh + var(--m-bid-y, 0cqh)); transform: scale(var(--m-bid-s, 1.07)); transform-origin: 0 0; }
  #biMobPanel { display: none; position: fixed; right: 0; top: 0; z-index: 999; width: 50vw; max-width: 230px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box;
    background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-left: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #biMobPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; cursor: pointer; user-select: none; }
  #biMobPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #biMobPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #biMobPanel.collapsed .dmpSec, #biMobPanel.collapsed .dmpRow { display: none; }
  #biMobPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #biMobPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #biMobPanel .dmpRow label { width: 10px; color: #9990c4; }
  #biMobPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #biMobPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  #caMobPanel { display: none; position: fixed; right: 0; top: 0; z-index: 999; width: 50vw; max-width: 230px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box;
    background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-left: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #caMobPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; cursor: pointer; user-select: none; }
  #caMobPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #caMobPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #caMobPanel.collapsed .dmpSec, #caMobPanel.collapsed .dmpRow { display: none; }
  #caMobPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #caMobPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #caMobPanel .dmpRow label { width: 10px; color: #9990c4; }
  #caMobPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #caMobPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  #migCloudyPanel { display: none; position: fixed; left: 0; top: 0; z-index: 999; width: 50vw; max-width: 230px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box; background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-right: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #migCloudyPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; cursor: pointer; user-select: none; }
  #migCloudyPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #migCloudyPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #migCloudyPanel.collapsed .dmpSec, #migCloudyPanel.collapsed .dmpRow { display: none; }
  #migCloudyPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #migCloudyPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #migCloudyPanel .dmpRow label { width: 10px; color: #9990c4; }
  #migCloudyPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #migCloudyPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  /* ---- PRICING CLOUDY tuner (#priceCloudyPanel, top-left, dev-only) — X/Y/Scale offsets for the Enterprise card's mothership + two escorts (writes the --pcm / --pce1 / --pce2 x/y/s vars on :root, read by the .pmCloudy--mother/.pmEscort rules). Top-LEFT so it never covers the Enterprise card (bottom of the stack) while tuning. NB no asterisk-slash sequences in this comment — a "-*" glob right before "/" would end the comment early and eat the next rule ---- */
  #priceCloudyPanel { display: none; position: fixed; left: 0; top: 0; z-index: 999; width: 50vw; max-width: 230px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box; background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-right: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #priceCloudyPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; cursor: pointer; user-select: none; }
  #priceCloudyPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #priceCloudyPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #priceCloudyPanel.collapsed .dmpSec, #priceCloudyPanel.collapsed .dmpRow { display: none; }
  #priceCloudyPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #priceCloudyPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #priceCloudyPanel .dmpRow label { width: 10px; color: #9990c4; }
  #priceCloudyPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #priceCloudyPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  /* ---- BATCH CLOUDY tuner (#batchCloudyPanel, top-RIGHT, dev-only) — GROUP X/Y/Scale for the whole squad of 5 product-page Cloudys on mobile Product; writes --pcg-x/y/s on :root, folded into every #uiCloudy/#pcloudy2-5 rule. Top-right so it never covers the bottom-centre Cloudy formation while tuning. ---- */
  #batchCloudyPanel { display: none; position: fixed; right: 0; top: 0; z-index: 999; width: 50vw; max-width: 230px; max-height: 100dvh; overflow-y: auto; box-sizing: border-box; background: rgba(10,8,22,0.9); color: #ece9ff; font: 500 11px/1.3 'Inter', system-ui, sans-serif; padding: 8px 10px 14px; border-left: 1px solid rgba(158,146,255,0.35); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  #batchCloudyPanel .dmpTitle { font-weight: 700; font-size: 10px; letter-spacing: 0.08em; margin-bottom: 4px; color: #b9abff; cursor: pointer; user-select: none; }
  #batchCloudyPanel .dmpTitle::after { content: ' ▾'; opacity: 0.7; }
  #batchCloudyPanel.collapsed .dmpTitle::after { content: ' ▸'; }
  #batchCloudyPanel.collapsed .dmpSec, #batchCloudyPanel.collapsed .dmpRow { display: none; }
  #batchCloudyPanel .dmpSec { font-weight: 700; font-size: 9px; letter-spacing: 0.1em; margin: 7px 0 1px; color: #8f80e0; }
  #batchCloudyPanel .dmpRow { display: flex; align-items: center; gap: 5px; margin: 1px 0; }
  #batchCloudyPanel .dmpRow label { width: 10px; color: #9990c4; }
  #batchCloudyPanel .dmpRow input { flex: 1; min-width: 0; height: 16px; }
  #batchCloudyPanel .dmpRow b { width: 36px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  /* slides 2–4 (BI-as-code / Metrics Catalog / Embedding): fixed-size cqh comps → scaled ~1.6× up from the first pass (old 0.35/0.72/0.56 read tiny); Product is exempt from the one-screen rule, so the sides may crop on narrow phones — fine-tune to taste */
  .mfill .gbScreen .biGroup { --bg-x: 1cqh; --bg-y: -8cqh; --bg-s: 0.48; }   /* dialled-in mobile BI-as-code composition — BAKED from the prodMobPanel (X 1 / Y -8 / S 0.48 per the user) */
  .mfill .gbScreen .mcGroup { --mcg-x: 0cqh; --mcg-y: -4.5cqh; --mcg-s: 0.95; }   /* dialled-in mobile Metrics Catalog composition — BAKED from the prodMobPanel (X 0 / Y -4.5 / S 0.95 per the user) */
  .mfill .gbScreen .embGroup { --emg-x: 0cqh; --emg-y: -4.5cqh; --emg-s: 0.72; }   /* dialled-in mobile Embedding composition — BAKED from the prodMobPanel (X 0 / Y -4.5 / S 0.72 per the user) */
  /* ---- Embedding (mobile): STRONGER purple + heavier/chunkier pixel dither on the placeholder→chart "screen flash" ---- */
  .mfill .gbScreen .embPh { border-color: #8b6bff; background: linear-gradient(135deg, rgba(124,92,252,0.10), rgba(124,92,252,0.32)); }   /* deeper purple field behind the bolt (desktop keeps the faint 0.02→0.09 wash) */
  .mfill .gbScreen .embSheen { background: linear-gradient(115deg, transparent 30%, rgba(150,115,255,0.6) 44%, rgba(208,192,255,0.98) 50%, rgba(150,115,255,0.6) 56%, transparent 70%);
    -webkit-mask-image: var(--screen-tex); mask-image: var(--screen-tex); -webkit-mask-size: 7px 7px; mask-size: 7px 7px; -webkit-mask-repeat: repeat; mask-repeat: repeat; image-rendering: pixelated; }   /* wider, brighter sweep punched through the chunky Bayer dither (same mask trick as Cloudy's pboost trail) → a retro PIXEL flash, not a smooth sheen */
  .mfill .gbScreen .embPh::after { content: ''; position: absolute; inset: 0; background: #7c5cfc; opacity: 0; -webkit-mask-image: var(--screen-tex); mask-image: var(--screen-tex); -webkit-mask-size: 9px 9px; mask-size: 9px 9px; -webkit-mask-repeat: repeat; mask-repeat: repeat; image-rendering: pixelated; pointer-events: none; }   /* full-embed dithered PURPLE flash layer — invisible at rest */
  .mfill .gbScreen .embComp.embPlay .embPh::after { animation: embFlashM 0.5s 1900ms steps(4, end) both; }   /* fires right at the placeholder→chart handoff (embPhOut 2050 / embChartIn 2100): a chunky purple pixel-flash as the embedded chart "fully appears" */
  @keyframes embFlashM { 0% { opacity: 0; } 25% { opacity: 1; } 60% { opacity: 0.55; } 100% { opacity: 0; } }
  /* ---- Data Apps deck (mobile): past the cover slide, keep the cover TEXTURE knocked back to ~20% over black instead of a flat black void (the 80%-black gradient over da-bg.webp ≈ 20% texture) ---- */
  .mfill .gbScreen .daDeck { background-image: linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0.8)), url('../da-bg.webp'); background-size: cover; background-position: center; background-repeat: no-repeat; }
  .mfill .gbScreen .daDeck.coverbg { background-image: url('../da-bg.webp'); }   /* cover slide keeps the full-strength crisp backdrop */
  /* ---- Metrics Catalog: the single-continuous-outline seam fix (transparent own borders on .mcRow--top/.mcOpen + ONE .mcRow--top::after rounded stroke, mcOutlineGrow synced to mcOpenUp) originated here as a mobile-only fix and now lives UN-prefixed in the main .mcComp block above (~line 633) — it applies to BOTH desktop and mobile (identical cqh geometry; mobile just scales the group). No .mfill overrides needed. ---- */
  .mfill .gbScreen .daCta { display: none; }   /* Data Apps: hide the in-visual "Learn more" CTA on mobile */
  /* the in-group CTAs are replaced on mobile by the slide-level .pcarCta below (uniform size + position, independent of each group's scale) */
  .mfill .gbScreen .biCta, .mfill .gbScreen .mcCta, .mfill .gbScreen .embCta { display: none; }
  /* ---- mobile product CTA: ONE slide-level pixel button per slide, bottom-centre BETWEEN the two corner arrows; it's a direct child of .pslide (outside the scaled groups) so every CTA renders at the same on-screen size ---- */
  .gbScreen .pcarCta { display: none; }   /* inert on desktop — desktop keeps the in-group CTAs (.agentCta/.biCta/.mcCta) */
  .mfill .gbScreen .pslide .cta.pcarCta { display: none; }   /* removed on mobile — the base .cta{opacity:0} left it INVISIBLE but still occupying a tappable slot between Cloudy and the dots; hidden outright */
  @media (hover: hover) { .mfill .gbScreen .pslide .cta.pcarCta:hover { background: #4a3fc9; color: #fff; } }
  .mfill .gbScreen .pslide .cta.pcarCta.hot { background: #4a3fc9; color: #fff; }
  /* ---- mobile product Cloudy: decorative mascot parked bottom-centre just ABOVE the CTA/arrows (no D-pad → not piloted, no taps). The pboost trail that setProductSlide already fires makes him dash right/left as you page. !important beats the inline left/top/transform the product tick writes every frame; only revealed while the Product view (.pcarView) is mounted — every other mobile view keeps the base display:none!important ---- */
  .mfill .gbScreen:has(.pcarView) #uiCloudy.on { display: block !important; left: calc(50% + var(--pcloudy-x, 23.5cqw) * var(--pcg-s, 0.6) + var(--pcg-x, -27.5cqw)) !important; top: auto !important; bottom: calc(var(--pcloudy-y, 5cqh) * var(--pcg-s, 0.6) + var(--pcg-y, 3.5cqh)) !important; transform: translateX(-50%) scale(calc(var(--pcloudy-s, 1) * var(--pcg-s, 0.6))) !important; width: 19cqw; pointer-events: auto; cursor: pointer; animation: pcloudyPark 3.4s steps(4, end) infinite; }   /* GROUP vars --pcg-x/y/s (BATCH CLOUDY panel) move+scale all 5 as one about bottom-centre; per-Cloudy fallbacks unchanged so defaults 0/0/1 = baked pose */
  @keyframes pcloudyPark { 0%, 100% { margin-bottom: 0; } 50% { margin-bottom: 1cqh; } }   /* stepped idle bob (margin, not transform — the transform is pinned !important) */
  /* ---- 4 extra decorative Cloudies (2-5) flanking the parked #uiCloudy on the mobile Product page: each holds its own LIVE <canvas> (CW×CH, same as uiCloudyCanvas) that copies cloudyCanvas every frame in the Product tick → pixel-identical to #uiCloudy's crisp procedural sprite, pointer-events:none (only Cloudy 1 taps to advance). Base display:none → they NEVER appear on desktop or any non-Product view; only the .mfill :has(.pcarView) rules below reveal them. Each is tuned via its own --pcloudyN-x/y/s (MINI CLOUDY panel); all 5 share the pboost dash retriggered in setProductSlide. z-index 11 = just under #uiCloudy (12) so Cloudy 1 wins overlaps. ---- */
  .pcloudyExtra { display: none; }
  .mfill .gbScreen:has(.pcarView) .pcloudyExtra { position: absolute; display: block; top: auto; width: 19cqw; z-index: 11; pointer-events: none; filter: drop-shadow(0 3px 8px rgba(126,224,168,0.55)); animation: pcloudyPark 3.4s steps(4, end) infinite; }
  .mfill .gbScreen:has(.pcarView) .pcloudyExtra canvas { display: block; width: 100%; height: auto; image-rendering: pixelated; }   /* live copy of cloudyCanvas (56:48 backing buffer) — width-only so the aspect is never distorted, pixelated = same crispness as #uiCloudy's canvas */
  .mfill .gbScreen:has(.pcarView) #pcloudy2 { left: calc(50% + var(--pcloudy2-x, -8.5cqw) * var(--pcg-s, 0.6) + var(--pcg-x, -27.5cqw)); bottom: calc(var(--pcloudy2-y, 8cqh) * var(--pcg-s, 0.6) + var(--pcg-y, 3.5cqh)); transform: translateX(-50%) scale(calc(var(--pcloudy2-s, 0.8) * var(--pcg-s, 0.6))); }
  .mfill .gbScreen:has(.pcarView) #pcloudy3 { left: calc(50% + var(--pcloudy3-x, -25cqw) * var(--pcg-s, 0.6) + var(--pcg-x, -27.5cqw)); bottom: calc(var(--pcloudy3-y, 11.5cqh) * var(--pcg-s, 0.6) + var(--pcg-y, 3.5cqh)); transform: translateX(-50%) scale(calc(var(--pcloudy3-s, 0.8) * var(--pcg-s, 0.6))); }
  .mfill .gbScreen:has(.pcarView) #pcloudy4 { left: calc(50% + var(--pcloudy4-x, 10cqw) * var(--pcg-s, 0.6) + var(--pcg-x, -27.5cqw)); bottom: calc(var(--pcloudy4-y, 13.5cqh) * var(--pcg-s, 0.6) + var(--pcg-y, 3.5cqh)); transform: translateX(-50%) scale(calc(var(--pcloudy4-s, 1) * var(--pcg-s, 0.6))); }
  .mfill .gbScreen:has(.pcarView) #pcloudy5 { left: calc(50% + var(--pcloudy5-x, 34cqw) * var(--pcg-s, 0.6) + var(--pcg-x, -27.5cqw)); bottom: calc(var(--pcloudy5-y, 16.5cqh) * var(--pcg-s, 0.6) + var(--pcg-y, 3.5cqh)); transform: translateX(-50%) scale(calc(var(--pcloudy5-s, 1.3) * var(--pcg-s, 0.6))); }
  /* (per the user: NO glow halo behind the parked product Cloudy on any product slide — just the plain sprite. The Pricing .pmCloudy/.priceGlow halo is a separate section and stays.) */
  /* ---- mobile swipe hint + pagination dots (they replace the hidden arrows). Both decorative (pointer-events:none). Base display:none = desktop never sees them (same pattern as .pcarCta above) ---- */
  .gbScreen .pcarBubble, .gbScreen .pcarDots { display: none; }
  /* retro speech bubble "from" the parked Cloudy: white NES-style dialogue box (4 offset box-shadows = pixel border with cut corners), pinned just above his head (his bottom = --pcloudy-y, he's 14cqw wide × 48/56 aspect + 1cqh bob) with a stepped tail pointing down at him */
  .mfill .gbScreen:has(.pcarView) .pcarBubble { display: none; position: absolute; left: 50%; bottom: calc(var(--pcloudy-y, 12cqh) + 19cqw * 48 / 56 + 3.4cqh); transform: translateX(-50%); z-index: 13; pointer-events: none;
    background: #fff; color: #1a1a21; font-family: 'Press Start 2P', monospace; font-size: 1.42cqh; line-height: 1; padding: 1.25cqh 1.5cqh; white-space: nowrap;   /* 4px-rhythm: 12px @390×844 */
    box-shadow: 0 -0.45cqh 0 0 #1a1a21, 0 0.45cqh 0 0 #1a1a21, -0.45cqh 0 0 0 #1a1a21, 0.45cqh 0 0 0 #1a1a21;
    animation: pcarNudge 1.5s steps(2, jump-none) infinite; transition: opacity .5s ease; }
  .mfill .gbScreen:has(.pcarView) .pcarBubble::after { content: ''; position: absolute; left: 50%; bottom: -1.35cqh; transform: translateX(-50%); width: 1.35cqh; height: 1.35cqh; background: #fff;
    box-shadow: -0.45cqh 0 0 0 #1a1a21, 0.45cqh 0 0 0 #1a1a21, 0 0.45cqh 0 0 #1a1a21; }   /* pixel tail: a white step below the box edge, side/bottom shadows continue the border */
  .mfill .gbScreen:has(.pcarView) .pcarBubble .pbArr { display: inline-block; width: 1.8cqh; height: 1.55cqh; margin-left: 0.55cqh; vertical-align: -0.12cqh; background: #5B5BD6;
    clip-path: polygon(0 32%, 52% 32%, 52% 0, 100% 50%, 52% 100%, 52% 68%, 0 68%); }   /* CSS pixel "→" — Press Start 2P has no U+2192 glyph, a fallback-font arrow would break the pixel look */
  @keyframes pcarNudge { 0%, 100% { margin-left: 0; } 50% { margin-left: 1.6cqw; } }   /* stepped sideways nudge in the arrow's direction (margin — the transform slot carries the centring) */
  .mfill .gbScreen .pcarView[data-swiped="1"] .pcarBubble { opacity: 0; animation: none; }   /* first successful page (mobile: the swipe) fades the hint — setProductSlide stamps data-swiped on any dir!==0 move */
  /* 5 pagination dots: pixel squares centred on the very bottom edge, BELOW the slide-level CTA (bottom:4cqh) so nothing is covered; active = the site purple */
  .mfill .gbScreen .pcarDots { display: flex; position: absolute; left: 50%; bottom: 4cqh; transform: translateX(-50%); z-index: 12; gap: 1.8cqw; pointer-events: none; }   /* lifted off the very bottom edge */
  .mfill .gbScreen .pcarDots i { width: 1.2cqh; height: 1.2cqh; background: rgba(255,255,255,0.22); transition: background-color .25s ease; pointer-events: auto; cursor: pointer; }   /* clickable → jump to that slide; fractionally smaller (1.4→1.2cqh) per the user */
  .mfill .gbScreen .pcarDots i.on { background: #5B5BD6; }   /* active dot = the CTA button purple (per the user) */

  /* ---- D. MIGRATION: hero + 3 cards FLEX-SHARE exactly one screen (100dvh, overflow hidden — no scroll). Every rule :not(.priceview)-scoped so nothing here can collide with the Pricing block above ---- */
  .mfill .gbScreen .migview:not(.priceview) { position: relative; inset: auto; height: 100%; max-height: 100%; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column; }
  .mfill .gbScreen .migview:not(.priceview) .vhead { flex: none; padding-left: 0; padding-top: 0.8cqh; }   /* drop the desktop card-edge alignment calc */
  .mfill .gbScreen .migview:not(.priceview) .vtitle { font-size: 4.27cqh; }   /* 4px-rhythm: 36px @390×844 */
  .mfill .gbScreen .migview:not(.priceview) .vsub { flex: none; max-width: 100%; font-size: 2.37cqh; line-height: 1.25; margin-top: -1.6cqh; }   /* 4px-rhythm: 20px @390×844; margin-top -1.6cqh cancels the .view's 2cqh flex gap → tight title→sub spacing EXACTLY matching the other pages (per the user) */
  .mfill .gbScreen .migview:not(.priceview) .migRow { position: static; left: auto; bottom: auto; width: 100%; margin-top: 0.2cqh; flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; }
  .mfill .gbScreen .migview:not(.priceview) .migBoxes { flex: 1 1 0; min-height: 0; flex-direction: column; align-items: stretch; gap: 2.4cqh; padding: 0; }   /* more breathing room between the three cards */
  .mfill .gbScreen .migview:not(.priceview) .migBox { width: 100%; aspect-ratio: auto; flex: 1 1 0; min-height: 0; padding: 2.4cqh; }   /* EQUAL padding all around (was 1.2 top/bottom vs 2.2 sides — too tight vertically); the three cards SHARE the column height, footer pinned via margin-top:auto */
  .mfill .gbScreen .migview:not(.priceview) .migHead { gap: 0.9cqh; }
  .mfill .gbScreen .migview:not(.priceview) .migTool { font-size: 2.84cqh; white-space: normal; min-height: 0; gap: 1cqh; }   /* 4px-rhythm: 24px @390×844 */
  .mfill .gbScreen .migview:not(.priceview) .mtoolLogo, .mfill .gbScreen .migview:not(.priceview) .mtoolSvg { display: none; }   /* card platform icons (Looker / Power BI / Tableau) removed on mobile — keep just the text name */
  .mfill .gbScreen .migview:not(.priceview) .mtoolLogo.wordmark { height: 3cqh; }
  .mfill .gbScreen .migview:not(.priceview) .mtoolSvg svg { height: 2.6cqh; }
  .mfill .gbScreen .migview:not(.priceview) .migCopy { font-size: 1.9cqh; line-height: 1.3; margin-top: 0.7cqh; }   /* 4px-rhythm: 16px @390×844 (down a step from 20 — better hierarchy under the 24px card title); margin pulled to 0.7cqh so it sits closer to the title */
  .mfill .gbScreen .migview:not(.priceview) .migFoot { padding-top: 0.4cqh; gap: 0.9cqh; }
  .mfill .gbScreen .migview:not(.priceview) .migEyebrow { margin-bottom: 0.2cqh; gap: 1cqh; }
  .mfill .gbScreen .migview:not(.priceview) .migEyebrow .mel { font-size: 1.42cqh; }   /* 4px-rhythm: 12px @390×844 */
  /* the abductable customer pills: ONE horizontal row on mobile (desktop keeps its 2×2 grid) — logos never wrap; names may wrap under their own logo */
  .mfill .gbScreen .migview:not(.priceview) .migLogos--grid { flex-wrap: nowrap; align-items: flex-end; justify-content: space-between; gap: 0 1cqw; }   /* one row; pills bottom-aligned so 1- and 2-line names share a baseline */
  .mfill .gbScreen .migview:not(.priceview) .migLogos--grid .mpill { width: auto; flex: 0 1 auto; min-width: 0; gap: 0.45cqh; }   /* CONTENT-sized pills (not equal quarters): short names (STAKE) cede width to long ones (GENERATION HOME) so the pixel names never overlap neighbours */
  .mfill .gbScreen .migview:not(.priceview) .migLogos--grid .mpillLogo { height: 4.4cqh; max-width: 20cqw; }   /* logos bumped ~47% (3cqh → 4.4cqh) — the row still fits because the pills are content-sized and the names slightly smaller */
  .mfill .gbScreen .migview:not(.priceview) .migLogos--grid .mpillLogo.wordmark { height: 3.2cqh; max-width: 13cqw; }   /* wordmark logos: width-capped so the named pills beside them keep their one-row fit */
  .mfill .gbScreen .migview:not(.priceview) .migLogos--grid .mpillName { font-size: 0.8cqh; }   /* reverted to 0.8cqh — 0.95cqh (8px) overflowed and the pixel names collided (STAKE/TITANBAY/TICKETSWAP ran together) */
  /* ---- mobile-only header Cloudy (UFO) + tractor beam: decorative mascot in the header's black space (right of the two-line title, left of the ✕). Tap a card → its logo pills float up into the beam and settle back (JS toggles .mAbduct / .zap). ---- */
  .gbScreen .migUfo, .gbScreen .migBeamFront { display: none; }   /* inert on desktop — the piloted #uiCloudy + #migBeam handle the abduction there (same pattern as .pmCloudy) */
  /* ---- Cloudy SPRITE: stays BEHIND the title/✕ (z:-1) so it never covers them — only peeks from behind the headline ---- */
  .mfill .gbScreen .migview:not(.priceview) .migUfo { display: block; position: absolute; z-index: 11; top: calc(2.2cqh + var(--m-mufo-y, 2cqh)); right: calc(11.5cqh + var(--m-mufo-x, -3.5cqh)); width: calc(9cqh * var(--m-mufo-s, 1.28)); pointer-events: none; animation: pmBob 3.4s steps(4, end) infinite; }   /* the desktop nav-cursor Cloudy sprite; z:11 → sits IN FRONT of the beam (z:10). X/Y/Scale live-tunable via #migCloudyPanel (--m-mufo-*) */
  .mfill .gbScreen .migview:not(.priceview) .migUfoImg { position: relative; z-index: 1; display: block; width: 100%; height: auto; image-rendering: pixelated; }   /* cloudyCanvas snapshot is 56:48 → height follows the sprite aspect */
  /* ---- Green tractor BEAM: lifted OUT of .migUfo into a FRONT sibling so it can paint ABOVE the cards (the beam is what should shine ON TOP; the sprite stays behind). z:10 → over the card bodies + their z-7 tframe rings, but UNDER the ✕ (z-20). Its own pmBob keeps it glued under the bobbing sprite. Same box (top/right/width) as .migUfo so the cone is perfectly aligned under the saucer. ---- */
  .mfill .gbScreen .migview:not(.priceview) .migBeamFront { display: block; position: absolute; z-index: 10; top: calc(2.2cqh + var(--m-mufo-y, 2cqh)); right: calc(11.5cqh + var(--m-mufo-x, -3.5cqh)); width: calc(9cqh * var(--m-mufo-s, 1.28)); height: calc(7.7cqh * var(--m-mufo-s, 1.28)); pointer-events: none; animation: pmBob 3.4s steps(4, end) infinite; }
  /* GREEN tractor beam copied from the desktop #migBeam: the SAME baked Bayer-dithered green cone (--beam-tex), screen-blended (translucent → the card + rising logos read THROUGH it, tinted green = "pulled into the beam") + gently glowing. Static (mobile Cloudy doesn't cursor-track), descending from beneath him DOWN OVER the top card to the customer logos. */
  .mfill .gbScreen .migview:not(.priceview) .migUfoBeam { position: absolute; left: 50%; top: 62%; width: 250%; height: calc(42cqh * var(--m-mufo-s, 1.28)); transform: translateX(-50%); }   /* the cone-shaped texture handles the widening; this box frames it from under the saucer DOWN THROUGH the top card's logo row (26→42cqh: longer tractor beam so tapped logos clearly ride up inside it) */
  .mfill .gbScreen .migview:not(.priceview) .migUfoRay { position: absolute; inset: 0; background-image: var(--beam-tex); background-size: 100% 100%; background-repeat: no-repeat; image-rendering: pixelated; mix-blend-mode: screen; opacity: 0.72; transform-origin: top center; animation: beamGlow 2.6s ease-in-out infinite; }   /* EXACT same layer as the desktop #migBeam .mbRay — visible green rays, gently glowing */
  .mfill .gbScreen .migview:not(.priceview) .migBeamFront.zap .migUfoRay { animation: migUfoZap 1.1s steps(10, end) 1; }   /* tap → the green beam flares brighter/taller while the pills ride up (replaces the glow pulse for one cycle) */
  @keyframes migUfoZap {
    0%, 100% { opacity: 0.72; transform: scaleY(1); }
    35%      { opacity: 1;    transform: scaleY(1.2); }
    70%      { opacity: 0.85; transform: scaleY(1.08); }
  }
  /* tap-abduct: each pill rides its OWN diagonal vector (--fx/--fy px, set inline by the tap handler = delta from pill centre to the beam mouth under Cloudy) so the paths naturally FAN/CONVERGE on the UFO top-right — shrinking + fading as it enters the beam, VANISHING at 100% (fill forwards holds the vanish until .mAbduct is removed, then the pill snaps back to its layout spot for a clean re-tap) */
  @keyframes migMFloat {
    0%   { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 1; }
    55%  { transform: translate(calc(var(--fx, 0px) * 0.55), calc(var(--fy, 0px) * 0.55)) scale(0.6) rotate(var(--fr, -6deg)); opacity: 0.55; }
    100% { transform: translate(var(--fx, 0px), var(--fy, 0px)) scale(0.12) rotate(0deg); opacity: 0; }
  }
  .mfill .gbScreen .migview:not(.priceview) .migBox.mAbduct .mpill { animation: migMFloat 1.05s steps(14) forwards; animation-delay: calc(var(--pi, 0) * 0.07s); }   /* stepped old-game cadence (like pillAbduct); per-pill stagger via the existing --pi index; fill FORWARDS → pills stay vanished inside the beam until the JS timeout drops .mAbduct (no end-of-animation flash back) */
  @keyframes mReturnFade { from { opacity: 0; transform: translateY(-1cqh) scale(0.85); } to { opacity: 1; transform: translateY(0) scale(1); } }   /* after abduction, the pills FADE back into place (settle down + up from small) instead of snapping */
  .mfill .gbScreen .migview:not(.priceview) .migBox.mReturn .mpill { animation: mReturnFade 0.5s ease both; animation-delay: calc(var(--pi, 0) * 0.05s); }
  /* mobile-only line break in the Power BI card copy: an EMPTY BLOCK box injected after "open source, " (migrationHTML) forces "AI-native…" onto line 2 — inert everywhere else (desktop/SEO keep the one whole sentence) */
  .gbScreen .migBr { display: none; }
  .mfill .gbScreen .migview:not(.priceview) .migBr { display: block; }

  /* ---- E. GOVERNANCE: hub-and-spoke → a CONTENT-SIZED vertical stack on one screen (no scroll). The old equal-share flex (1 1 0) squeezed cards below their content and clipped the KPI grid / Slack thread; now the Revenue hero + Sales/AI/Slack cards each take exactly the height their content needs (flex: none → can never be squeezed into clipping) and the CHART card alone is flexible — its SVG stretches (preserveAspectRatio none) so it soaks up whatever height is left over. The SVG WIRES + DOTS render here too (only the halo stays hidden): layoutGovWires() has an mfill branch that radiates 4 straight lines from the hero's top/bottom-centre to each card's inner-edge middle, dot half-overlapping the card — same desktop stroke/bloom + the same arming/lit auto-verify sequence per card (status pill, flash, counters, chart unfold). ---- */
  .mfill .gbScreen .govview { position: relative; inset: auto; height: 100%; max-height: 100%; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column; }
  .mfill .gbScreen .govview .vhead { flex: none; }
  .mfill .gbScreen .govview .vtitle { font-size: 4.27cqh; }   /* 4px-rhythm: 36px @390×844 — now equals the shared mobile hero (the old 4.2 "one notch under" is within 0.6px of the same grid step) */
  .mfill .gbScreen .govStage { flex: 1 1 0; min-height: 0; display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); grid-template-rows: 1fr auto 1fr; gap: 3.4cqh; }   /* 2-col grid: Sales|Charts (row 1) and AI|Slack (row 3) are BOTH 1fr → all four white cards share EQUAL height; hero spans row 2 at its natural height. UNIFORM gap = equal padding between all cards + the connector-wire channel */
  .mfill .gbScreen .govSpineGlow { display: none; }   /* halo off — but the WIRES + DOTS now render on mobile too: layoutGovWires() detects mfill and routes 4 straight lines hero top/bottom-centre → each card's inner-edge middle (same stroke/glow/dot styling + the same arming/lit auto-verify classes as desktop) */
  /* Revenue hero — row 2 at its compact natural height, directly ABOVE the AI Chatbot (keeps the dark card + purple border) */
  .mfill .gbScreen .govSpine { position: static; order: -1; transform: none; width: 100%; flex: none; grid-row: 2; grid-column: 1 / -1; gap: 0.7cqh; padding: 1cqh 1.6cqh 1.1cqh; }   /* Revenue hero (mobile) */
  .mfill .gbScreen .gsMetric { padding-bottom: 0.7cqh; }
  .mfill .gbScreen .gsName { font-size: 2.37cqh; }   /* 4px-rhythm: 20px @390×844 */
  .mfill .gbScreen .gsDesc { font-size: 1.9cqh; line-height: 1.3; }   /* 4px-rhythm: 16px @390×844 */
  .mfill .gbScreen .gsChip { font-size: 1.42cqh; padding: 0.35cqh 0.65cqh; gap: 0.5cqh; }   /* 4px-rhythm: 12px @390×844 */
  .mfill .gbScreen .pxTick { width: 1.4cqh; height: 1.05cqh; }
  .mfill .gbScreen .gsCount { font-size: 1.42cqh; }   /* 4px-rhythm: 12px @390×844 */
  .mfill .gbScreen .gsCountIco { width: 1.9cqh; height: 1.7cqh; }
  /* the four surfaces: Slack spans both columns at natural height, AI spans both columns in the flexible row (it absorbs the spare height — its body just gains breathing room, content can never clip); Sales + Charts SHARE compact row 1 side-by-side at half width each — content-sized (auto row), so nothing can squeeze them into clipping */
  .mfill .gbScreen .govCard { position: static; width: 100%; height: 85%; align-self: center; min-width: 0; flex: none; display: flex; flex-direction: column; }   /* 85% of the 1fr row (15% shorter → squarer), centred in the track so the freed space splits evenly above/below; wires re-route off live geometry */
  .mfill .gbScreen .govCard.gcDash  { grid-row: 1; grid-column: 1; }
  .mfill .gbScreen .govCard.gcChart { grid-row: 1; grid-column: 2; }
  .mfill .gbScreen .govCard.gcAi    { grid-row: 3; grid-column: 1; }
  .mfill .gbScreen .govCard.gcSlack { grid-row: 3; grid-column: 2; }
  .mfill .gbScreen .gcHead { font-size: 1.9cqh; padding: 0.9cqh 0 0.6cqh; margin: 0 1.5cqh; }   /* 4px-rhythm: 16px @390×844 (base — every gov card overrides this with its own 12px head below) */
  .mfill .gbScreen .gcStatus { top: 0.5cqh; width: 9.6cqh; font-size: 1.35cqh; padding: 0.3cqh 0.55cqh; }   /* Verify/Verifying/Verified pill scaled with the head */
  .mfill .gbScreen .govCard.verified .gcStatus::before { padding-left: 1.7cqh; background-size: auto 1.05cqh; }   /* the ✓ tick scales with the smaller pill */
  .mfill .gbScreen .gcBody { padding: 0.7cqh 1.5cqh 0.8cqh; }
  /* the two HALF-WIDTH cards: tighter head + a smaller status pill so "Sales overview"/"Embedded charts" and the Verify pill share one line without touching */
  .mfill .gbScreen .gcDash .gcHead, .mfill .gbScreen .gcChart .gcHead { font-size: 1.42cqh; margin: 0 0.95cqh; padding: 1.1cqh 0 0.6cqh; }   /* 4px-rhythm: 12px @390×844; MORE top padding (was 0.6cqh — the titles read too tight) — horizontal inset matches the body's 0.95cqh so title + divider line up with the content below */
  .mfill .gbScreen .gcDash .gcStatus, .mfill .gbScreen .gcChart .gcStatus { width: 7.6cqh; font-size: 1.1cqh; padding: 0.28cqh 0.35cqh; top: 0.55cqh; }
  .mfill .gbScreen .gcDash.verified .gcStatus::before, .mfill .gbScreen .gcChart.verified .gcStatus::before { padding-left: 1.35cqh; background-size: auto 0.9cqh; }
  .mfill .gbScreen .gcDash .gcBody, .mfill .gbScreen .gcChart .gcBody { padding: 0.95cqh; }   /* EQUAL-padding unit: 0.95cqh = 8px @390×844 on all four sides (was 0.4/1.1/0.45 — vertical insets read pinched next to the 1.1 sides); every internal gap below uses the same unit */
  /* Sales overview at HALF width — the 2×2's nowrap uppercase labels ("AVG ORDER VALUE") can't fit two-up, so the KPIs restack as a single column of 4 slim rows: label left, value right → all four stay fully legible (row 1 is auto/content-sized: the card is exactly as tall as these rows need, no taller) */
  .mfill .gbScreen .gcDashBody { grid-template-columns: 1fr; grid-template-rows: repeat(4, 1fr); gap: 0.95cqh; }   /* KPI-row gap == the body's 0.95cqh edge padding → gap-between == top == bottom == left == right, one uniform spacing unit (rows are 1fr, so they just share the remaining height) */
  .mfill .gbScreen .gcMini { flex-direction: row; align-items: center; justify-content: space-between; gap: 0.6cqh; padding: 0.18cqh 0.8cqh; }
  .mfill .gbScreen .gcMiniLbl { align-self: center; font-size: 0.95cqh; }   /* 4px-rhythm: 8px @390×844 */
  .mfill .gbScreen .gcMiniNum { align-self: center; font-size: 1.42cqh; }   /* 4px-rhythm: 12px @390×844 */
  /* Embedded charts at HALF width — row 1 is content-sized so the Sales card's KPI stack sets the row height and the chart's stretchy SVG (flex:1 body) fills the same height; 6 bars narrow but the month labels keep their own centred columns */
  .mfill .gbScreen .gcChartBody { gap: 0.95cqh; }   /* bars→month-labels gap on the same 0.95cqh unit as the card's edge padding (bars are flex:1, they absorb the difference) */
  .mfill .gbScreen .gcBars { min-height: 3.4cqh; }   /* floor LOW enough that the 10.5cqh card floor fits head + bars + month labels — the card's overflow:hidden kills the min-content floor, so the explicit min-height must genuinely cover the content */
  .mfill .gbScreen .gcChartX span { font-size: 0.95cqh; }   /* 4px-rhythm: 8px @390×844 (axis labels sit on the same step as the KPI labels) */
  /* AI chat — the body reserves its FINAL height (question + answer) so the stack doesn't jump between the composer/typing/answered states */
  .mfill .gbScreen .gcAiBody { gap: 0.8cqh; min-height: 6.8cqh; }
  .mfill .gbScreen .gcInput { font-size: 1.7cqh; padding: 0.7cqh 1cqh; }
  .mfill .gbScreen .gcAnswer { font-size: 1.7cqh; padding: 0.15cqh 0.2cqh; }
  .mfill .gbScreen .gcComposer { font-size: 1.7cqh; min-height: 2cqh; padding: 0.7cqh 1cqh; }
  .mfill .gbScreen .gcCaret { height: 2cqh; }
  .mfill .gbScreen .gcTyping { padding: 0.8cqh 0.9cqh; }
  .mfill .gbScreen .gcTyping span { width: 0.65cqh; height: 0.65cqh; }
  /* Slack thread — the body reserves BOTH messages up front → the verified reply slides into space that already exists (nothing clips, nothing pushes) */
  .mfill .gbScreen .gcSlackBody { gap: 0.6cqh; min-height: 10.8cqh; }
  .mfill .gbScreen .gcSlackRow { gap: 0.8cqh; }
  .mfill .gbScreen .gcYouAv, .mfill .gbScreen .gcSlackIcon { width: 3cqh; height: 3cqh; border-radius: 0.7cqh; }
  .mfill .gbScreen .gcSlackName { font-size: 1.6cqh; }
  .mfill .gbScreen .gcSlackText { font-size: 1.6cqh; margin-top: 0.15cqh; line-height: 1.3; }   /* 1.35→1.3: pays for the equal 0.95cqh body padding — the two-message thread was ~4px over the card's fixed 1fr height */
  .mfill .gbScreen .gcSlackStats { font-size: 1.45cqh; }   /* one notch under the message text so the Total · Orders · Profit line stays on ONE line at phone width */
  .mfill .gbScreen .gcTime { font-size: 0.95cqh; }   /* 4px-rhythm: 8px @390×844 — timestamp metadata stays a full step under the 12px name */
  .mfill .gbScreen .gcApp { font-size: 0.95cqh; padding: 0.05cqh 0.4cqh; }   /* 4px-rhythm: 8px @390×844 */
  /* AI Chatbot + Slack alerts now SHARE row 3 side-by-side (half width each) — shrink both cards' internals so the chat and the Slack thread stay legible and unclipped at half width (Verify pill is already hidden on mobile, so the heads get the full card width) */
  .mfill .gbScreen .gcAi .gcHead, .mfill .gbScreen .gcSlack .gcHead { font-size: 1.42cqh; margin: 0 0.95cqh; padding: 1.1cqh 0 0.6cqh; }   /* MATCH the gcDash/gcChart heads (1.1cqh top) so all FOUR governance card titles have equal padding (AI Chatbot + Slack alerts were tighter at 0.55cqh) */
  .mfill .gbScreen .gcAi .gcBody, .mfill .gbScreen .gcSlack .gcBody { padding: 0.95cqh; }   /* same EQUAL 0.95cqh unit as gcDash/gcChart — the bottom-pinned composer (margin-top:auto) now sits 0.95cqh off the bottom edge == its 0.95cqh side gaps */
  .mfill .gbScreen .gcAi .gcInput, .mfill .gbScreen .gcAi .gcAnswer, .mfill .gbScreen .gcAi .gcComposer { font-size: 1.42cqh; padding: 0.4cqh 0.6cqh; }   /* 4px-rhythm: 12px @390×844 (up from 1.2 = 10.1px; nearest step) */
  .mfill .gbScreen .gcAiBody { min-height: 4.9cqh; gap: 0.95cqh; }   /* chat-stack gap on the shared 0.95cqh unit */
  .mfill .gbScreen .gcSlack .gcSlackName, .mfill .gbScreen .gcSlack .gcSlackText { font-size: 1.42cqh; }   /* 4px-rhythm: 12px @390×844 (up from 1.15 = 9.7px — 8px was the nearer step but reads too small for the message text; verify the thread still fits the card) */
  .mfill .gbScreen .gcSlack .gcSlackStats { font-size: 1.42cqh; white-space: normal; line-height: 1.3; }   /* same 12px size as the message text ("Your daily revenue update") per request; 1.3 line-height with the thread text — pays for the equal padding */
  .mfill .gbScreen .gcSlack .gcSep { display: block; width: 0; height: 0; overflow: hidden; }   /* mobile: EVERY separator becomes a break → four stacked metric lines (dots clipped) */
  .mfill .gbScreen .gcSlack .gcYouAv, .mfill .gbScreen .gcSlack .gcSlackIcon { width: 3.2cqh; height: 3.2cqh; }   /* profile pics bumped up */
  .mfill .gbScreen .gcSlack .gcSlackRow { gap: 0.5cqh; }
  .mfill .gbScreen .gcSlackBody { min-height: 7.4cqh; gap: 0.95cqh; }   /* thread-row gap on the shared 0.95cqh unit (card has spare height — the thread is content-sized, nothing clips) */
