/* ============================================================================
   enso-design.css — EnsoLab design-pass overrides (post-preservation phase).
   Loaded AFTER the Webflow export stylesheets on every Barba page, so
   equal-specificity rules here win by source order. One file for every
   intentional design deviation from the export.
   ========================================================================== */

/* ---- Global type scale: body/main text and small text bumped ~6-7% ---- */
:root {
  --_typography---font-size--text-main: clamp(1.0625rem, 1rem + 0.3vw, 1.1875rem);
  --_typography---font-size--text-small: clamp(0.9375rem, 0.875rem + 0.3vw, 1.0625rem);
  --_typography---font-size--text-tiny: clamp(0.71875rem, 0.66rem + 0.25vw, 0.8125rem);
}

/* ---- Hero fold-over (index): hero recedes under the about section ---- */
/* isolation:isolate makes the section a stacking context so its z:-1 video/
   overlay paint ABOVE the section's opaque theme background (they lost their
   stacking context when they were moved out of the transformed parallax
   target); the video drops to -2 so the darkening overlay stays above it. */
[data-enso-hero] { overflow: clip; isolation: isolate; }
[data-enso-hero] > video.u-cover-absolute { z-index: -2; }
#about { z-index: 2; }
/* scroll darkening: a TOP overlay above the hero content (the export's own
   overlay sits at z:-1 under the text and stays at its static 40%) —
   injected and scrubbed 0->1 by initHeroFold as the About right column
   scrolls past. Charcoal, matching the site's dark sections. */
.enso-hero-dark { position: absolute; inset: 0; background: var(--swatch--dark-900); opacity: 0; pointer-events: none; z-index: 5; }

/* ---- About section split layout (index) ---- */
.enso-about-grid { display: grid; grid-template-columns: 1fr 1fr; column-gap: 4rem; row-gap: 2.5rem; align-items: start; }
.enso-about-right { display: flex; flex-direction: column; align-items: flex-start; gap: 2rem; }
@media screen and (max-width: 991px) { .enso-about-grid { grid-template-columns: 1fr; } }

/* ---- Nav hairlines follow the section theme, like the dots/text do ----
   The export's nav_line rules use static theme vars at opacity .1 and were
   never included in the data-theme-nav colour switch. Full opacity and a
   uniform 1px thickness (the export's horizontal line is
   var(--nav--hamburger-thickness) ~1.5px; verticals are already 1px). */
.nav_line { transition: background-color 0.3s ease, opacity 0.3s ease; }
/* ROUND 26 §1 — THE reason the hairlines "don't show" (client, twice).
   They were never mispositioned: measured live at 390x844 the horizontal
   sits at the bar's bottom edge and the verticals span the bar, on index
   AND about, on fresh load AND after a Barba swap, menu open or closed.
   They were sub-visible. The rounds 2-4 tuning drove the WIDTH to
   0.175px, which is 0.34 device px on a 2x screen and 0.52 on a 3x
   phone — i.e. HALF A DEVICE PIXEL OR LESS. Blink alpha-composites that
   into a smear (why it renders on a desktop Chrome check); WebKit snaps
   sub-half-device-pixel boxes, which is why the same line is there on
   one iOS page/scroll position and gone on the next. No opacity value
   can rescue a box the rasteriser rounds away.
   Fix: pin the weight to EXACTLY ONE DEVICE PIXEL per screen density —
   the standard retina-hairline idiom (0.5px @2x), the thinnest line that
   is guaranteed to paint, and thinner in physical terms on a 3x phone
   than the old value pretended to be. The delicacy the client tuned for
   now lives in the ALPHA, which is a knob the rasteriser respects.
   ONE KNOB: --enso-hairline-o. Dots: 2.8px, FULL opacity, untouched. */
:root {
  --enso-hairline-w: 0.5px;  /* 2x screens: 1 device px */
  --enso-hairline-o: 0.35;
}
@media (min-resolution: 2.5dppx)  { :root { --enso-hairline-w: 0.34px; } } /* 3x phones */
@media (max-resolution: 1.25dppx) { :root { --enso-hairline-w: 1px;    } } /* 1x screens */
.nav_line.is-horrizontal { height: var(--enso-hairline-w); }
.nav_component .nav_line.is-left-vertical, .nav_component .nav_line.is-right-vertical { width: var(--enso-hairline-w); }
.nav_component[data-theme-nav="light"] .nav_line, [data-theme-nav="light"] .nav_line { background-color: var(--swatch--brand-text) !important; opacity: var(--enso-hairline-o); }
.nav_component[data-theme-nav="dark"] .nav_line, [data-theme-nav="dark"] .nav_line { background-color: var(--swatch--light-100) !important; opacity: var(--enso-hairline-o); }

/* ---- Nav link type: one step smaller (round 5) ----
   Client wants the whole nav row down a step — links + the Contact label
   together so they stay optically matched. 0.9 = the "one step", one knob.
   NOTE (measured, not what the brief assumed): on all six pages the
   VISIBLE desktop links carry no w-variant class and simply inherit
   text-main (~18.5px); the export's h3 sizing only ever hits the
   mobile-menu links (.is-desktop.u-flex-vertical-nowrap — the export
   reuses the class there). So: desktop links go to main x0.9, the
   mobile-menu links keep their h3 scale x0.9 (rule ORDER matters — the
   h3 row must come after the main row, both are (0,3,0)). The variant
   :where() rows cover the export's variant-classed links if they ever
   appear. Rule-level change only, markup untouched (syncNav-safe). */
.nav_component .nav_links_link.is-desktop { font-size: calc(var(--_typography---font-size--text-main) * 0.9); }
.nav_links_link.is-desktop:where(.w-variant-48c3d670-2982-8f83-a98a-a13861f8e8cb),
.nav_links_link.is-desktop:where(.w-variant-953abaad-8dd5-9e2d-4d77-eff82d8976c4) { font-size: calc(var(--_typography---font-size--h3) * 0.9); }
.nav_component .nav_links_link.is-desktop.u-flex-vertical-nowrap { font-size: calc(var(--_typography---font-size--h3) * 0.9); }
.nav_component .button_main_text { font-size: calc(var(--_typography---font-size--text-main) * 0.9); }

/* round 9 REPAIR of the round-8 A5 rule. The intent stands (the link-hover
   block passes UNDER the hairlines) but round 8's blanket
   `.nav_component .nav_line { position: relative }` broke the nav: the
   VERTICAL lines are absolutely positioned in the export
   (.nav_line.is-left-vertical / .is-right-vertical, height:100%), and the
   round-8 rule — equal specificity, loaded later — forced them back into
   normal flow, where a height:100% flow item with no definite parent
   height collapses to nothing (lines vanished) and the wrappers' content
   boxes shifted (the corner-anchored dots moved with them).
   Rule of repair: NEVER set position on the lines that are already
   absolute — z-index works on them as they are. Only the horizontal line
   is a static flex item and needs position:relative for z-index — and
   only the DESKTOP one: the mobile-menu variant
   (.is-horrizontal.is-mobile) is absolutely positioned by a same-
   specificity export rule and must be excluded for the same reason.
   Dots: z-index scoped to the four corner-dot classes that are already
   absolute, never blanket .nav_dot. */
.nav_component .nav_line.is-left-vertical,
.nav_component .nav_line.is-right-vertical { z-index: 3; }
.nav_component .nav_line.is-horrizontal:not(.is-mobile) { position: relative; z-index: 3; }
.nav_component .nav_dot.is-logo,
.nav_component .nav_dot.is-logo-top,
.nav_component .nav_dot.is-links,
.nav_component .nav_dot.is-links-top { z-index: 4; }

/* ---- Nav corner dots (2px -> 4px -> 2.8px, full opacity) ---- */
.nav_dot.is-logo, .nav_dot.is-logo-top, .nav_dot.is-links, .nav_dot.is-links-top { width: 2.8px; height: 2.8px; opacity: 1; }

/* ---- Nav dots sit centred on the hairline intersections ----
   The dots are corner-anchored inside their wrappers, whose edges are where
   the hairlines run; translating by half the dot's size puts its centre on
   the corner point (= the line intersection). Scoped to .nav_component: the
   technologies tab dots share the is-links class and must not move. Top dots
   have no horizontal line to cross, so they only centre onto the vertical
   line and stay tangent to the viewport top (a -50% Y would clip them). */
.nav_component .nav_dot.is-logo { transform: translate(50%, 50%); }
.nav_component .nav_dot.is-links { transform: translate(-50%, 50%); }
/* round 22 §2: the export parks this dot bottom-LEFT of the mobile contain
   (left:0), where no vertical line exists — the "rogue square" (client).
   Moved to the intersection of the mobile right-vertical (inset right 4rem,
   mirroring that line's own offset) and the bottom hairline, left of the
   menu button. The is-logo dot at the LEFT intersection is the one the
   client likes — untouched. */
.nav_component .nav_dot.is-links.is-mobile { left: auto; right: 4rem; transform: translate(50%, 50%); }
.nav_component .nav_dot.is-logo-top { transform: translate(50%, 0); }
.nav_component .nav_dot.is-links-top { transform: translate(-50%, 0); }

/* ---- SkyBridge section (index) ----
   Panels start half a viewport sooner so the title is on screen before the
   video reaches fullscreen; first panel centred and raised. overflow:clip
   bounds the video stack's slide-down parallax without breaking the sticky
   (clip, unlike hidden, creates no scroll container). */
#skybridge { overflow: clip; }
#skybridge .sticky-text_contain { margin-top: -50vh; }
#skybridge .sticky-text_layout:first-child .u-content-wrapper.u-column-1 { text-align: center; margin-top: -8vh; }
/* every .u-content-wrapper resets text-align to start via --_alignment---direction;
   cover the nested ones too so the More button stays centred */
#skybridge .sticky-text_layout:first-child .u-content-wrapper { text-align: center; }
#skybridge .sticky-text_layout:first-child .eyebrow_wrap { justify-content: center; }
#skybridge .sticky-text_layout:first-child .eyebrow_layout { justify-content: center; }
#skybridge .sticky-text_layout:first-child .c-paragraph { margin-left: auto; margin-right: auto; text-align: center; }

/* ---- SkyBridge connector lines (index) ----
   Centred vertical hairline: low-opacity track whose white fill scrubs
   top-down with scroll; a 4px square cap (nav-dot sized) rides the fill's
   leading edge and rests at the line's end. The intro connector's start is
   pulled up to sit just below the More button by JS (positionSkyConnector —
   the 100vh panel leaves a viewport-dependent gap that CSS can't know). */
.enso-connector { display: flex; justify-content: center; height: 60vh; pointer-events: none; }
.enso-connector.is-quote { height: 50vh; }
.enso-connector_track { position: relative; width: 1px; height: 100%; background: rgba(239, 238, 236, 0.25); }
.enso-connector_fill { position: absolute; inset: 0; background: var(--_theme---text); transform-origin: 50% 0; }
.enso-connector_cap { position: absolute; top: 0; left: 50%; width: 4px; height: 4px; background: var(--_theme---text); transform: translate(-50%, -50%); }

/* ---- SkyBridge phase timeline: sticky, Joby-style columns (index) ----
   The panel pins for 320vh; the centred H3 stays visible (dimmed while the
   timeline runs). Below the single centre-out hairline, one row of columns:
   Phase label | title stack | small meta | paragraph | status pill. All
   four titles are always visible, stacked — the active one full-size and
   full-opacity right under the line, the others faded; the stack slides so
   the active title holds that position (initSkybridgePhases). The other
   columns crossfade their four stacked items per phase. Content hugs the
   line (the reference sits its year directly beneath it). */
.enso-phases { height: 320vh; }
.enso-phases_sticky { position: sticky; top: 0; height: 100vh; height: 100svh; /* round 24 §0.2: the pinned screen = what the reader SEES */ display: flex; flex-direction: column; align-items: center; justify-content: center; }
.enso-phases_title { text-align: center; margin-bottom: 8vh; }
.enso-phases_title h3 { margin: 0; }
/* the whole block sits inset from the page edges — the material above and
   below is centred, so this pulls in without being fully centred itself */
.enso-phases_stack { position: relative; width: min(88%, 74rem); padding-top: 1.1rem; }
.enso-phases_line { position: absolute; top: 0; left: 0; right: 0; height: 1px; background: rgba(239, 238, 236, 0.2); }
.enso-phases_line-fill { position: absolute; inset: 0; background: var(--_theme---text); transform-origin: 50% 50%; }
.enso-phases_grid { display: grid; grid-template-columns: max-content minmax(11rem, max-content) max-content minmax(0, 28ch) max-content; column-gap: clamp(1.5rem, 3.5vw, 3.5rem); align-items: start; justify-content: space-between; width: 100%; }
.enso-phases_col { display: grid; padding-top: 0.5em; }
.enso-phases_col > .enso-phases_item { grid-area: 1 / 1; visibility: hidden; }
.enso-phases_col.is-label, .enso-phases_col.is-meta { text-transform: uppercase; }
.enso-phases_col.is-para p { margin: 0; }
.enso-phases_col.is-pill { justify-self: end; }
/* past titles slide up INTO this mask (clipped just under the line) instead
   of crossing it; upcoming titles wait faded below with real breathing room */
.enso-phases_titles-mask { overflow: hidden; }
.enso-phases_titles { display: flex; flex-direction: column; gap: 0.8em; }
.enso-phases_titleitem { margin: 0; opacity: 0.22; transform-origin: left top; }
.enso-phase_pill { display: inline-block; padding: 0.2em 0.9em; border: 1px solid rgba(239, 238, 236, 0.35); border-radius: 100em; color: rgba(239, 238, 236, 0.85); }
.enso-phase_pill.is-active { color: var(--swatch--light-100); border-color: var(--swatch--light-100); }
@media screen and (max-width: 991px) {
  .enso-phases_grid { grid-template-columns: max-content 1fr max-content; row-gap: 1.5rem; }
  .enso-phases_col.is-para { grid-column: 1 / -1; }
  .enso-phases_col.is-pill { justify-self: end; }
}
/* round 19 (§8.3 mobile): at phone widths the three-column row starved the
   titles column — "Payload Activation" clipped mid-glyph inside the mask's
   overflow:hidden. Full restack: label + meta share the top row, the titles
   mask and paragraph each take the full width, the pill closes. CSS ONLY —
   the phases markup is byte-identical on index and skybridge (hash-checked),
   so a CSS-scoped change is the only kind allowed here. */
@media screen and (max-width: 767px) {
  .enso-phases_grid { grid-template-columns: max-content 1fr; row-gap: 1.25rem; }
  .enso-phases_col.is-label { grid-column: 1; grid-row: 1; }
  .enso-phases_col.is-meta { grid-column: 2; grid-row: 1; justify-self: end; }
  .enso-phases_titles-mask { grid-column: 1 / -1; grid-row: 2; }
  .enso-phases_col.is-para { grid-column: 1 / -1; grid-row: 3; }
  .enso-phases_col.is-pill { grid-column: 1 / -1; grid-row: 4; justify-self: start; }
  .enso-phases_titles { gap: 0.5em; }
}

/* ---- SkyBridge founder quote (index) ----
   The quote connector splits at the box's top centre: the box outline sits
   at low opacity (::before) and six white edge fills draw it — top edges
   outward from the centre, sides downward, bottom edges corner-to-centre —
   then 4px corner squares (nav dots) pop and the quote scrambles in
   (initEnsoQuote). The text_1 gradient highlight is applied after the
   scramble, recoloured for the dark section. */
/* the quote pins centred (sticky 100vh block) with a 100vh dwell after it,
   during which #technologies (pulled up by its own negative margin, higher
   z-index, opaque theme background) scrolls in OVER the pinned quote */
/* client-tuned gaps (Aug 2026): the whole quote block rides up 18vh so its
   connector line appears while Ground Validation is still on screen, and
   the sticky rides up 20vh inside it so the line drops off just above the
   quotebox instead of half a viewport short. min-height drops 250->230vh
   to match the 20vh the sticky pull-up removed from the content flow, so
   the pinned dwell stays exactly 100vh. */
.enso-quote { display: flex; flex-direction: column; align-items: center; min-height: 230vh; margin-top: -18vh; }
.enso-quote_sticky { position: sticky; top: 0; min-height: 100vh; min-height: 100svh; /* round 24 §0.2 */ width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: -20vh; }
#technologies { position: relative; z-index: 2; margin-top: -100vh; }
.enso-quotebox { position: relative; width: min(66rem, 94vw); padding: clamp(2.5rem, 5vw, 3.5rem) clamp(1.75rem, 3.5vw, 2.75rem); text-align: center; }
.enso-quotebox::before { content: ""; position: absolute; inset: 0; border: 1px solid rgba(239, 238, 236, 0.2); pointer-events: none; }
.enso-quotebox_edge { position: absolute; background: var(--_theme---text); pointer-events: none; }
.enso-quotebox_edge.is-top-l { top: 0; left: 0; width: 50%; height: 1px; transform-origin: 100% 50%; }
.enso-quotebox_edge.is-top-r { top: 0; right: 0; width: 50%; height: 1px; transform-origin: 0% 50%; }
.enso-quotebox_edge.is-side-l { top: 0; left: 0; width: 1px; height: 100%; transform-origin: 50% 0%; }
.enso-quotebox_edge.is-side-r { top: 0; right: 0; width: 1px; height: 100%; transform-origin: 50% 0%; }
.enso-quotebox_edge.is-bot-l { bottom: 0; left: 0; width: 50%; height: 1px; transform-origin: 0% 50%; }
.enso-quotebox_edge.is-bot-r { bottom: 0; right: 0; width: 50%; height: 1px; transform-origin: 100% 50%; }
.enso-quotebox_dot { position: absolute; width: 4px; height: 4px; background: var(--_theme---text); pointer-events: none; }
.enso-quotebox_dot.is-tl { top: 0; left: 0; transform: translate(-50%, -50%); }
.enso-quotebox_dot.is-tr { top: 0; right: 0; transform: translate(50%, -50%); }
.enso-quotebox_dot.is-bl { bottom: 0; left: 0; transform: translate(-50%, 50%); }
.enso-quotebox_dot.is-br { bottom: 0; right: 0; transform: translate(50%, 50%); }
.enso-quote_text { margin: 0 auto; }
.enso-quote_attr { margin-top: 2rem; text-transform: uppercase; letter-spacing: 0.06em; }
.enso-quote_attr-name { color: var(--swatch--light-100); }
.enso-quote_attr-rest { opacity: 0.55; }

/* (the long outlined .enso-quote_btn was replaced by the export's real
   primary button component — see the CTA note in the Core Technologies
   block below; the reveal timeline still hooks [data-enso-quote-btn]) */

/* the splash corner frame is decorative — never let it eat clicks */
.unicor_studio .nav-corners { pointer-events: none; }

/* during the pinned dwell the scene whites out, finishing exactly as the pin
   releases — so the incoming (white) Core Technologies section lands on white */
/* full-bleed: the sticky lives inside the padded container, so inset:0
   left the page margins dark — span the viewport width instead */
.enso-quote_white { position: absolute; top: 0; bottom: 0; left: 50%; width: 100vw; transform: translateX(-50%); background: var(--swatch--light-100); opacity: 0; pointer-events: none; z-index: 3; }

/* ---- footer aurora: full-width beam field, now on BLACK (Aug 2026) ----
   Client trial: the footer base flipped white -> the site's dark swatch.
   Theming rides the export's own switch — the <footer> carries u-theme-dark
   (text/links/borders all invert through the theme vars) plus
   data-theme-section="dark" so the nav observer treats it like any dark
   section; only the background layers are owned here. The export's two
   brand radial glows return on top of the dark base, retuned for it: the
   navy #123b624d glow is invisible over near-black, so it's lifted to a
   lighter blue at higher alpha; the cyan glow keeps its export alpha (it
   reads as light on black where it read as a tint on white). The aurora
   beam field (initFooterAurora: purple->blue beams, white square data
   particles, per-layer fades) sits above and reads brighter on the dark
   ground — unchanged. */
.footer_wrap.u-position-relative {
  background-color: var(--swatch--dark-900);
  /* round 7: the two bottom-anchored radial glows MOVED from here onto
     .enso-footer-aurora (below). Diagnosed cause of the CTA/footer seam:
     with circle farthest-side the radius resolved to the HORIZONTAL
     extent (0.95 x width ≈ 1368px at 1440w vs a 771px footer), so the
     navy gradient still carried ~0.06 alpha at the footer's top edge and
     clipped there against the CTA's flat dark-900 — a visible line. The
     glows now live on the aurora wrap, which spans CTA + footer.
     background-image must be EXPLICITLY none: the export's own
     .footer_wrap rule ships a light-theme glow stack that this rule had
     been overriding since round 4 — dropping the property revives it. */
  background-image: none;
  padding-top: 5rem;
}
.footer_contain { position: relative; z-index: 1; }
/* the slide-in hover line is brand-500 navy — near-invisible against the
   dark base, so the dark footer lifts it one step (brand-400 = brand-500
   +20% white, the export's own derived swatch) */
/* round 11: FULL brand blue — client-directed reversal of the round-3 lift
   to brand-400, and deliberately the SAME colour as the marker-reveal bars
   so hover block and reveal read as one idea. ROUND 12: both now resolve
   ONE token, --enso-marker-accent (:root = brand-500; the solutions
   namespace overrides it to the purple family because that page's ground
   IS brand-500 — see the solutions ground block). colorMap.brand in
   barba-boot resolves the same var, so the JS stays unbranched across the
   five byte-identical footers. */
.footer_wrap.u-theme-dark .footer_link_line { background-color: var(--enso-marker-accent); }
/* (round 6: the round-4 ::after top strip is GONE. It guaranteed a pure
   site-dark footer top edge when the section above could be light; the CTA
   section now sits above the footer on every footer page and is itself
   opaque dark-900, so the boundary is dark-on-dark by construction. More
   importantly the strip painted ABOVE the aurora canvas, and the canvas
   now rises through the CTA — the strip would have masked the particles
   across the footer's top 34% and re-revealed them above it: a visible
   band exactly where the seam used to be.) */
/* taller zone = longer fade runway; each layer carries its own vertical fade
   (ground mask, beam gradients, particle height-fade) so the darker bottom
   colours still dissolve fully into the white above */
.enso-footer-aurora { position: absolute; left: 0; right: 0; bottom: 0; height: 80%; overflow: hidden; pointer-events: none; z-index: 0;
  /* round 7: the footer's brand glows live HERE now, on the box that
     spans CTA + footer (JS sets the inline height), so the colour fade
     CONTINUES up through the CTA instead of clipping at the footer's top
     edge — the diagnosed seam. ellipse (not circle) farthest-side keys
     the vertical radius to the box height: straight above each anchor
     the gradient hits 100% exactly at the zone's top, and with the
     70%/45% stops the colour is fully dissolved well inside the zone —
     nothing left to clip at any edge. */
  background-image:
    radial-gradient(ellipse farthest-side at 5% 100%, rgba(56, 110, 176, 0.3), transparent 70%),
    radial-gradient(ellipse farthest-side at 80% 100%, #1fc0d94d, transparent 45%); }
/* the colour ground: a purple->blue band along the bottom — rich and dark-ish
   (never black), melting upward into the white */
.enso-footer-aurora_ground { position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--enso-purple), rgb(30, 86, 168)); /* round 13: left stop = the shared purple token (identical value) */
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.8) 32%, transparent 82%);
  mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.8) 32%, transparent 82%); }
.enso-footer-aurora_beam { position: absolute; bottom: -12%; filter: blur(22px);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 30%, #000 70%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 30%, #000 70%, transparent); }
.enso-footer-aurora_canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.enso-quote .text_1_wrap { --text_1_color-1: rgba(239, 238, 236, 0.28); --text_1_color-2: var(--swatch--light-100); }
/* ---- footer [ Info ] column (round 8: flag INSIDE the bracket) ----
   The flag is an inline image inside the heading's <p> — "[ Info CH ]" —
   baseline-tucked to the h6 type (round 7's beside-the-heading row and
   round 6's beside-the-clock row are both superseded). Tabular figures
   keep the clock's seconds from jiggling the line. */
.enso-footer-flag { display: inline-block; width: 0.8em; height: 0.8em; vertical-align: -0.05em; margin: 0 0.05em; }
/* plain block since round 8: the old flex+gap (a flag-row leftover from
   round 6) indented the clock 9px — an export global gives the element an
   empty ::before, which flex counted as an item and gap spaced. */
.enso-footer-locale { display: block; }
.enso-footer-clock { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* round 8 (A2): the export centres .footer_bottom_link_wrap (flex,
   justify-content:center), so the © line, Site Credit and the whole
   [ Legal ] column floated off the column's left edge while the clock and
   the .footer_link_wrap columns sat correctly at it. Scoped to the footer
   column groups; anywhere else the class appears keeps the export look. */
.footer_group_wrap .footer_bottom_link_wrap { justify-content: flex-start; }
/* round 8 (A3): at rest the hover line parks at translate(-100%) inside an
   overflow-hidden wrap — ix3 owns that transform (t-f09e9a26/t-8b690982),
   so the fix is geometric: overshoot the box by 1px each side so -100%
   clears completely at every subpixel rounding (the sliver showed on
   Terms & Conditions / Site Credit at some widths). */
.footer_link_line { width: calc(100% + 2px); left: -1px; }

/* ============================================================
   FOOTER CTA — "From Demonstration / To Deployment" (round 7)
   One dark block with the footer. Layout (round 7): full-bleed
   image LEFT — off the viewport edge, >=60vh, deliberately
   overrunning the info column top and bottom — with the eyebrow /
   heading / paragraph / tab list in the RIGHT column. The reveal
   is BACK (round-7 reversal of round 6, client-directed after
   seeing both): titles always visible, paragraph + secondary
   button only on the active item. The reveal is grid-template-rows
   0fr -> 1fr driven purely by .is-active in CSS — no height:auto
   tween (the jank source round 6 was guarding against stays
   solved), interruptible, instant under reduced motion. The
   layout-shift problem is handled two ways: the bleed image is
   taller than the info column in every state (it sets the row
   height), and the three bodies are equalised with a min-height on
   the copy so switching moves nothing.
   Progress track unchanged: each item's bottom hairline IS the
   autoplay track (white fill + 4px cap riding the leading edge).
   Numbers stay hairline SQUARES. JS: initFooterCta (unchanged).
   ============================================================ */
/* explicit dark-900 base (not just the theme background) so the block is
   guaranteed continuous with the footer's dark-900; the contain lifts to
   z:2 so copy and images sit ABOVE the footer's aurora canvas (footer is
   z:1 and the canvas extends up over this section — the particles pass
   BEHIND the content, the splash-scene layering) */
/* the container's left inset, recomputed from the export's own tokens
   (.u-container: width = 100% - 2*site-margin, capped at max-width,
   centred) — the exact distance an element must extend to bleed off
   the viewport edge. HOISTED to :root in round 16 (the round-10
   --enso-approach-bg move): the CTA visual and the technology-detail
   slider both consume it. */
:root { --enso-bleed: calc((100vw - min(100vw - 2 * var(--site--margin), var(--max-width--main))) / 2); }
.enso-cta { background-color: var(--swatch--dark-900);
  /* how far the image overruns the info column top and bottom */
  --enso-cta-overrun: clamp(1.5rem, 4vh, 3.5rem); }
.enso-cta .section_contain { position: relative; z-index: 2; }
.enso-cta_grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); column-gap: clamp(2.5rem, 5vw, 5.5rem); align-items: center; }
/* round 7: more air between the heading and the paragraph, and a clear
   step down to the list */
.enso-cta_intro { margin-top: clamp(1rem, 1.6vw, 1.6rem); margin-bottom: clamp(1.75rem, 3vw, 2.75rem); }
.enso-cta_list { display: flex; flex-direction: column; }
.enso-cta_item { position: relative; padding-top: 1.4rem; }
.enso-cta_item:first-child { border-top: 1px solid rgba(239, 238, 236, 0.2); }
/* the whole title row is the tab button — plain button chrome reset */
.enso-cta_tab { display: flex; align-items: center; gap: 1.1rem; width: 100%; padding: 0 0 1.4rem; margin: 0; background: none; border: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.enso-cta_tab:focus-visible { outline: 1px solid var(--swatch--light-100); outline-offset: 5px; }
/* number chip: a SQUARE with a hairline border (the .enso-badge / hint-
   bracket vocabulary — nothing round in this section); fills solid when
   its item is active */
/* round-12 amend (client): the bordered 1.9rem square became the house
   eyebrow FORM — "[ 01 ]" in the markup, no box. Active state is full-
   opacity ink instead of the filled chip. Shared with the Philosophy
   snake numbers (same amend, see the snake block). */
.enso-cta_nr { display: inline-flex; align-items: center; flex: none; color: rgba(239, 238, 236, 0.72); transition: color 0.4s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-cta_item.is-active .enso-cta_nr { color: var(--swatch--light-100); }
.enso-cta_title { margin: 0; opacity: 0.45; transition: opacity 0.4s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-cta_item.is-active .enso-cta_title { opacity: 1; }
/* ---- the reveal (round 7): rows-collapse, never height:auto ----
   visibility flips after the collapse so hidden buttons leave the tab
   order; reduced-motion switches instantly */
.enso-cta_reveal { display: grid; grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows 0.55s cubic-bezier(0.625, 0.05, 0, 1), visibility 0s 0.55s; }
.enso-cta_item.is-active .enso-cta_reveal { grid-template-rows: 1fr; visibility: visible; transition: grid-template-rows 0.55s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-cta_reveal > .enso-cta_body { overflow: hidden; min-height: 0; }
@media (prefers-reduced-motion: reduce) {
  .enso-cta_reveal, .enso-cta_item.is-active .enso-cta_reveal { transition: none; }
}
/* body indents to the title's left edge (number width + gap); the copy
   min-height equalises the three bodies so switching items cannot change
   the column height. NO vertical padding on the body: padding survives
   the 0fr collapse (only the flexible content shrinks), which left a
   ~29px sliver on closed items — the rhythm lives in the children's
   margins instead, which the body's BFC contains and the collapse clips. */
.enso-cta_body { padding: 0 0 0 calc(1.9rem + 1.1rem); }
.enso-cta_copy { max-width: 44ch; margin: 0.2rem 0 1rem; min-height: 4.5em; opacity: 0.85; }
.enso-cta_body .button_main_wrap { margin: 0.25rem 0 1.6rem; }
/* row divider = progress track: one element, two jobs. Track at the
   connector's low opacity; fill + cap written by initFooterCta. */
.enso-cta_line { position: relative; height: 1px; background: rgba(239, 238, 236, 0.2); }
.enso-cta_line-fill { position: absolute; inset: 0; background: var(--_theme---text); transform: scaleX(0); transform-origin: 0 50%; }
.enso-cta_line-cap { position: absolute; top: 50%; left: 0; width: 4px; height: 4px; background: var(--_theme---text); transform: translate(-50%, -50%); opacity: 0; }
/* ---- the visual (round 7): big, bleeding off the page ----
   Stretches to the row, floored at 60vh PLUS the overrun it wins back
   through its negative block margins — so it is always 2x overrun taller
   than the info column beside it. The negative left margin extends it to
   the viewport edge (auto width in a grid cell absorbs the margin);
   .page_wrap { overflow: clip } guarantees no horizontal scrollbar.
   Cropping is sanctioned — object-fit: cover on true 1:1 sources. */
.enso-cta_visual { position: relative; align-self: stretch; min-height: calc(60vh + 2 * var(--enso-cta-overrun)); margin-block: calc(-1 * var(--enso-cta-overrun)); margin-left: calc(-1 * var(--enso-bleed)); }
.enso-cta_panel { position: absolute; inset: 0; opacity: 0; visibility: hidden; }
.enso-cta_panel.is-active { opacity: 1; visibility: visible; } /* no-JS / first paint; GSAP owns it after init */
.enso-cta_panel img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media screen and (max-width: 991px) {
  .enso-cta_grid { grid-template-columns: 1fr; row-gap: 2.5rem; }
  /* narrow fallback: image first, full-bleed BOTH edges (the container
     inset equals the bleed below max-width), 48vh, no vertical overrun */
  .enso-cta_visual { align-self: auto; min-height: 48vh; margin-block: 0; margin-inline: calc(-1 * var(--enso-bleed)); }
}

/* ---- Core Technologies modal: full-height right drawer ---- */
.modal_1_content { will-change: clip-path; }

/* ---- SkyBridge "More" toggle: mini secondary button (no arrow) ---- */
.enso-more-btn {
  display: inline-block;
  margin-top: 0.5rem;
  padding: 0.375em 1.1em;
  font-family: inherit;
  font-size: var(--_typography---font-size--text-tiny);
  line-height: 1.4;
  cursor: pointer;
  color: var(--_theme---button-secondary--text);
  background: var(--_theme---button-secondary--background);
  border: 1px solid var(--_theme---button-secondary--border);
  border-radius: 0;
  transition: background-color 0.3s cubic-bezier(.4,0,.2,1), color 0.3s cubic-bezier(.4,0,.2,1), border-color 0.3s cubic-bezier(.4,0,.2,1), opacity 0.3s ease;
}
.enso-more-btn:hover {
  color: var(--_theme---button-secondary--text-hover);
  background: var(--_theme---button-secondary--background-hover);
  border-color: var(--_theme---button-secondary--border-hover);
}
.enso-more-para[hidden] { display: none !important; }

/* ============================================================
   CORE TECHNOLOGIES — Trust Platform recast (Aug 2026)
   Five layer-cards + drawer content additions. The cards use the
   export's dark variant, which sets color:white directly WITHOUT
   re-scoping the theme vars (--_theme---text stays dark inside the
   card) — so everything card-scoped below rides currentColor, not
   the theme tokens. The drawer panel and the section surface are
   both light, where the theme tokens DO resolve correctly.
   ============================================================ */

/* ---- HINT BRACKETS: "this opens a pop-up over the page" affordance ----
   Two plain ‹ › chevrons in a wrapper rotated -45°, so their horizontal
   translateX reads as a diagonal expand (the fullscreen-icon gesture).
   Animation is pure CSS, driven by the ancestor CARD ([data-modal-trigger])
   — the badge itself is decorative and aria-hidden. Cards never carry
   arrows (arrows = navigation); brackets never appear on links out.
   Client-directed treatment (matched to the reference mock in
   "Example Sections/"): a solid chip in the primary-button vocabulary
   (brand-blue fill, no border, --radius--small like .arrow_icon_bg),
   anchored in the CARD's bottom-right corner — a direct child of
   .card_modal_inner, inset by the inner's own padding value
   (--_spacing---space--4) so its right inset equals its bottom inset
   equals the card's content padding, per the mock. "Overview" stays in
   flow at bottom-left; its baseline is lifted to sit ~28% of the chip's
   height above the chip's bottom edge (see .card_modal_link_wrap below).
   The chevrons ride currentColor (the card's white text — the dark card
   variant never re-scopes the theme tokens). */
.hint-brackets {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25em;
  height: 2.25em;
  flex: none;
  position: absolute;
  right: var(--_spacing---space--4);
  bottom: var(--_spacing---space--4);
  background: var(--_theme---button-primary--background, #123b62);
  border: none;
  border-radius: var(--radius--small);
  color: color-mix(in srgb, currentColor 70%, transparent);
  transition: color 0.4s cubic-bezier(0.625, 0.05, 0, 1);
}

/* The -45° trick: horizontal motion becomes diagonal */
.hint-brackets_icon { display: flex; align-items: center; transform: rotate(-45deg); }

.hint-brackets_bracket {
  width: 0.9em;
  height: 0.9em;
  flex: none;
  transition: transform 0.4s cubic-bezier(0.625, 0.05, 0, 1);
}

/* Rest: barely open, so the affordance still reads without hovering */
.hint-brackets_bracket.is--a { transform: translateX(-0.05em); }
.hint-brackets_bracket.is--b { transform: translateX(0.05em); }

/* Hover/focus driven by the CARD, not the icon (keyboard = identical) */
[data-modal-trigger]:hover .hint-brackets,
[data-modal-trigger]:focus-within .hint-brackets {
  color: inherit;
}
[data-modal-trigger]:hover .hint-brackets_bracket.is--a,
[data-modal-trigger]:focus-within .hint-brackets_bracket.is--a { transform: translateX(-0.26em); }
[data-modal-trigger]:hover .hint-brackets_bracket.is--b,
[data-modal-trigger]:focus-within .hint-brackets_bracket.is--b { transform: translateX(0.26em); }

/* ---- drawer modal content additions ----
   The drawer now opens on the image; the technical classifier moved INTO
   the eyebrow (the separate .enso-modal_tag line under the H2 was cut). */
.modal_1_scroll .eyebrow_wrap { margin-bottom: 0.75rem; }
.modal_1_scroll .modal-image { margin-bottom: 0.75rem; }

/* ---- SkyBridge Validated badge: the section's ONLY badge ----
   Hairline-outline vocabulary — 1px border, tiny uppercase, no fill.
   The border IS the bracket treatment, so no literal [ ] inside. */
.enso-badge {
  display: inline-block;
  border: 1px solid currentColor;
  padding: 0.1em 0.5em 0.15em;
  font-size: var(--_typography---font-size--text-tiny);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  vertical-align: 0.08em;
}

/* ---- CTAs: modal ("Explore …"), section ("Discover Our Technologies")
   and the quote's "Discover SkyBridge" are all the export's OWN primary
   button component (button_main_wrap) now — client direction: CTAs must
   replicate the site's main buttons, not a custom outline vocabulary.
   Only placement is ours. Placeholder hrefs — the real URL waits in
   data-enso-future until technology.html exists (promote with one
   find-and-replace). */
.enso-quotebox .button_main_wrap { margin-top: 2.5rem; }

/* ---- section header row: heading left, "Discover Our Technologies" right.
   align-items:flex-end, NOT baseline (flex baseline uses the FIRST line —
   it would align the button to "Core"). The display heading's line box
   ends 4.7px below the second line's baseline at 1440w (tight 0.91
   line-height; measured with an inline-block probe), so the button gets a
   0.25rem (=4.75px at the 19px root) optical nudge up to land its bottom
   edge on the baseline of "Technologies". The row carries u-mb-text in the
   markup and zeroes the heading's own margin, so the heading-to-slider
   rhythm is unchanged. */
.enso-tech-header_row { display: flex; justify-content: space-between; align-items: flex-end; gap: 2rem; }
.enso-tech-header_row .c-heading.u-mb-text { margin-bottom: 0; }
.enso-tech-header_row .button_main_wrap { margin-bottom: 0.25rem; }
@media screen and (max-width: 991px) {
  /* header stacks: button falls below the heading, left-aligned, full size */
  .enso-tech-header_row { flex-direction: column; align-items: flex-start; gap: 1.5rem; }
  .enso-tech-header_row .button_main_wrap { margin-bottom: 0; }
}

/* ---- card text spacing to match the reference mock ----
   "Overview" reads bottom-aligned with the chip with breathing room under
   its baseline: target is 28% of the chip's height (0.28 x 2.25em =
   0.63em) above the chip's bottom edge. In flow its baseline lands
   ~0.08em above the card padding line, so the row is lifted by the
   difference — 0.55em, measured at 1440w (baseline probe: 1.5px -> 12px
   above the chip bottom on a 42.8px chip). em so it tracks the chip.
   The title clears the Overview cap by ~10% of the card width (mock:
   46px/461) — u-mb-text gave 25px baseline-to-cap; 2.65rem measures 46px
   baseline-to-cap at 1440w. */
.card_modal_link_wrap { margin-bottom: 0.55em; }
.card_modal_content .card_modal_title { margin-bottom: 2.65rem; }

/* ============================================================
   TECHNOLOGY PAGE (Aug 2026, reworked round 4) — technology.html
   Hero (About-hero structure, no video) with a sparse field of
   rising "data" sparks on its empty right side; the two-column
   platform section (sticky title left, five three-column cards
   right); a left-anchored, NON-sticky beam scene inside #platform
   that starts around the section's vertical centre; the homepage
   accordion as the closer. JS: initTechAurora in barba-boot.js —
   the round-3 travelling column is GONE (client verdict: read as
   a cheap animation; its static star field also drowned the few
   moving sparks in the hero).
   ============================================================ */

/* ---- aurora scenes: absolute inside their own (position:relative)
   sections — never fixed (initModalPageShift transforms the Barba
   container; a transformed ancestor turns fixed into absolute).
   Section content lifts to z:1 so each canvas sits between the section's
   opaque theme background and the copy. */
[data-barba-namespace="technology"] .u-section > .section_contain,
[data-barba-namespace="solutions"] .u-section > .section_contain { position: relative; z-index: 1; }
.enso-hero-sparks { position: absolute; inset: 0; overflow: hidden; z-index: 0; pointer-events: none; }
.enso-hero-sparks_canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.enso-platform-aurora { position: absolute; inset: 0; overflow: hidden; z-index: 0; pointer-events: none;
  /* bottom dissolve (round 5): the scene fades into the dark before the
     section boundary instead of clipping — same vocabulary as the footer
     aurora's ground mask. Fades beams AND particles; cards/copy live
     outside the host and are untouched. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 78%, transparent 98%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 78%, transparent 98%);
  /* tunables: beam column x (under the sticky title); where the beam
     region starts vertically (fraction of the section's height) */
  --enso-plat-x: 22%;
  --enso-plat-beam-top: 30%; }
.enso-plat-aurora_canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
/* the beam region occupies the section's lower half; the wrapper's opacity
   is the fade-in write target (scrubbed by initTechAurora as the region
   scrolls into view) and implicitly scales the ambient breathing */
.enso-plat-aurora_beams { position: absolute; top: var(--enso-plat-beam-top); bottom: -4%; left: 0; right: 0; opacity: 0; }
/* positioning shells vs painted fills: GSAP breathes the FILLS (scaleX /
   opacity) — a tween on the shell would bake its translateX(-50%) into px */
.enso-plat-aurora_pos { position: absolute; top: -4%; bottom: -4%; left: var(--enso-plat-x); transform: translateX(-50%); }
.enso-plat-aurora_pos.is--haze { top: -6%; bottom: -6%; width: min(60vw, 50rem); }
.enso-plat-aurora_pos.is--beam2 { width: clamp(17rem, 30vw, 28rem); }
.enso-plat-aurora_pos.is--beam { width: clamp(15rem, 26vw, 24rem); }
.enso-plat-aurora_pos.is--core { top: -2%; bottom: -2%; width: clamp(6rem, 9vw, 9.5rem); }
.enso-plat-aurora_fill { position: absolute; inset: 0; }
/* layer paints: the splash aurora's values (index.html inline block) */
.enso-plat-aurora_fill.is--haze { background: radial-gradient(46% 58% at 50% 50%, rgba(56,124,198,0.48) 0%, rgba(24,70,116,0.24) 55%, rgba(18,59,98,0) 80%); filter: blur(46px); }
.enso-plat-aurora_fill.is--beam2 { background: linear-gradient(to bottom, rgba(56,124,198,0) 0%, rgba(56,124,198,0.24) 18%, rgba(96,156,224,0.4) 42%, rgba(140,190,246,0.5) 50%, rgba(96,156,224,0.4) 62%, rgba(56,124,198,0.2) 84%, rgba(56,124,198,0) 100%); -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 30%, #000 70%, transparent 100%); mask-image: linear-gradient(to right, transparent 0%, #000 30%, #000 70%, transparent 100%); filter: blur(30px); mix-blend-mode: screen; opacity: 0.35; }
.enso-plat-aurora_fill.is--beam { background: linear-gradient(to bottom, rgba(124,92,204,0) 0%, rgba(124,92,204,0.22) 16%, rgba(168,124,224,0.44) 38%, rgba(226,178,232,0.62) 50%, rgba(168,124,224,0.44) 64%, rgba(124,92,204,0.2) 84%, rgba(124,92,204,0) 100%); -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 32%, #000 68%, transparent 100%); mask-image: linear-gradient(to right, transparent 0%, #000 32%, #000 68%, transparent 100%); filter: blur(22px); }
.enso-plat-aurora_fill.is--core { background: linear-gradient(to bottom, rgba(244,214,250,0) 2%, rgba(246,208,244,0.4) 22%, rgba(255,226,244,0.8) 48%, rgba(246,208,244,0.45) 72%, rgba(244,214,250,0) 98%); filter: blur(10px); mix-blend-mode: screen; -webkit-mask-image: linear-gradient(to right, transparent, #000 38%, #000 62%, transparent); mask-image: linear-gradient(to right, transparent, #000 38%, #000 62%, transparent); }
@media screen and (max-width: 767px) {
  /* no empty side columns on narrow viewports: centre the beam (the JS
     mirrors this for the canvas and dims both scenes x0.6) */
  .enso-platform-aurora { --enso-plat-x: 50%; }
}

/* ---- platform section: sticky title left, card stack right ---- */
/* items STRETCH (grid default): the left column must fill the row's full
   height or its sticky title has no travel range and scrolls straight out */
.enso-platform_grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); column-gap: clamp(3rem, 6vw, 6rem); }
/* +2.5rem clearance (round 4): the pinned eyebrow was getting caught
   under the nav's progressive blur */
.enso-platform_sticky { position: sticky; top: calc(clamp(5rem, 14vh, 9rem) + 2.5rem); }
/* the right column's first line aligns with the TITLE's first line, not
   the eyebrow: offset = the eyebrow's one text-main line + its u-mb-5
   margin, token-tracked. 0.95em not 1.5em: the title's taller line box
   carries ~11px more half-leading than the paragraph's at 1280w, so a
   full-line offset set the paragraph's cap visibly low (measured with
   Range rects: 1.5em -> +18px box delta; 0.95em lands the caps level) */
.enso-platform_right { padding-top: calc(var(--_spacing---space--5) + 0.95em); }
.enso-platform_intro { max-width: 44ch; margin-bottom: 3rem; }
.enso-platform_cards { display: flex; flex-direction: column; gap: 1.25rem; }
/* round 5: the stack was running into the section boundary; this is also
   the runway the beam's bottom dissolve happens over */
#platform { padding-bottom: clamp(4rem, 10vh, 8rem); }
/* card surface: a step lighter than the page, hairline-bound, square
   corners; round 4 adds a backdrop blur so the star field never fights
   the copy (checked over the beam — the brightest thing behind a card) */
.enso-platform_card { position: relative; display: flex; align-items: stretch; gap: clamp(1.25rem, 2vw, 2rem); padding: clamp(1.25rem, 2vw, 1.75rem); background: rgba(239, 238, 236, 0.05); border: 1px solid rgba(239, 238, 236, 0.14); color: inherit; text-decoration: none; -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); transition: background-color 0.4s cubic-bezier(0.625, 0.05, 0, 1), border-color 0.4s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-platform_card:hover, .enso-platform_card:focus-visible { background: rgba(239, 238, 236, 0.085); border-color: rgba(239, 238, 236, 0.32); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  /* no blur available: a more opaque surface keeps the copy clear of the
     particles behind it */
  .enso-platform_card { background: rgba(42, 40, 41, 0.9); }
  .enso-platform_card:hover, .enso-platform_card:focus-visible { background: rgba(48, 46, 47, 0.92); }
}
/* image column runs the card's FULL height (round 5 — no square crop):
   fixed width, natural portrait as the card grows, object-fit keeps it
   from squashing or letterboxing */
.enso-platform_card-media { position: relative; flex: none; width: clamp(6rem, 11vw, 10rem); align-self: stretch; overflow: hidden; }
.enso-platform_card-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.enso-platform_card-body { display: flex; flex-direction: column; align-items: flex-start; flex: 1; min-width: 0; }
.enso-platform_card-title { margin-bottom: 0.6em; }
.enso-platform_card-copy { max-width: 52ch; }
.enso-platform_card-copy p { margin-bottom: 0; }
/* arrows column (round 4): the button chrome is gone — the double
   .arrow_icon_group pair alone, vertically centred, as the card's third
   column. The slide is Webflow ix3's own numbers (i-b695db2a: x 0 ->
   100%, 0.25s) reimplemented in CSS and driven by CARD hover/focus —
   ix3 binds to .button_main_wrap's mouseenter, which no longer exists
   inside these cards, so nothing fights the transform. House curve;
   verify side-by-side with a real arrow button. */
.enso-platform_card-arrows { flex: none; align-self: flex-end; display: flex; align-items: flex-end; }
.enso-platform_card .arrow_icon_group { transition: transform 0.25s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-platform_card:hover .arrow_icon_group,
.enso-platform_card:focus-visible .arrow_icon_group { transform: translateX(100%); }
@media screen and (max-width: 991px) {
  .enso-platform_grid { grid-template-columns: 1fr; row-gap: 2.5rem; }
  .enso-platform_sticky { position: static; }
  .enso-platform_right { padding-top: 0; }
}
@media screen and (max-width: 479px) {
  .enso-platform_card { flex-direction: column; }
  .enso-platform_card-media { width: 100%; aspect-ratio: 16 / 9; }
  .enso-platform_card-arrows { align-self: flex-end; }
}

/* ============================================================
   ABOUT PAGE (round 5) — hero rebuild, section two, the
   EnsoLab Approach section (the homepage's PQC/patents section,
   repurposed — original archived in "Archive - PQC Research
   Section/"). JS: the canvas sketches live in the shared
   'canvas' ENSO block (squares + visibility gating, round 5).
   ============================================================ */

/* ---- hero: centred both ways (was bottom-left) ---- */
.enso-hero-centre .section_contain { text-align: center; }
.enso-hero-centre .eyebrow_wrap, .enso-hero-centre .eyebrow_layout { justify-content: center; }
.enso-hero-centre .c-heading, .enso-hero-centre .c-paragraph { margin-left: auto; margin-right: auto; }
.enso-hero-centre .u-button-group { justify-content: center; }

/* ---- section two: centred, at the homepage section-two size (the two
   text_1 paragraphs carry u-text-style-h3 in the markup) ---- */
.enso-about2 { text-align: center; }
.enso-about2 .eyebrow_wrap, .enso-about2 .eyebrow_layout { justify-content: center; }
.enso-about2 .text_1_wrap { max-width: 34ch; margin-left: auto; margin-right: auto; }
.enso-about2 .text_1_wrap + .text_1_wrap { margin-top: 1.5rem; }

/* ---- the EnsoLab Approach ----
   Four stage rows — on the LIGHT ground since round 6 (the section flipped
   from dark at the client's request; the sketches read their ink from the
   section's computed color, see the canvas block in barba-boot). The old
   section's three sins are structural here: the animation is a real grid
   column OF ITS ROW (no absolute positioning, no margin-left:22%), nothing
   is sticky, and the size is fixed (--enso-approach-anim — THE knob). The
   sketches' square particles sit directly on the section ground, same
   language as the aurora fields. Each row's hairline carries a 4px square
   cap at both ends (the site's line-intersection dot vocabulary); rows
   read as a 01->04 progression with the number column at low opacity.
   Hairlines are color-mix'd from the theme text token so they follow the
   section theme like the nav hairlines do (18% ink on white reads at the
   weight 14% white read on dark). */
.enso-approach { --enso-approach-anim: clamp(5.5rem, 8vw, 7rem); --enso-approach-line: color-mix(in srgb, var(--_theme---text) 18%, transparent); }
.enso-approach_head, .enso-approach_row { display: grid; grid-template-columns: 3.5rem minmax(0, 5fr) minmax(0, 4fr) var(--enso-approach-anim); column-gap: clamp(1.5rem, 3vw, 3.5rem); }
.enso-approach_head { padding-bottom: 0.75rem; }
.enso-approach_label { text-transform: uppercase; opacity: 0.5; font-size: var(--_typography---font-size--text-tiny); letter-spacing: 0.06em; }
.enso-approach_label-anim { text-align: right; }
.enso-approach_row { position: relative; align-items: center; padding-top: 1.5rem; padding-bottom: 1.5rem; border-top: 1px solid var(--enso-approach-line); }
.enso-approach_row:last-child { border-bottom: 1px solid var(--enso-approach-line); }
/* round 6: stage names step down from the display type (the patent-years
   scale) to row headings — the section heading stays the loudest thing */
.enso-approach_row .enso-approach_stage { font-size: var(--_typography---font-size--h4); }
/* round 6: more runway before the next section */
#approach { padding-bottom: clamp(3rem, 8vh, 6rem); }
/* 4px square caps on the hairline ends — the intersection-dot vocabulary */
.enso-approach_row::before, .enso-approach_row::after { content: ""; position: absolute; top: 0; width: 4px; height: 4px; background: var(--_theme---text); transform: translateY(-50%); }
.enso-approach_row::before { left: 0; }
.enso-approach_row::after { right: 0; }
.enso-approach_num { opacity: 0.45; }
.enso-approach_stage p, .enso-approach_stage h3 { margin: 0; }
.enso-approach_detail { max-width: 44ch; }
.enso-approach_detail p { margin-bottom: 0; }
.enso-approach_anim { position: relative; width: var(--enso-approach-anim); aspect-ratio: 1 / 1; justify-self: end; }
.enso-approach_anim .circle-container { position: absolute; inset: 0; width: 100%; height: 100%; background-color: transparent; }
/* the sketches read their ink from computed color (round 6; per-frame
   since round 7). Contact's sketch host (.contact_bg_wrap) sits OUTSIDE
   the dark section in the DOM — it would inherit the page's light-theme
   ink while painting over the section's dark ground. Pin it to light
   explicitly. */
.contact_bg_wrap .circle-container { color: var(--swatch--light-100); }

/* ---- round 7: Approach header CENTRED (reversal of round 6's
   left-alignment — client call after seeing both; the rows below are
   untouched) ---- */
#approach .section_contain > .eyebrow_wrap { justify-content: center; }
#approach .section_contain > .eyebrow_wrap .eyebrow_layout { justify-content: center; }
#approach .section_contain > .c-heading,
#approach .section_contain > .c-paragraph { text-align: center; margin-left: auto; margin-right: auto; }

/* ---- round 7: directional hover on the Approach rows ----
   A brand-navy tile slides in from the edge the cursor entered and leaves
   toward the edge it exits (initDirectionalHover in barba-boot). The tile
   lives inside its own overflow-hidden shell (.enso-dir-clip) rather than
   clipping the row itself — the row's 4px corner caps overhang its top
   edge by design and row-level overflow:hidden would halve them. Row
   content lifts to z:1 so the tile passes underneath; the caps
   (::before/::after) get an explicit z so tree order cannot drop the left
   cap below the tile. While hovered the row INVERTS on the tile's own
   0.5s curve: text, number, hairline caps — and the sketches follow by
   themselves, because they re-read their ink from the row's transitioning
   computed colour every frame. */
.enso-dir-clip { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
/* NO CSS transform on the tile: GSAP would parse translateY(101%) into a
   baked px base under its yPercent tweens (the pos-shell trap from the
   aurora scenes) and the tile would never reach 0. The off-state lives in
   GSAP (yPercent 101, set at bind); visibility:hidden is only the no-JS
   default, flipped once when initDirectionalHover binds. */
/* round-12 amend: WHITE tile — the old brand-navy tile is invisible now
   that the Approach ground is itself the brand blue; the row inversion
   below flips to blue ink on the white (the exact inverse of the round-7
   navy-tile/white-ink pairing; the sketches follow via computed color) */
.enso-dir-tile { position: absolute; inset: 0; background: var(--swatch--light-100); visibility: hidden; }
.enso-approach_row > :not(.enso-dir-clip) { position: relative; z-index: 1; }
.enso-approach_row::before, .enso-approach_row::after { z-index: 1; }
.enso-approach_row,
.enso-approach_row .c-heading,
.enso-approach_row .c-paragraph { transition: color 0.5s cubic-bezier(0.33, 0, 0.2, 1); }
.enso-approach_row::before, .enso-approach_row::after { transition: background-color 0.5s cubic-bezier(0.33, 0, 0.2, 1); }
.enso-approach_row.is-dir-hover,
.enso-approach_row.is-dir-hover .c-heading,
.enso-approach_row.is-dir-hover .c-paragraph { color: var(--enso-approach-bg); }
.enso-approach_row.is-dir-hover::before,
.enso-approach_row.is-dir-hover::after { background: var(--enso-approach-bg); }
/* ============================================================
   ROUND 7 — accordion polish ([ Quantum Security Risks ] et al)
   Closed rows recede (opacity, not colour — works on the dark and
   light accordion grounds alike) on the timeline's own 0.3s
   power1.inOut-equivalent curve; a hover half-step keeps the rows
   reading as interactive. The icon is the round-7 morph polyline
   (down -> flat -> up, tweened in the accordion timeline) and steps
   down from the export's 1.4rem — it was oversized against the
   row's h5 type.
   ============================================================ */
.accordion_wrap .accordion_component { transition: opacity 0.3s cubic-bezier(0.45, 0, 0.55, 1); }
.accordion_wrap .accordion_component:not(.is-opened) { opacity: 0.5; }
.accordion_wrap .accordion_component:not(.is-opened):hover,
.accordion_wrap .accordion_component:not(.is-opened):focus-within { opacity: 0.8; }
.accordion_toggle_icon { width: 1.1rem; }

@media screen and (max-width: 767px) {
  .enso-approach_head { display: none; }
  .enso-approach_row { grid-template-columns: 2.5rem minmax(0, 1fr) var(--enso-approach-anim); row-gap: 0.75rem; }
  .enso-approach_detail { grid-column: 2 / 3; grid-row: 2; }
  .enso-approach_anim { grid-row: 1 / 3; grid-column: 3; align-self: center; }
}

/* ============================================================
   ROUND 8 — the pinned-phases eyebrow (index), the About page's
   finished structure (Mission/Vision quote boxes, off-white
   Approach overlap, Philosophy snake), and the shared whiteout.
   ============================================================ */

/* ---- A7: [ SkyBridge Continued ] eyebrow above the phases H3 ---- */
.enso-phases_title .eyebrow_wrap, .enso-phases_title .eyebrow_layout { justify-content: center; }

/* ---- shared whiteout (the index .enso-quote_white pattern, promoted
   to attributes; initWhiteouts). z:4 clears section-two's z:3 content
   wrapper on about; inside the sticky visual it sits above the videos
   and the p5 grid (the text contain is a later sibling and stays on
   top). ---- */
.enso-whiteout { position: absolute; inset: 0; background: var(--swatch--light-100); opacity: 0; pointer-events: none; z-index: 4; }

/* ---- B1: the Approach section — off-white, sliding OVER section two ----
   --enso-approach-bg is THE background knob. The section rides up 18vh
   (into section two's page-top closing spacer) at z:3 — one step above
   the #about z:2 that both pages' section-twos carry — while the
   whiteout inside section two ramps in underneath it (markup +
   initWhiteouts). */
/* round 9: section two (about only — the id is shared with index) gets
   real bottom padding; the Approach overlap is tuned against it. Section
   two also RECEDES now (parallax attrs in the markup — the hero-fold
   family) while the whiteout ramps underneath. */
/* round 13 (§4): padding raised TOP + BOTTOM (client). The bottom value
   and #approach's margin-top are a COUPLED PAIR: the Approach eats the
   bottom padding as it climbs. Round 13: bottom 10vh -> 14vh mid-band,
   margin -18vh -> -20vh — half the added padding becomes visible air
   under the text (+2vh), half keeps the fold's bite on the taller tail. */
[data-barba-namespace="about"] #about { padding-top: clamp(3.5rem, 9vh, 7rem); padding-bottom: clamp(5.5rem, 14vh, 11rem); }
/* round-12 amend (client: "I'm enjoying the blue… want to see more of it"):
   the Approach ground is the site BLUE now — the same brand-500 the
   solutions page and Philosophy own. Still ONE knob shared with
   #mission-vision (round 10), so the video-grow's side reveal stays
   seamless — the video section is dark-themed, the coupling is safe.
   The section itself flipped dark in about.html (u-theme-dark +
   data-theme-section="dark"): ink, hairlines, caps and the canvas
   sketches (computed-color-per-frame) all invert through the tokens. */
:root { --enso-approach-bg: var(--swatch--brand-500); }
#approach { background-color: var(--enso-approach-bg); position: relative; z-index: 3; margin-top: -20vh; /* round 13: re-tuned with section-two's raised bottom padding — see the coupled-pair note above */ padding-top: clamp(3.5rem, 9vh, 7rem); }
/* rows: the content insets so the directional tile keeps a margin of
   colour around the text — the hairlines and their 4px caps stay
   full-width (border/caps span the border box; padding only moves the
   grid content). The header row insets identically so the columns keep
   lining up. */
.enso-approach_head, .enso-approach_row { padding-inline: clamp(1rem, 2vw, 1.75rem); }

/* ---- B2: Mission / Vision (the renamed #mission-vision section) ---- */
/* bound the new parallax slide exactly as the homepage does (clip, not
   hidden — hidden would break the sticky visual) */
#mission-vision { overflow: clip;
  /* round 10 (A3): the grow's 3% side inset reveals the SECTION's ground —
     it was white against the off-white Approach above; same variable now,
     so one knob moves both and the reveal reads as continuous colour */
  background-color: var(--enso-approach-bg); }
/* the p5 grid competes with the boxes — THE dim knob */
#mission-vision .p5-grid-container { opacity: 0.35; }
.enso-mv_layout { display: flex; align-items: center; justify-content: center; }
/* the quote-box shell reused; this variant is narrower, centred, and
   carries a blurred surface so the video never fights the copy (the
   Technology-card vocabulary; opaque fallback below) */
.enso-qbox { width: min(56rem, 92vw); text-align: center; padding: clamp(2.25rem, 4.5vw, 3.25rem) clamp(1.5rem, 3.5vw, 2.5rem); }
/* round 9 (4.3): the blurred surface is a DEDICATED LAYER, hidden until
   the box's draw timeline fades it in at the corner-dot beat (a static
   backdrop-filter arrived before the box existed). The @supports fallback
   lives on the same layer so it fades identically. Content lifts above it. */
.enso-qbox_bg { position: absolute; inset: 0; background: rgba(239, 238, 236, 0.06); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); opacity: 0; visibility: hidden; pointer-events: none; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .enso-qbox_bg { background: rgba(42, 40, 41, 0.9); }
}
.enso-qbox_content { position: relative; z-index: 1; }
.enso-qbox .eyebrow_wrap, .enso-qbox .eyebrow_layout { justify-content: center; }
/* round 9 (4.2): title one step up (h3 in markup) with real air under it,
   and the More-revealed text gets a genuine paragraph gap — 0.9rem at the
   same size read as a continuation, which was the complaint */
.enso-qbox_title { margin: 0 auto clamp(1.5rem, 3.2vh, 2.25rem); max-width: 24ch; }
.enso-qbox_para { max-width: 52ch; margin: 0 auto; }
.enso-qbox_para + .enso-qbox_para { margin-top: 1.5em; }
.enso-qbox .enso-more-btn { margin-top: 1.2rem; }
/* the connector between the two boxes — the site's generic scrubbed line,
   round 9: OUT OF FLOW and measured (positionMvConnector pins its top to
   the Mission box's bottom edge and its height to the gap to the Vision
   box, re-measured on every refreshInit — incl. the refresh the More
   handler fires when an expanded box changes the gap) */
#mission-vision .sticky-text_contain { position: relative; }
.enso-connector.is-mv { position: absolute; left: 0; right: 0; top: 0; height: 40vh; }
/* the 100vh dwell the sticky video holds while #philosophy climbs over it */
.enso-mv_dwell { height: 100vh; }

/* ---- B3: Philosophy — centred header + the snake ----
   Round 9: SOLID DARK BLUE (--enso-phil-bg at :root — THE knob; the video
   whiteout hands off in the same colour via data-whiteout-color), theme
   flipped to dark in the markup (u-theme-dark + data-theme-section="dark"
   — snake stroke, hairlines and number chips all ride the theme tokens),
   and the section CLIMBS the held video scene: margin-top -100vh over the
   dwell at #mission-vision's tail, z:2, opaque — the Core-Technologies-
   over-the-pinned-quote pattern from index. */
:root { --enso-phil-bg: var(--swatch--brand-500, #123b62); }
/* round 12: the footer accent — the highlight-marker reveal bar AND the
   link hover block resolve this one token (Josh's round-11 rule: the two
   are the same colour). brand-500 at :root (technology's charcoal footer
   keeps it); every BLUE footer overrides it to the purple accent — see
   the round-13 blue-tail block. */
:root { --enso-marker-accent: var(--swatch--brand-500); }

/* round 13 (§5): index's [ Quantum Security Risks ] section OVERLAPS Core
   Technologies as it arrives — the #approach pattern (relative + z above
   the section below + negative top margin + padding-top compensation so
   the accordion content clears the Swiper cards). z:3 steps over
   #technologies' z:2 (which itself climbs the pinned quote); the Core
   Technologies drawer is a <dialog> in the TOP LAYER, so it stays above
   any z-index. Index only — the Risks sections on the other pages are
   untouched. ROUND 13.1: the read comes from the PAIR — this margin puts
   the resting edge ~137px below the card row, and #technologies' Osmo
   recede (0->16 yPercent on its contain, in the index markup) sinks the
   cards ~147px through the handover, so the blue edge visibly catches
   and covers their bottom edge as it arrives. Margin deepened -11vh ->
   -14vh with the recede ("slightly", vs the Approach's -20vh). */
[data-barba-namespace="home"] .page_main > .u-section:has(.accordion_wrap) {
  position: relative; z-index: 3; margin-top: -14vh; padding-top: clamp(3.5rem, 9vh, 7rem);
}
/* round 13.2 (client): "a good amount more padding under core
   technologies… I want the animation, but a little further down." The
   recede window keys off the SECTION'S BOTTOM, so this padding moves the
   whole handover down with it — the sink depth and the -14vh overlap are
   untouched. THE knob for where the fold happens. */
[data-barba-namespace="home"] #technologies { padding-bottom: clamp(6rem, 16vh, 12rem); }

/* ============ round 13: the blue tail — THE SITE DEFAULT ============
   Josh: the blue accordion + CTA + footer tail "becomes part of the main
   website". BLUE IS THE DEFAULT for every page with a footer; charcoal is
   the EXPLICIT exception (BLUE_TAIL_EXCLUDE in barba-boot's initBlueTail:
   technology today, SkyBridge when it exists — "SkyBridge is in space").
   ONE list drives everything: initBlueTail marks the tail elements with
   the runtime class .enso-blue (the CTA and footer are byte-identical
   across five pages — the class must NEVER be typed into their markup);
   these rules key every colour to that class; the nav observer (inline
   ×7) reads the same class for the data-nav-accent flip. Solutions is
   blue end to end via its namespace block below (no first-paint flash on
   its above-fold blue) and gets .enso-blue marks only for the accent. */
:root {
  --enso-blue-ground: var(--swatch--brand-500);
  /* the purple family, §3: --enso-purple is the footer aurora ground's
     own base violet (unchanged value, promoted to a token);
     --enso-purple-accent lifts it 18% toward white — the marker bar /
     hover-block accent wherever the footer is blue. More chroma at the
     same lightness = "more vibrant, less faded" (measurements in
     HANDOVER round 13). */
  --enso-purple: rgb(84, 48, 148);
  --enso-purple-accent: color-mix(in srgb, var(--enso-purple) 82%, #ffffff);
}
/* the theme machinery: same var flip as the solutions block, keyed to the
   runtime class. The accent overrides are defensive — nothing consumes
   heading-accent / text-link--border-hover on any current page (re-audited
   round 13: zero .u-heading-accent / .button_link_wrap consumers), but
   future tail content must not inherit an invisible brand-500. */
.u-section.enso-blue,
.u-theme-dark.enso-blue {
  --_theme---background: var(--enso-blue-ground);
  --_theme---heading-accent: var(--swatch--light-100);
  --_theme---text-link--border-hover: var(--enso-purple-accent);
}
/* explicit charcoals the var does not reach */
.enso-cta.enso-blue { background-color: var(--enso-blue-ground); }
.footer_wrap.u-position-relative.enso-blue { background-color: var(--enso-blue-ground); }
/* a brand-500 bar / hover block inside a BLUE footer is the round-12
   collision — the accent follows the tail (derived from the same class,
   never a second list). Technology's charcoal footer keeps :root's
   brand-500. */
.footer_wrap.enso-blue { --enso-marker-accent: var(--enso-purple-accent); }

/* round 13 §2: the nav accent over blue sections. ADDITIVE attribute —
   data-theme-section stays two-valued ("blue" as a third value would drop
   through every dark/light rule: the hairlines at the top of this file,
   the ink rules in the pages' inline head styles). The inline observer
   (initCheckSectionThemeScroll, one copy in each of the seven heads) sets
   AND CLEARS data-nav-accent from the .enso-blue class of the section
   under the nav's midpoint; reinitAll's synthetic scroll event re-checks
   after every Barba swap. Black = the site's dark-900, never #000; text
   and hover tokens are set EXPLICITLY (the :root white text is luck, not
   design). Replaces round 12's solutions-only body:has() purple rule —
   solutions falls out of this naturally (blue from the top, so the button
   is black for the whole page). */
.nav_component[data-nav-accent="blue"] .button_main_wrap {
  --_theme---button-primary--background: var(--swatch--dark-900);
  --_theme---button-primary--text: var(--swatch--light-100);
  --_theme---button-primary--text-hover: var(--swatch--light-100);
  --_theme---button-primary--background-hover: var(--swatch--dark-800);
}
.enso-philosophy { background-color: var(--enso-phil-bg); position: relative; z-index: 2; margin-top: -100vh; padding-top: clamp(3rem, 8vh, 6rem); padding-bottom: clamp(3rem, 8vh, 6rem); }
/* (the round-9 is-current numeral-in-phil-bg rule is GONE with the filled
   chip — round-12 amend: the number is the bracket form, ink only) */
.enso-philosophy .section_contain > .eyebrow_wrap,
.enso-philosophy .section_contain > .eyebrow_wrap .eyebrow_layout { justify-content: center; }
.enso-philosophy .section_contain > .c-heading,
.enso-philosophy .section_contain > .c-paragraph { text-align: center; margin-left: auto; margin-right: auto; }
/* the snake column: --enso-snake-gap = vertical rhythm between steps
   (also the room the horizontal runs live in); --enso-snake-inset = the
   text's clearance from its vertical line. The two knobs. */
.enso-snake { position: relative; width: min(100%, 62rem); margin: 0 auto; padding-block: 0.25rem; --enso-snake-gap: clamp(4.5rem, 10vh, 8rem); --enso-snake-inset: clamp(2.25rem, 4.5vw, 3.5rem); }
.enso-snake_svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.enso-snake_track { fill: none; stroke: color-mix(in srgb, var(--_theme---text) 20%, transparent); stroke-width: 1; }
.enso-snake_fill { fill: none; stroke: var(--_theme---text); stroke-width: 1; }
.enso-snake_dot { fill: var(--_theme---text); opacity: 0; transition: opacity 0.3s ease; }
.enso-snake_dot.is-on { opacity: 1; }
/* items alternate sides; right-side items butt their text against the
   right-hand vertical (mirrored alignment). Three states, opacity steps. */
.enso-snake_item { position: relative; width: min(46ch, 78%); text-align: left; opacity: 0.35; transition: opacity 0.4s ease; }
.enso-snake_item + .enso-snake_item { margin-top: var(--enso-snake-gap); }
.enso-snake_item.is-left { margin-right: auto; padding-left: var(--enso-snake-inset); }
.enso-snake_item.is-right { margin-left: auto; padding-right: var(--enso-snake-inset); text-align: right; }
.enso-snake_item.is-active { opacity: 0.65; }
.enso-snake_item.is-current { opacity: 1; }
/* numbers: the CTA's .enso-cta_nr — since the round-12 amend the eyebrow
   FORM ("[ 01 ]", no box), theme-inked; is-current = full theme ink */
.enso-snake_item .enso-cta_nr { display: inline-flex; margin-bottom: 0.75rem; color: color-mix(in srgb, var(--_theme---text) 75%, transparent); }
.enso-snake_item.is-current .enso-cta_nr { color: var(--_theme---text); }
.enso-snake_title { margin: 0 0 0.5rem; }
.enso-snake_copy { margin: 0; }
.enso-snake_item.is-right .enso-snake_copy, .enso-snake_item.is-right .enso-snake_title { margin-left: auto; }
@media screen and (max-width: 767px) {
  /* the snake collapses to a plain left vertical (JS builds the straight
     path below 768); every item left-aligns against it */
  .enso-snake_item, .enso-snake_item.is-right { width: 100%; margin-left: 0; margin-right: 0; padding: 0 0 0 var(--enso-snake-inset); text-align: left; }
}


/* ============================================================
   ROUND 10 — SOLUTIONS PAGE (solutions.html, the 7th source page)
   Hero: the aurora's wide quiet particle field (initTechAurora,
   per-host attrs) + the standard hero darkening. Intro band with
   proof chips. Six sticky stack cards (initSolutionsStack) indexed
   by the fixed left rail (initSolutionsRail). Accordion + CTA +
   footer are the standard closers.
   ============================================================ */

/* ---- round 12: the BLUE page ground (solutions ONLY, hard-scoped) ---- */
/* Josh: the whole page — hero to footer, CTA and the fade into the footer
   included — moves off the site charcoal onto the site's own blue,
   brand-500 (the colour About's Philosophy section already owns via
   --enso-phil-bg). Scoped on the Barba namespace, which sits on .page_wrap
   and contains every section, the CTA and the footer — so the five
   byte-identical footers render charcoal on four pages and blue here
   WITHOUT forking the footer markup. --enso-blue-ground is THE token: every
   surface below derives from it; never hard-code the blue. */
[data-barba-namespace="solutions"] {
  /* --enso-blue-ground is defined at :root since round 13 (the blue tail
     is the site standard); this page consumes it page-wide */
  /* the footer accent on the blue ground is the PURPLE family (round 12;
     round 13 made it the shared --enso-purple-accent — more chroma at the
     same lightness, the "more vibrant" ask — and every blue-tail footer
     now shares it via the blue-tail block above) */
  --enso-marker-accent: var(--enso-purple-accent);
  /* §2's card outline + §9's intro edge share this knob; take it to
     rgba(239,238,236,1) for full white */
  --enso-stack-outline: rgba(239, 238, 236, 0.35);
}
/* the theme machinery: the export sets --_theme---background to dark-900 on
   the section variant and on .u-theme-dark (the footer). Same elements,
   higher specificity, later file — the var flip reaches every .u-section
   background on the page plus the footer. */
[data-barba-namespace="solutions"] .u-section:where(.w-variant-857e5430-97c7-deb6-3c1a-d3063f9fe2c7),
[data-barba-namespace="solutions"] .u-theme-dark {
  --_theme---background: var(--enso-blue-ground);
  /* brand-500 accents the theme also carries would vanish on the blue —
     nothing on this page consumes them TODAY (no .u-heading-accent, no
     .button_link_wrap — audited round 12), but content added later must
     not inherit an invisible accent */
  --_theme---heading-accent: var(--swatch--light-100);
  --_theme---text-link--border-hover: var(--enso-purple-accent);
}
/* explicit charcoals the var does not reach (each was dark-900 or a
   literal): the hero fold must darken TOWARD THE BLUE, and the CTA +
   footer grounds carry the blue through to the page's end */
[data-barba-namespace="solutions"] .enso-hero-dark { background: var(--enso-blue-ground); }
[data-barba-namespace="solutions"] .enso-cta { background-color: var(--enso-blue-ground); }
[data-barba-namespace="solutions"] .footer_wrap.u-position-relative { background-color: var(--enso-blue-ground); }
/* (the round-12 solutions-only purple nav-button rule is GONE — the
   round-13 data-nav-accent mechanism covers it: solutions is blue from
   the top, so its button is black for the whole page) */

/* ---- hero (round 11) ---- */
/* the p5 dots-and-lines field as the hero ground (replaced the round-10
   aurora sparks — too close to the technology hero). Negative z under the
   [data-enso-hero] isolation: above the section background, below the copy
   and the initHeroFold darkening. Opacity is THE dim knob — round 12 took
   it 0.5 -> 0.42 (one step toward About's 0.35) as the second lever under
   the §6 readability veil. */
.enso-sol-hero_field { position: absolute; inset: 0; z-index: -1; opacity: 0.42; overflow: hidden; pointer-events: none; }
/* round 12: the readability veil — ONE centre-weighted radial (not four
   stacked linears, which composite additively and seam on the diagonals):
   page-ground at the centre where the copy sits, dissolving to nothing at
   the edges so the field keeps its strength there. Sits ABOVE the field,
   BELOW the copy: same z:-1, LATER SIBLING in the DOM — tree order is the
   lift, do not add a higher z (that would put it over the heading).
   Knobs: the element opacity (veil strength) and the two stops. */
.enso-sol-hero_veil { position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0.8;
  background: radial-gradient(ellipse at center, var(--enso-blue-ground) 0%, var(--enso-blue-ground) 28%, transparent 72%); }

/* ---- intro band ---- */
.enso-sol-intro_h { margin-left: auto; margin-right: auto; text-align: center; }
.enso-sol-proof { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; }
/* round 12 (§9): the intro paints OVER the receding hero (the index
   #about z:2 pattern) and carries a DEFINED top edge — hairline + 4px
   corner squares, the stack cards' own vocabulary — because blue sliding
   over blue has no boundary of its own; without the drawn edge the fold
   mechanism runs invisibly. Shares §2's outline knob. */
.enso-sol-intro { z-index: 2; border-top: 1px solid var(--enso-stack-outline, rgba(239, 238, 236, 0.35)); }
.enso-sol-intro::before, .enso-sol-intro::after { content: ""; position: absolute; top: 0; width: 4px; height: 4px; background: var(--_theme---text); transform: translateY(-50%); z-index: 2; }
.enso-sol-intro::before { left: 0; }
.enso-sol-intro::after { right: 0; }

/* ---- the card stack ---- */
/* deal rhythm: every card pins at the same top; the next one scrolls up
   over it. --enso-stack-top is the pin line; the inter-card margin is the
   scroll runway each handover gets. */
.enso-stack { --enso-stack-top: 9vh;
  /* round 12 (§5): re-widened — the rebuilt rail is a dotted line + hit
     area (~80px from the left edge; its flyout OVERLAYS and reserves
     nothing), so the round-11 28rem flank shrank to 15rem. Measure notes
     in HANDOVER (round 12). */
  max-width: min(68rem, calc(100vw - 15rem)); margin-inline: auto; }
.enso-stack_card { position: sticky; top: var(--enso-stack-top); display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); min-height: 72vh; margin-bottom: 10vh;
  /* opaque — cards cover one another; a translucent panel would ghost the
     card beneath through it. Round 12: derived from the page ground token
     (blue on solutions; the dark-900 fallback keeps the recipe portable) */
  background: color-mix(in srgb, var(--enso-blue-ground, var(--swatch--dark-900)) 93%, #ffffff);
  /* round 12 (§2): the FULL outline — all four sides, --enso-stack-outline
     is THE knob (0.35; 1.0 = the literal white Josh described — built at
     0.35 because a solid #fff box reads as wireframe chrome against the
     corner squares). Corner squares are four .enso-quotebox_dot spans in
     the markup now (the chipbox treatment); the old ::before/::after
     top-corner pseudos are GONE — they would double up. */
  border: 1px solid var(--enso-stack-outline, rgba(239, 238, 236, 0.35));
  will-change: transform, filter; }
.enso-stack_card:last-child { margin-bottom: 0; }
/* the card's own corner squares need a lift over the media column */
.enso-stack_card > .enso-quotebox_dot { z-index: 2; }
.enso-stack_body { display: flex; flex-direction: column; align-items: flex-start; padding: clamp(1.75rem, 3.5vw, 3rem); min-width: 0; }
/* round 12 (§3): the number is the house EYEBROW now — [ 01 ] in the real
   eyebrow markup, scrambling in on the card's arrival (driven from
   initSolutionsStack's arrival trigger, NOT data-scramble="scroll" — a
   sticky card's layout position would mistime the global binding). The
   bordered .enso-cta_nr square is gone from the cards (the CTA section
   still uses it). */
.enso-stack_meta { display: flex; align-items: center; gap: 0.9rem; margin-bottom: clamp(1.25rem, 2.6vh, 2rem); }
.enso-stack_meta .eyebrow_wrap { margin-bottom: 0; }
/* round 12 (§8.2): the tagline is gone (merged into the copy), so this
   margin is the whole title/paragraph gap — doubled from 0.45em. THE knob. */
.enso-stack_title { margin: 0 0 0.9em; max-width: 16ch; }
/* round 12 (§8.3): the dim moved OFF opacity onto color — playLineReveal
   ends with autoAlpha:1, which would have blown an opacity dim to full
   strength the moment the reveal completed */
.enso-stack_copy { margin: 0 0 1.4em; color: color-mix(in srgb, var(--_theme---text) 78%, transparent); max-width: 52ch; }
/* round 11: with the chips gone into the image, "Built on" takes the
   panel's foot (margin-top: auto). Round 12 (§4): the label is its OWN
   LINE above the links. */
.enso-stack_builton { margin: 1.2em 0 0; margin-top: auto; text-transform: uppercase; letter-spacing: 0.06em; }
.enso-stack_builton-label { display: block; margin-bottom: 0.55em; opacity: 0.5; }
/* round 12 (§4): resting underline OFF — the Osmo underline-link draws it
   on hover instead (the [data-underline-link] block below) */
.enso-stack_tech { color: inherit; text-decoration: none; padding-bottom: 0.1em; transition: opacity 0.3s ease; opacity: 0.85; }
.enso-stack_tech:hover, .enso-stack_tech:focus-visible { opacity: 1; }
/* chips: hairline-bordered badges (the .enso-badge vocabulary) */
.enso-stack_chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.enso-stack_chip { display: inline-block; border: 1px solid rgba(239, 238, 236, 0.28); padding: 0.28em 0.7em 0.32em; font-size: var(--_typography---font-size--text-tiny); text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap; color: rgba(239, 238, 236, 0.78); }
/* the light intro band re-inks its chips */
.enso-sol-proof .enso-stack_chip { border-color: color-mix(in srgb, var(--_theme---text) 30%, transparent); color: color-mix(in srgb, var(--_theme---text) 75%, transparent); }
/* image: held to the card's RIGHT edge — one object being dealt. The wrap
   clips the round-10 parallax (yPercent on the img). Round 11 layers, in
   order: photo -> darkening shade (the images sit back) -> p5 dots-and-
   lines field -> the chip connector box. The shade alpha and field opacity
   are THE knobs. z:0 on shade/field creates local stacking contexts that
   contain the p5 canvas's own z:10 (the head-style rule) below the chipbox. */
.enso-stack_media { position: relative; overflow: hidden; order: 2; }
.enso-stack_media img { position: absolute; inset: -8% 0; width: 100%; height: 116%; object-fit: cover; }
/* round 12: shade + chip grounds derive from the PAGE ground token —
   the old charcoal literals would read as muddy grey-brown over blue */
.enso-stack_media-shade { position: absolute; inset: 0; z-index: 0; background: color-mix(in srgb, var(--enso-blue-ground, var(--swatch--dark-900)) 50%, transparent); }
.enso-stack_media-field { position: absolute; inset: 0; z-index: 0; opacity: 0.55; overflow: hidden; pointer-events: none; }
/* the chip box: the .enso-quotebox vocabulary (hairline outline + 4px
   corner squares) centred in the image, with the chips stacked as stations
   on a vertical .enso-connector-family line running through them */
.enso-stack_chipbox { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 2; padding: clamp(1.2rem, 2.4vh, 1.9rem) clamp(1.1rem, 2vw, 1.7rem); max-width: 92%; }
.enso-stack_chipbox::before { content: ""; position: absolute; inset: 0; border: 1px solid rgba(239, 238, 236, 0.22); pointer-events: none; }
.enso-stack_chipline { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; margin-left: -0.5px; background: rgba(239, 238, 236, 0.25); pointer-events: none; }
.enso-stack_chipline-fill { position: absolute; inset: 0; background: var(--_theme---text); transform-origin: 50% 0; }
.enso-stack_chipline-cap { position: absolute; top: 0; left: 50%; width: 4px; height: 4px; background: var(--_theme---text); transform: translate(-50%, -50%); }
.enso-stack_chipbox .enso-stack_chips { position: relative; flex-direction: column; align-items: center; flex-wrap: nowrap; gap: clamp(0.7rem, 1.6vh, 1.1rem); }
/* chips need a ground over the busy image — near-opaque page ground */
.enso-stack_chipbox .enso-stack_chip { background: color-mix(in srgb, var(--enso-blue-ground, var(--swatch--dark-900)) 78%, transparent); }
@media screen and (max-width: 1099px) {
  /* the rail is hidden here — give the cards their full width back */
  .enso-stack { max-width: none; margin-inline: 0; }
}
@media screen and (max-width: 991px) {
  .enso-stack_card { grid-template-columns: 1fr; min-height: 0; }
  .enso-stack_media { order: 0; height: 34vh; }
  .enso-stack_media img { inset: 0; height: 100%; }
  /* 34vh of image: tighten the box so five stations still fit */
  .enso-stack_chipbox { padding: clamp(0.8rem, 1.6vh, 1.2rem) clamp(0.9rem, 1.6vw, 1.3rem); }
  .enso-stack_chipbox .enso-stack_chips { gap: 0.5rem; }
}

/* ---- the left rail ---- */
/* round 12 rebuild (REVERSES round 11's always-visible two-line labels —
   do not "fix" them back): at rest the rail is ONLY the framed vertical
   hairline with six dots on the track, the active dot tracking the current
   card. The riding 4px cap is DELIBERATELY REMOVED (round-10 element): the
   active dot is the position indicator now, and two indicators on one 1px
   line fight for the same pixels. Hovering the ≥44px transparent hit area
   (or keyboard focus entering the flyout's buttons) slides out a
   corner-squared box carrying the six labels as clickable rows — the
   labels are back to ONE line inside it. Hidden at rest: initSolutionsRail
   fades it in with the second section; autoAlpha's visibility:hidden also
   kills the hit area's pointer events while hidden/retired. */
.enso-rail { position: fixed; left: clamp(0.9rem, 2vw, 2.25rem); top: 50%; transform: translateY(-50%); z-index: 20; opacity: 0; visibility: hidden; }
/* the hover/focus target: a 1px line is not hoverable — this transparent
   strip, centred on the track and spanning its full height, is what opens
   the flyout */
.enso-rail_hit { position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%); width: 48px; }
.enso-rail_track { position: relative; width: 1px; height: min(24vh, 14rem); background: rgba(239, 238, 236, 0.22); }
/* terminal squares frame the line (the snake's start/end terminals) */
.enso-rail_terminal { position: absolute; left: 50%; width: 4px; height: 4px; background: var(--swatch--light-100); transform: translate(-50%, -50%); }
.enso-rail_terminal.is-top { top: 0; }
.enso-rail_terminal.is-bottom { top: 100%; }
.enso-rail_fill { position: absolute; inset: 0; background: var(--swatch--light-100); transform: scaleY(0); transform-origin: 50% 0; }
/* six dots ON the track: centres of six equal segments —
   top = (i + 0.5) / 6, --rail-i set inline per dot */
.enso-rail_dot { position: absolute; left: 50%; top: calc((var(--rail-i, 0) + 0.5) * 100% / var(--rail-n, 6)); width: 4px; height: 4px; background: var(--swatch--light-100); transform: translate(-50%, -50%); opacity: 0.45; transition: opacity 0.3s ease, transform 0.3s ease; }
.enso-rail_dot.is-active { opacity: 1; transform: translate(-50%, -50%) scale(1.6); }
/* the flyout: quotebox vocabulary (hairline outline + 4px corner squares)
   on a near-opaque page-ground panel; overlays the cards, reserves no
   layout width. Focusable at rest (opacity 0, NOT visibility:hidden — a
   hidden flyout could never receive keyboard focus, and :focus-within is
   what opens it for keyboard users); pointer-events off until open so it
   never eats clicks on the cards behind it. */
.enso-rail_flyout { position: absolute; left: calc(100% + 1rem); top: 50%; padding: clamp(0.9rem, 1.8vh, 1.3rem) clamp(1rem, 1.8vw, 1.5rem);
  background: color-mix(in srgb, var(--enso-blue-ground, var(--swatch--dark-900)) 82%, black);
  opacity: 0; transform: translate(-0.45rem, -50%); pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-rail_flyout::before { content: ""; position: absolute; inset: 0; border: 1px solid rgba(239, 238, 236, 0.22); pointer-events: none; }
.enso-rail:hover .enso-rail_flyout, .enso-rail:focus-within .enso-rail_flyout { opacity: 1; transform: translate(0, -50%); pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
  .enso-rail_flyout { transition: none; transform: translate(0, -50%); }
}
.enso-rail_list { display: flex; flex-direction: column; justify-content: flex-start; gap: 0.35rem; margin: 0; padding: 0; }
.enso-rail_tick { display: flex; align-items: center; gap: 0.6rem; padding: 0.15rem 0.25rem; margin: 0; background: none; border: none; color: var(--swatch--light-100); font: inherit; cursor: pointer; text-align: left; }
.enso-rail_tick:focus-visible { outline: 1px solid var(--swatch--light-100); outline-offset: 3px; }
.enso-rail_square { width: 4px; height: 4px; flex: none; background: currentColor; opacity: 0.45; transition: opacity 0.3s ease, transform 0.3s ease; }
/* one-line labels inside the flyout (round-12 reversal of the round-11
   two-line break — labels are not always-visible any more) */
.enso-rail_label { text-transform: uppercase; letter-spacing: 0.07em; white-space: nowrap; line-height: 1.3; opacity: 0.55; transition: opacity 0.3s ease; }
.enso-rail_tick:hover .enso-rail_label, .enso-rail_tick:focus-visible .enso-rail_label { opacity: 0.9; }
.enso-rail_tick.is-active .enso-rail_square { opacity: 1; transform: scale(1.6); }
.enso-rail_tick.is-active .enso-rail_label { opacity: 1; }
@media screen and (max-width: 1099px) {
  /* no room for the rail; the cards remain fully scroll-navigable */
  .enso-rail { display: none; }
}

/* ============================================================
   ROUND 11 — footer highlight-marker reveal (all five footers)
   Osmo Supply "Highlight Marker Text Reveal" — resource CSS
   verbatim. Footer column headings, link labels and the clock's
   WRAPPER carry [data-highlight-marker-reveal]: brand-blue bars
   cover each line and pull away to the right as the footer
   arrives (initFooterMarkerReveal / initHighlightMarkerTextReveal
   in barba-boot; column stagger via data-marker-delay).
   ============================================================ */
[data-highlight-marker-reveal] {
  visibility: hidden;
}
.wf-design-mode [data-highlight-marker-reveal] {
  visibility: visible;
}
[data-highlight-marker-reveal] .highlight-marker-line {
  width: auto;
  display: inline-block !important;
  margin: -0.055em 0px;
}
.highlight-marker-bar {
  position: absolute;
  inset: -0.055em 0px;
  z-index: 1;
  pointer-events: none;
}

/* ============================================================
   ROUND 12 — Osmo "Underline Link Animation" (§4)
   Base [data-underline-link] variant only: nothing at rest, the
   line draws in from the LEFT on hover, retracts to the RIGHT on
   exit. Reusable site-wide, but applied ONLY to .enso-stack_tech
   this round (the resting border-bottom came off there). Pure
   CSS — no init, no teardown, Barba-safe by construction.
   Deviations from the resource, both deliberate:
   - height gets a 1px floor: 0.0625em at text-tiny computes well
     under 1px and alpha-composites away on 1x displays (the nav
     hairline lesson);
   - the ="alt" two-stage variant is OMITTED, not shipped inert —
     it exists for links that already carry an underline, which
     this case (and this site) does not.
   Trap, documented: the [data-hover]:hover selector underlines
   EVERY descendant link when a [data-hover] ANCESTOR is hovered —
   never put data-hover on a wrapper around a group of these links
   (the links' own data-hover is fine: the combinator needs a
   descendant). rotate(0.001deg) is the resource's anti-aliasing
   nudge — keep it. */
[data-underline-link] {
  text-decoration: none;
  position: relative;
}

[data-underline-link]::before {
  content: "";
  position: absolute;
  bottom: -0.0625em;
  left: 0;
  width: 100%;
  height: max(1px, 0.0625em);
  background-color: currentColor;
  transition: transform 0.735s cubic-bezier(0.625, 0.05, 0, 1);
  transform-origin: right;
  transform: scaleX(0) rotate(0.001deg);
}

@media (hover: hover) and (pointer: fine) {
  [data-hover]:hover [data-underline-link]::before,
  [data-underline-link]:hover::before {
    transform-origin: left;
    transform: scaleX(1) rotate(0.001deg);
  }
}

/* ============================================================
   ROUND 14 — technology/<slug>.html detail pages
   One-screen block (beam ground + eyebrow + H1 + intro + the
   capability slider + application pills) over the standard
   charcoal tail. "One screen" is a TARGET, not a constraint:
   no 100vh, no max-height, no overflow clamp on content — the
   block is natural-height and tuned toward one screen at a
   normal desktop; on short laptops and phones it scrolls.
   ============================================================ */
/* round 16: real air below the nav — the old +clamp(1.25rem,2.5vh,2rem)
   left the eyebrow 23px under the bar, inside the nav's blur footprint
   (client). Now nav height + 10vh. */
/* round 17: more air below the slider before [ Quantum Security Risks ]
   (client). The gap is a chain — slider margin-bottom (16px) + the controls
   foot (54.5px) + THIS padding + the accordion section's own
   --_spacing---section-space--main (97.6px at 1440). Only this term belongs to
   the page type, so only this term moved: 4vh -> 9vh (36px -> 81px at 900
   tall). --_spacing---section-space--main is the export's global section
   rhythm and every page uses it — do NOT reach for it here. */
.enso-techdetail { padding-top: calc(var(--nav--height-total, 4rem) + clamp(4rem, 10vh, 7rem)); padding-bottom: clamp(4rem, 9vh, 7rem); }
/* content above the beam host (the host is z:0 absolute) */
.enso-techdetail > .section_contain { position: relative; z-index: 1; }
/* round 16: everything LEFT-ALIGNED on the container edge (client — the
   round-14 centred text sat over a left-aligned slider). The old centring
   rules are gone; the container's natural left alignment is the layout. */
/* application pills: under the H1, left-aligned, constrained — never
   page-spanning (client). 46rem wraps every set to 2 rows at 1440. */
.enso-techdetail_apps { display: flex; flex-wrap: wrap; gap: 0.6rem; max-width: min(100%, 46rem); margin-bottom: clamp(1.5rem, 3.5vh, 2.5rem); }
/* the intro: TWO left-aligned blocks side by side (client), each with its
   own measure; single column under 768. (The round-14 56ch "one-screen
   budget" rationale is retired — the one-screen figure was an ambition,
   not a spec.) */
/* round 17: the intro steps down from text-main to text-small (client — "the
   paragraph text can also be a smaller size"): 19px -> 17px at 1440. Set via
   the class's OWN token, the round-16 card-copy pattern, never a raw px — the
   export's .c-paragraph reads font-size from --_text-style---font-size on the
   same element, and :root defaults it to text-main. The card copy keeps its
   ×0.85 step BELOW this (14.45px), so the intro/card hierarchy survives.
   The 44ch measure is untouched and deliberately re-resolves smaller in px
   (621 -> 555): ch tracks font-size, so the columns still hold 44 characters. */
.enso-techdetail_intro { --_text-style---font-size: var(--_typography---font-size--text-small); display: grid; grid-template-columns: repeat(2, minmax(0, 44ch)); justify-content: start; column-gap: clamp(2.5rem, 5vw, 5rem); margin-bottom: clamp(1.75rem, 4vh, 2.75rem); }
.enso-techdetail_intro p { margin-block: 0; /* the export's rich-text p margins would misalign the two grid columns' tops */ }
@media screen and (max-width: 767px) {
  .enso-techdetail_intro { grid-template-columns: minmax(0, 1fr); row-gap: 1em; }
}

/* ---- the capability slider (Osmo "Basic GSAP Slider") ----
   Structural CSS adapted from the resource. --slider-spv is THE
   consequential knob: the resource DISABLES itself (static layout,
   aria/controls removed) when spv >= item count — 3.5 keeps the
   four-card pages sliding while six-card pages slide too, and the
   half-slide peek is the drag affordance. Keep spv BELOW 4. */
[data-gsap-slider-init] { --slider-status: on; --slider-spv: 3.5; --slider-gap: 1.5em; }
@media screen and (max-width: 991px) {
  [data-gsap-slider-init] { --slider-spv: 2.25; --slider-gap: 1.5em; }
}
@media screen and (max-width: 767px) {
  [data-gsap-slider-init] { --slider-spv: 1.15; --slider-gap: 1em; }
}
/* round 16: the slider BLEEDS to the viewport edge on both sides (client —
   cards must run off the page, not vanish into a margin). Negative inline
   margin + matching padding: overflow:hidden clips at the PADDING box, so
   moving cards stay visible out to the viewport edge while card 1 rests
   aligned with the H1 on the container edge. Never width:100vw — with a
   visible scrollbar that overshoots the document and adds page scroll. */
.enso-techslider { position: relative; overflow: hidden; margin-bottom: clamp(1rem, 2.5vh, 1.75rem); margin-inline: calc(-1 * var(--enso-bleed)); padding-inline: var(--enso-bleed); }
.enso-techslider_collection { width: 100%; }
.enso-techslider_list { display: flex; user-select: none; -webkit-user-select: none; will-change: transform; touch-action: pan-y; backface-visibility: hidden; }
.enso-techslider_item { flex: none; width: calc(((100% - 1px) - (var(--slider-spv) - 1) * var(--slider-gap)) / var(--slider-spv)); margin-right: var(--slider-gap); }
[data-gsap-slider-item]:last-child { margin-right: 0; }
/* the cards: the technology page's platform-card treatment — light fill,
   hairline border, padding, backdrop blur, hover lift (client, round 16).
   CLIENT-DIRECTED REVERSAL of the round-14 no-chrome rule: the image
   handoff said a light card fill would make the near-black frames
   "dissolve into the surface"; Josh saw the built pages and asked for
   exactly this treatment. Do not restore the no-chrome rule. Square
   frames: the set is 1024×1024 (the resource's 125% portrait frame
   would crop 20%). */
.enso-techslider_item { padding: clamp(1.25rem, 2vw, 1.75rem); background: rgba(239, 238, 236, 0.05); border: 1px solid rgba(239, 238, 236, 0.14); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); }
.enso-techslider_item:hover { background: rgba(239, 238, 236, 0.085); border-color: rgba(239, 238, 236, 0.32); }
@supports not (backdrop-filter: blur(1px)) {
  .enso-techslider_item { background: rgba(42, 40, 41, 0.9); }
  .enso-techslider_item:hover { background: rgba(48, 46, 47, 0.92); }
}
.enso-techslider_media { position: relative; width: 100%; padding-top: 100%; }
.enso-techslider_media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* round 16: title->copy gap opened (was 0.35em ≈ 8px — client: "way too
   close") and the copy steps down ×0.85 from text-small via the class's
   own token (≈14.5px — text-tiny at ~11.6px read too far). */
.enso-techslider_title { margin: 1em 0 0.65em; }
.enso-techslider_copy { margin: 0; --_text-style---font-size: calc(var(--_typography---font-size--text-small) * 0.85); color: color-mix(in srgb, var(--_theme---text) 72%, transparent); }
/* active/inactive: the resource's own status attribute as a DIM — never a
   background swap (the dim + border treatment reads as one system) */
[data-gsap-slider-item] { transition: opacity 0.35s ease, background-color 0.4s cubic-bezier(0.625, 0.05, 0, 1), border-color 0.4s cubic-bezier(0.625, 0.05, 0, 1); }
[data-gsap-slider-item-status="not-active"] { opacity: 0.4; }
/* controls: the platform double-arrow pair (round 16) — the export's
   arrow_icon_group ×2 inside the overflow-hidden button_main_icon_group.
   Hover slides the pair out and the twin in, the platform-card numbers.
   The PREV button's group is mirrored with scaleX(-1): chevrons point left
   and the same translateX(100%) reads as a leftward slide. Disabled = the
   resource's control-status dim. */
[data-gsap-slider-status="not-active"] [data-gsap-slider-controls] { display: none; }
.enso-techslider_controls { display: flex; gap: 0.75rem; }
/* round 17: the controls carry a 1px WHITE outline (client — "the arrows …
   should be outlined with a white outline"). Full --swatch--light-100, not a
   low-opacity hairline, because he said white. SQUARE (border-radius 0, the
   site rule) — the export's group keeps its own inert 2.4px --radius--small,
   invisible with no background behind it.
   The 0.6rem inset is what stops the chevrons being clipped by the box: the
   overflow:hidden that swallows the sliding pair belongs to the inner
   button_main_icon_group (59.2×48), so the slide is clipped 9.6px INSIDE the
   border and the outgoing chevron never reaches it. Padding + border grow the
   button to 78.4×67.2; both controls are identical so they stay the same size.
   The prev button's scaleX(-1) is on the inner group, not here, so this border
   is not mirrored — and the inset is uniform, so a mirror would be invisible
   anyway. Hover FILLS rather than brightens (white cannot brighten): the
   round-16 card hover in miniature, background only, border left alone. */
.enso-techslider_control { display: flex; align-items: center; justify-content: center; padding: 0.6rem; background: none; border: 1px solid var(--swatch--light-100); border-radius: 0; color: var(--swatch--light-100); cursor: pointer; transition: opacity 0.3s ease, background-color 0.4s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-techslider_control:hover,
.enso-techslider_control:focus-visible { background-color: rgba(239, 238, 236, 0.12); }
.enso-techslider_control .arrow_icon_group { transition: transform 0.25s cubic-bezier(0.625, 0.05, 0, 1); }
.enso-techslider_control:hover .arrow_icon_group,
.enso-techslider_control:focus-visible .arrow_icon_group { transform: translateX(100%); }
.enso-techslider_control[data-gsap-slider-control="prev"] .button_main_icon_group { transform: scaleX(-1); }
[data-gsap-slider-control-status="not-active"] { opacity: 0.2; pointer-events: none; }
/* grab cursors (plain — the demo's custom cursor files are not ours) */
[data-gsap-slider-list-status="grab"], [data-gsap-slider-list-status="grab"] * { cursor: grab; }
[data-gsap-slider-list-status="grabbing"], [data-gsap-slider-list-status="grabbing"] * { cursor: grabbing; }

/* ---- foot row: controls alone, bottom-right — the platform cards'
   arrows-in-the-corner placement (the pills moved up under the H1,
   round 16; the proof note is client-removed) ---- */
.enso-techdetail_foot { display: flex; justify-content: flex-end; }

/* ============================================================
   ROUND 18 — skybridge.html: the video-chapter framework
   N full-viewport videos crossfading under scrolling chapter
   panels (initVideoChapters in barba-boot — the count is read
   from the DOM). DELIBERATELY its own class family: index's
   crossfade is a SITE-scoped ix3 interaction triggering on the
   class pair `sticky_visual_wrap.u-section` and hard-coded to
   exactly three nth-child layers — reusing those classes here
   would bind that timeline to this stack. Never rename these to
   the index classes.
   ============================================================ */
.enso-chapters { overflow: clip; /* bounds the sticky visual, the #skybridge lesson (clip, not hidden — hidden breaks sticky) */ }
.enso-chapters_visual { position: sticky; top: 0; height: 100vh; overflow: hidden; }
.enso-chapters_item { position: absolute; inset: 0; opacity: 0; }
.enso-chapters_item:first-child { opacity: 1; /* no-JS / pre-init state: the opener is visible */ }
/* the handover darkening (the .enso-hero-dark family) — scrubbed by
   initVideoChapters as the NEXT section rises over the stack (round 19:
   Mission Architecture, found generically); sits above the layers */
.enso-chapters_dark { position: absolute; inset: 0; background: var(--swatch--dark-900); opacity: 0; pointer-events: none; }
/* the panels column rides up over the sticky visual: -100vh puts panel 1
   over the pinned opener, so panel k tops exactly at k*100vh of scroll —
   the boundary geometry initVideoChapters' fade windows key off */
.enso-chapters_texts { position: relative; z-index: 1; margin-top: -100vh; }
.enso-chapters_panel { height: 100vh; height: 100svh; /* round 24 §0.2: the hero must fit the VISIBLE viewport (the tag row sat under the browser bar) */ display: flex; flex-direction: column; justify-content: flex-end; padding-bottom: clamp(3rem, 8vh, 6rem); }
/* settled tail (ROUND 19 §2: 35vh -> 150vh). The last chapter's fade
   completes at its panel; the tail is then 50vh of genuinely settled hold
   PLUS the 100vh the next section's -100vh overlap consumes while climbing
   over the still-pinned visual. The sticky release only happens once the
   stack is fully covered — the release jolt is off-screen by construction.
   Tuning: settled hold = tail height - 100vh (the overlap depth below). */
.enso-chapters_tail { height: 150vh; }

/* ROUND 20 §1.3: with every section pulled INTO the reel, the climbing
   section is the RISKS accordion again — the round-19 mechanics (full -100vh
   dwell + z:3 + explicit opaque ground, the index
   Core-Technologies-over-the-quote pattern) re-targeted at it. The sticky
   visual is fully covered before its release; initVideoChapters' darkening
   finds this section generically as the host's next section sibling. */
[data-barba-namespace="skybridge"] .page_main > .u-section:has(.accordion_wrap) { position: relative; z-index: 3; margin-top: -100vh; padding-top: clamp(4.5rem, 12vh, 9rem); padding-bottom: clamp(3.5rem, 9vh, 7rem); background-color: var(--swatch--dark-900); }

/* ============================================================
   ROUND 19/20 — skybridge.html: hero, mission tags, mission clock,
   Mission Architecture accordion, Demonstrates + phases, Voices.
   ROUND 20 pulled every section INTO the video reel (chapter panels
   carry the content; the standalone sections are gone) and reworked
   the accordion + voices per client notes. The whole page is CENTRED
   by client direction (deliberately unlike the left-aligned
   technology detail pages — do not "fix" either way).
   ============================================================ */

/* ---- hero: panel 1 of the chapter stack. Heading block centred in the
   viewport, mission clock directly under the second heading line (round 20
   — was a fixed corner readout), mission tags at the panel's foot. ---- */
.enso-chapters_panel.is-hero { justify-content: flex-start; align-items: center; text-align: center; padding-top: 0; }
.enso-chapters_hero { margin-top: auto; margin-bottom: auto; display: flex; flex-direction: column; align-items: center; text-align: center; padding-top: clamp(4rem, 10vh, 7rem); /* nav clearance: the block centres in the leftover space */ }
.enso-chapters_hero .c-heading { text-align: center; /* the export's heading blocks reset to start; multi-line wraps must centre too */ }

/* ---- the mission readout (round 20: IN-FLOW hero content, all viewports —
   the desktop-only rule belonged to the fixed overlay, not to content).
   Instrument treatment: tiny caps, tabular figures. The honesty rule lives
   at the markup and initMissionClock — never extend this beyond elapsed
   time + the ≈-labelled derived orbit count. ---- */
.enso-mclock { margin-top: 0.9em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--swatch--light-100); }
.enso-mclock_row { margin: 0; display: flex; justify-content: center; gap: 0.75em; }
.enso-mclock_row + .enso-mclock_row { margin-top: 0.2em; }
.enso-mclock_label { opacity: 0.55; }
.enso-mclock_label::after { content: ":"; }
.enso-mclock_value { font-variant-numeric: tabular-nums; }

/* ---- mission tags: three .enso-quotebox-family outlined chips cycling
   through six tags with the house scramble (initMissionTags). Round 20 §3:
   the drawn lines + corner dots SCRUB away as the hero leaves (and back on
   scroll up) — the scrub tween lives in initMissionTags. ---- */
.enso-tags { display: flex; gap: clamp(0.75rem, 1.6vw, 1.25rem); width: min(100%, 58rem); justify-content: center; }
.enso-tag { position: relative; flex: 1 1 0; min-width: 0; min-height: 3.1em; display: flex; align-items: center; justify-content: center; gap: 0.55em; padding: 0.6em 1em; text-align: center; }
.enso-tag::before { content: ""; position: absolute; inset: 0; border: 1px solid rgba(239, 238, 236, 0.2); pointer-events: none; }
.enso-tag_text { text-transform: uppercase; letter-spacing: 0.06em; }
/* the status SQUARE (site rule: squares, never the source's ● bullet) —
   shown only while a box holds the In Orbit tag (initMissionTags toggles) */
.enso-tag_square { display: none; width: 4px; height: 4px; background: var(--_theme---text); flex: none; }
.enso-tag_square.is-on { display: inline-block; }
@media (max-width: 767px) {
  .enso-tags { flex-direction: column; align-items: stretch; }
  .enso-tag { min-height: 2.6em; }
}

/* ---- section panels (round 20 §1): each chapter panel carries one real
   content block over its video. height:auto so unequal panels work — the
   fade boundaries are MEASURED per panel in initVideoChapters, never
   assumed equal. ---- */
.enso-chapters_panel.is-section { position: relative; height: auto; min-height: 100vh; min-height: 100svh; /* round 24 §0.2 */ justify-content: center; align-items: center; padding-top: clamp(5rem, 12vh, 8rem); padding-bottom: clamp(3rem, 8vh, 6rem); }
/* ROUND 21 §3: the scrim is ONE CONTINUOUS LAYER on the sticky visual, not
   four per-panel rectangles — the per-panel version (round 20) was bounded
   by each panel and skipped the hero, so its 0.35 edge scrolled up the
   screen as a visible line at every boundary over the continuous video.
   Painted as the visual's ::after it sits above every video layer and
   below the texts column (z:1) with NO edge anywhere, hero included.
   --enso-panel-scrim is still THE legibility knob (one value, whole reel;
   hero verified legible at it — see the round-21 report). */
.enso-chapters_visual::after { content: ""; position: absolute; inset: 0; background: var(--swatch--dark-900); opacity: var(--enso-panel-scrim, 0.35); pointer-events: none; }
/* the phases panel keeps ~34vh of bottom runway so the NEXT chapter's
   crossfade (the last ~30vh before its panel tops) happens over quiet
   video after the timeline region ends — the chapter-3 clip must hold
   until the Voices section genuinely arrives (round 21 §5.3) */
.enso-chapters_panel.is-phases { padding-bottom: 34vh; }

/* ---- §4 Mission Architecture: the horizontal-axis accordion, round-20
   mechanics. THE JITTER FIX (§4.1), both halves:
   1) NOTHING RE-WRAPS during the width change — the paragraph has a FIXED
      width and titles are white-space:nowrap, so the animating box just
      clips a constant-layout content block (overflow:hidden on the item).
      Animating flex-grow still re-solves the row, but with no text
      re-flow inside it the solve is cheap and visually stable.
   2) NO CSS transitions on the moving parts — width, reveal, paragraph,
      blur and edges all live on ONE GSAP timeline per open/close in
      initArchAccordion, so an interrupted click is killed and rebuilt
      from real current state (CSS transitions here would double-ease the
      GSAP writes — the glass-block lesson).
   .is-open flex-grow is the no-JS/first-paint resting value only; GSAP
   animates the inline style over it. ---- */
.enso-arch_header { text-align: center; }
.enso-arch_header .eyebrow_wrap, .enso-arch_header .eyebrow_layout { justify-content: center; }
.enso-arch_header .c-heading { margin-left: auto; margin-right: auto; }
.enso-arch { width: min(100%, 68rem); margin: clamp(2rem, 5vh, 3.5rem) auto 0; }
.enso-arch_row { display: flex; align-items: stretch; gap: clamp(0.75rem, 1.5vw, 1.25rem); }
/* the card: number pinned top-left, title on the floor, paragraph entering
   UNDER the title inside the bottom-anchored block (§4.3) — taller than
   round 19, left-aligned, clipping its fixed-width content as it narrows */
.enso-arch_item { position: relative; flex: 1 1 0; min-width: 0; min-height: clamp(17rem, 38vh, 22rem); display: flex; flex-direction: column; align-items: flex-start; text-align: left; padding: clamp(1.1rem, 2.2vh, 1.6rem) clamp(1rem, 1.6vw, 1.5rem); cursor: pointer; overflow: hidden; }
.enso-arch_item::before { content: ""; position: absolute; inset: 0; border: 1px solid rgba(239, 238, 236, 0.2); pointer-events: none; /* §4.2: the faint resting outline on ALL cards; the bright edges draw on open */ }
.enso-arch_item.is-open { flex-grow: 2.8; cursor: default; }
/* INVERTED blur (round 19, deliberate): the surface belongs to the CLOSED
   boxes and clears when a box opens. GSAP owns the fades (no transition). */
.enso-arch_item .enso-qbox_bg { opacity: 1; visibility: visible; }
/* the number and the bottom block are LIFTED above the absolute blur layer
   — absolutes paint over static flow content regardless of DOM order (the
   round-19 head carried this; the restructure must too) */
.enso-arch_nr { display: block; position: relative; z-index: 1; }
.enso-arch_bottom { margin-top: auto; width: 100%; position: relative; z-index: 1; }
.enso-arch_head { padding: 0; margin: 0; background: none; border: 0; color: inherit; font-family: inherit; cursor: inherit; text-align: left; display: block; }
/* nowrap = constant layout while the box narrows (§4.1); the step down from
   h5 keeps the longest title ("Architecture") inside a CLOSED card at every
   supported width — a resting clipped title is not a state */
.enso-arch_title { display: block; margin: 0; line-height: 1.15; white-space: nowrap; font-size: calc(var(--_typography---font-size--h5) * 0.82); }
.enso-arch_reveal { overflow: hidden; height: 0; /* GSAP animates height; .is-open's auto is the no-JS state */ }
.enso-arch_item.is-open .enso-arch_reveal { height: auto; }
/* §4.1: FIXED paragraph measure — it never re-wraps as the card animates.
   The value tracks the OPEN card's content width across viewports (it only
   changes on resize, never with the card's animated width). */
.enso-arch_para { margin: 0; padding-top: 0.9em; width: min(26rem, 40vw - 2rem); }
/* closed boxes recede a step (the accordion closed-row dim vocabulary) —
   number and title together */
.enso-arch_item:not(.is-open) .enso-arch_head,
.enso-arch_item:not(.is-open) .enso-arch_nr { opacity: 0.6; transition: opacity 0.3s ease; }
.enso-arch_item:not(.is-open):hover .enso-arch_head,
.enso-arch_item:not(.is-open):focus-within .enso-arch_head,
.enso-arch_item:not(.is-open):hover .enso-arch_nr,
.enso-arch_item:not(.is-open):focus-within .enso-arch_nr { opacity: 0.9; }
.enso-arch_controls { display: flex; justify-content: center; gap: 0.75rem; margin-top: clamp(1.25rem, 3.5vh, 2rem); }
/* ROUND 21 §4/§6.1 — ONE shared compact control for the arch AND Voices
   arrows (replaces round 19/20's is-small, which shrank the whole control
   glyph included). The glyph size is governed by the INNER
   .arrow_icon_group: the export fixes it at height 3rem with 1rem padding
   every side, so the chevrons rendered 16px tall no matter the button —
   the compact variant reclaims that inner padding (glyph ≥2× at ~2rem)
   while the BUTTON's own padding drops a third (0.35rem → 0.23rem).
   Tighter button, much more present arrow. The technology detail pages
   keep the plain un-modified control (round 22's brief owns them). */
/* ROUND 23 §1 — exact numbers (two rounds of adjectives missed): the
   reference is the main button component's arrow element, measured
   59.2 x 48; the compact control is 83% of it — box ≈ 49 x 41 with a
   ≈ 26.5px glyph (round 21's 32px glyph comes DOWN, with the box).
   One rule for all three consumers: SkyBridge arch, SkyBridge Voices,
   and the five technology/<slug> slider controls. */
.enso-techslider_control.is-compact { padding: 0.14rem; }
.enso-techslider_control.is-compact .button_main_icon_group { width: 2.66rem; }
/* round 23.1 (client): ICONS smaller, buttons untouched — this settled the
   round-23 "83%" ambiguity toward the main button's own glyph scale. The
   glyph size is height − 2×padding, so the padding is the knob that
   shrinks the icon WITHOUT moving the button box: glyph 26.5px → ~16px,
   the same icon size the main button component itself renders. */
.enso-techslider_control.is-compact .arrow_icon_group { height: 2.16rem; padding: 0.58rem; }
@media (max-width: 767px) {
  /* narrow: the horizontal axis has no room — the accordion stacks and the
     open/close becomes the paragraph reveal alone (deliberate degrade) */
  .enso-arch_row { flex-direction: column; align-items: stretch; }
  .enso-arch_item, .enso-arch_item.is-open { flex: 0 0 auto; min-height: 0; }
  .enso-arch_bottom { margin-top: 0.75em; }
  .enso-arch_para { width: 100%; }
}

/* ---- §6 Demonstrates + phases, one block. The phases markup stays
   BYTE-IDENTICAL with index (hash-checked) — the duplicate eyebrow/title
   are hidden HERE, in CSS scoped to this namespace, never edited out of
   the markup. That CSS-hide is the mechanism that preserves the byte
   contract; breaking it means re-hashing both pages. ---- */
.enso-demo_head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.enso-demo_head .eyebrow_wrap, .enso-demo_head .eyebrow_layout { justify-content: center; }
/* round 21 §5.2: a smaller CONTAINER (client — not a <br>) so the heading
   breaks to two clean lines: "Post-Quantum Security" / "Beyond the
   Laboratory". The measure sits on the P (which carries the heading font)
   in em, so it scales with the type: "Post-Quantum Security" ≈ 12em, with
   " Beyond" ≈ 16.1em — 14em breaks after "Security" at every size. */
.enso-demo_head .c-heading p { max-width: 14em; margin-left: auto; margin-right: auto; }
.enso-demo_copy { max-width: 52ch; margin-left: auto; margin-right: auto; }
.enso-phases_reelwrap { width: 100%; }
/* ROUND 23 §2.4 — the reel connectors are MEASURED, not guessed (they join
   the positionSkyConnector family): all three (is-arch, is-phlink, and the
   reel's is-quote) are ABSOLUTE inside the texts column so their length
   never feeds back into the layout they are measuring; JS
   (positionReelConnectors) sets top/height from the real gap between the
   previous element's bottom and the next element's top, re-measured on
   every refreshInit. The round-21 fixed heights (50vh/16vh) are GONE —
   they were why the lines "rarely started in the right place". Index's
   own connectors live outside .enso-chapters_texts and are untouched. */
.enso-chapters_texts .enso-connector { position: absolute; left: 0; right: 0; height: auto; margin: 0; }
/* the inter-section runway lives on the PANELS now (the connectors no
   longer occupy flow): each section panel stands 24vh off its
   predecessor — the space the measured lines span and the anchor-based
   crossfades (initVideoChapters, round 23) breathe in */
.enso-chapters_panel.is-section { margin-top: 24vh; }
/* round 23 §2.3: the round-21 top-anchoring of the phases stack on this
   page is REVERTED — the stack centres in its sticky viewport exactly as
   on index (the gap it was working around is now spanned by the measured
   is-phlink connector). */
[data-barba-namespace="skybridge"] .enso-phases_eyebrow,
[data-barba-namespace="skybridge"] .enso-phases_title { display: none; }
/* the hidden title took its 8vh margin with it — the sticky centres the
   stack alone in each viewport, which is the wanted reading; the head
   above scrolls away before the stack pins */

/* ---- §7 Voices: TWO full drawn cards in one grid cell (box height = the
   taller quote, cannot jump mid-swing); the WHOLE card swings — controls
   sit outside below. Perspective comes from gsap transformPerspective per
   card. ---- */
.enso-voices { display: flex; flex-direction: column; align-items: center; width: 100%; }
.enso-voices_stage { display: grid; justify-items: center; }
.enso-voices_card { grid-area: 1 / 1; backface-visibility: hidden; min-width: 0; }
.enso-voices_card:not(.is-active) { visibility: hidden; /* no-JS rest state; JS drives autoAlpha */ }
.enso-voices_role { display: block; margin: 0 0 1.25em; text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.7; }
.enso-voices_controls { display: flex; justify-content: center; gap: 0.75rem; margin-top: clamp(1.25rem, 3vh, 2rem); }

/* reduced motion: the JS inits skip entrances/cycles and make open/close
   instant; this keeps the CSS-driven head dim from animating too */
@media (prefers-reduced-motion: reduce) {
  .enso-arch_item:not(.is-open) .enso-arch_head { transition: none; }
}

/* ============================================================
   ROUND 22 — the mobile pass (site-wide). Mobile-only fixes live
   at breakpoints; desktop treatments are deliberately untouched.
   ============================================================ */

/* ---- §1 the wipe gaps: the five .transition_column bands abut in a
   column flex — at some device heights/DPRs 5 × 20% doesn't sum to the
   viewport and hairline slivers of the page show between bands. Overlap
   instead of abut: each band 3px taller, pulled 1.5px over its
   predecessor — rounding can never open a gap. Applies to the Barba wipe
   AND the return-visit load-in (same .transition_wrap markup). The scaleX
   animation is horizontal and unaffected. ---- */
.transition_column { flex: none; height: calc(20% + 3px); }
.transition_column:not(:first-child) { margin-top: -1.5px; }

/* ---- ROUND 28.1 — the blue column loader must never appear on a FIRST visit.
   `.transition_wrap` is `display: flex` by default (inline <style> in each
   page head), and the ONLY thing that hid it on a first visit was barba's
   `once()` hook inside barba-boot.js. Round 28 deferred barba-boot until the
   splash intro finished, so the blue wrap sat over the screen for the whole
   intro (~11s on a throttled phone, vs ~0.8s before) — the aurora sequence
   played underneath it and the overlay lifted to reveal an already-settled,
   motionless splash.

   Hiding it in CSS instead removes the dependency on WHEN the JS lands. This
   is not a behaviour change: it is exactly what barba's own `once()` does on
   the `:not(.no-preloader)` branch, just from the first paint. Return visits
   (html.no-preloader) keep `display: flex` and still get the export's wipe
   load-in, and page transitions are unaffected because `coverIn()` sets
   `display: flex` as an INLINE style, which outranks this rule. ---- */
html:not(.no-preloader) .transition_wrap { display: none; }

/* ---- §3.1 / ROUND 23 §3 — the "extra large" menu Contact (Josh's own
   name for it). Round 22 compared the label to the DESKTOP bar's links
   (~15px) and set text-main (~17px) — but the links inside the open menu
   are the h3 x 0.9 step (~40px at phone widths), so Contact still looked
   tiny beside them. The label now uses THE IDENTICAL EXPRESSION as the
   menu links (calc(h3 * 0.9)) so the two can never drift, and the arrow
   group and padding scale up to suit — large text in a matching pill, not
   a small one. Scoped to the menu overlay: the desktop bar's Contact is
   untouched (the overlay only ever holds the relocated menu instance). ---- */
.w-nav-overlay .button_main_wrap { margin-top: clamp(1.5rem, 4vh, 2.5rem); }
/* ROUND 24 §8.3 — the proper "extra large" PRIMARY BUTTON SIZE VARIANT
   (third attempt; rounds 22/23 patched the label and left the halves
   mismatched). The desktop component's own arithmetic makes its two
   halves equal: text box = label + 1rem padding all round (= 48px),
   icon box = 3rem arrow group + its 1rem inner padding (= 48px). This
   variant scales THAT WHOLE ARITHMETIC by one factor — the label ratio
   (h3x0.9 / text-mainx0.9 ≈ 1.92) — so text box, icon box, padding and
   glyph all grow together and the two halves are identical BY
   CONSTRUCTION, not by tuning. --menu-btn-scale is the only knob. */
.w-nav-overlay .button_main_wrap { --menu-btn-scale: 1.92; }
.w-nav-overlay .button_main_text { font-size: calc(var(--_typography---font-size--h3) * 0.9); }
/* the export sets an EXPLICIT height: 3rem on the text wrap (the padding is
   decorative) — both halves derive from the same 3rem, so both scale it */
.w-nav-overlay .button_main_text_wrap { height: calc(3rem * var(--menu-btn-scale)); padding: calc(1rem * var(--menu-btn-scale)); }
.w-nav-overlay .button_main_icon_group { width: calc(3.7rem * var(--menu-btn-scale)); }
.w-nav-overlay .arrow_icon_group { height: calc(3rem * var(--menu-btn-scale)); padding: calc(1rem * var(--menu-btn-scale)); }
/* ROUND 26 §5 — the ONE piece of the button §8.3's arithmetic missed. The
   two halves do measure equal now (92.2px each, verified live at 390x844),
   but .arrow_icon_bg — the absolutely-positioned fill BEHIND the arrows,
   which carries the button's border and its hover colour — kept the
   unscaled 3rem. Pinned to the icon half's top by `inset: 0%` with an
   explicit height, it drew a 48px bordered box inside a 92px half: a seam
   across the arrow side at rest, and on hover only the top half of that
   side would have changed colour. Same factor as its three siblings. */
.w-nav-overlay .arrow_icon_bg { height: calc(3rem * var(--menu-btn-scale)); padding: calc(1rem * var(--menu-btn-scale)); }

/* ---- §3.2 the hairline under the nav vanished when the menu opened: the
   menu surface (a later sibling) painted over it. z-index ONLY — the line
   is already absolute (round-9 rule: never set position on the absolute
   nav lines), and its weight/opacity are client-tuned three times over
   and must not change. ---- */
.nav_component .nav_line.is-horrizontal.is-mobile { z-index: 20; }

/* ---- §5 the footer CTA visual goes at PHONE widths (client: desktop
   keeps it, "potentially on tablet" — the ≤991px stacked fallback stays
   as the tablet treatment; 767 is the site's phone line and the image
   disappears there). CSS only: the markup stays byte-identical x10.
   initFooterCta keeps cycling — it only toggles classes on the hidden
   panels and drives the progress hairline in the list column. ---- */
@media (max-width: 767px) {
  .enso-cta_visual { display: none; }
}

/* ---- §7.1 About: more room under section two at phone widths. COUPLED:
   #approach climbs with margin-top -20vh — at phones the recede parallax
   is minimal and the deepened padding gives the fold its room; overlap
   re-measured at 390 (see the round-22 report). ---- */
@media (max-width: 767px) {
  [data-barba-namespace="about"] #about { padding-bottom: clamp(9rem, 22vh, 13rem); }
}

/* ---- §7.4 phases titles: more air under the ACTIVE title at phone
   widths (the gap below "Launch" before the dimmed stack). Extends the
   round-19/20 mobile restack; CSS only — the block stays byte-identical
   with index. ---- */
@media screen and (max-width: 767px) {
  .enso-phases_titles { gap: 1em; }
}

/* ---- §9 Solutions at phone widths: the stack cards can't fit — the
   round-11 chipbox (chips + connector in a drawn box, centred on the
   image) is HIDDEN and the media flattens to a shallow band, well under
   16:9. MOBILE-ONLY REVERSAL: desktop keeps chips, connector, box and the
   34vh tablet media exactly as round 11/12 built them — do not restore
   the chips here or carry the flattening up. The p5 field and the -14
   yPercent image parallax run unchanged in the shorter frame. ---- */
@media (max-width: 767px) {
  .enso-stack_chipbox { display: none; }
  .enso-stack_media { height: 18vh; min-height: 7rem; }
}

/* ============================================================
   ROUND 24 — the mobile audit. §0's rules live inline at their
   subjects (svh on visible-height boxes; ignoreMobileResize +
   stableVH in barba-boot). "Mobile" = every nav-collapse width
   (<=991px), per the client. Desktop untouched throughout.
   ============================================================ */

/* ---- §1 the export's fixed scroll counter goes below the nav
   breakpoint (every page; desktop keeps it) ---- */
@media (max-width: 991px) {
  .progres-nr_wrap { display: none; }
}

/* ---- §3 the Risks overlap at nav-collapse widths: the -14vh margin is
   width-agnostic, but its PARTNER (the #technologies Osmo recede) is
   data-parallax-disable="tablet" — deliberately off below 992px since
   round 13.2 (stacked cards over-sink at 16 yPercent). Solo, -14vh only
   ate the closing spacer (the round-13 finding). At mobile the margin
   carries the effect ALONE, so it is deepened until the blue edge
   genuinely rides the card row. Desktop keeps -14vh + the recede pair. ---- */
   ROUND 26 §3 SUPERSEDES the -42vh: the client's read was "overlapping
   too far up but NOT MOVING", and both halves of that are this rule's
   fault. -42vh is 354px at 844 — it ate the section's 252px tail AND
   100px of the card row in one static jump (too far up), and a margin
   cannot animate (not moving). The overlap was never meant to be a
   position; it is a TRAVEL — the blue edge climbs the cards as you
   scroll, which on desktop is the #technologies recede doing the work.
   The margin is now MEASURED off the section's own tail (vh never knew
   where the tail was) and the recede runs on mobile in PIXELS, which is
   why it was disabled here in the first place: yPercent is a share of
   the target's height, and the stacked mobile contain is ~950px, so the
   desktop 16% would sink 152px+ and grow with the content. See
   initRisksRecede in barba-boot.js — this rule is only the pre-JS floor
   so the fold never renders with no overlap at all. ---- */
/* ROUND 27.1 — the pre-JS floor now matches the new rest position, which
   leaves the "Discover Our Technologies" CTA visible instead of biting over
   it. initRisksRecede replaces this with a MEASURED inline margin on load;
   this value only governs the frame before that runs, so it deliberately
   UNDER-overlaps — a frame with slightly too much air is invisible, a frame
   with the CTA buried is the bug the client reported. */
@media (max-width: 991px) {
  [data-barba-namespace="home"] .page_main > .u-section:has(.accordion_wrap) { margin-top: -20vh; }
}

/* ---- §4 the accordion focus box on tap: a tap gives the toggle button
   focus, and the export's outline showed where a mouse user never sees
   it. Suppressed for NON-KEYBOARD focus only — :focus-visible (real
   keyboard a11y) still shows the ring. Shared accordion = five pages,
   one rule. ---- */
.accordion_toggle_button:focus:not(:focus-visible) { outline: none; }

/* ---- §7 the About hero paragraph pair (see the markup comment: two
   copies, KEEP IN SYNC) ---- */
.enso-hero-para-mobile { display: none; }
@media (max-width: 991px) {
  .enso-hero-para-desktop { display: none; }
  .enso-hero-para-mobile { display: block; }
}

/* ---- §8.1 the nav lines are VARIANT-AGNOSTIC now. The export keys its
   line POSITIONING to Webflow variant hashes (48c3d670 / 953abaad) — a nav
   without the hash silently loses it. Inventory (round 24): index's nav
   ("nav-temporary-jan-26") carries NO hash on any line; the other seven
   source pages all carry 48c3d670. Index's mobile horizontal was therefore
   position:STATIC, landing at the bar's bottom only by flex accident. The
   lines are ours (weight tuned x3 with the client) so their geometry must
   not depend on which variant a page was exported with. This pins the
   mobile horizontal to the BAR's bottom on every variant — and since the
   bar (.nav_wrap) does not grow when the menu opens (the menu is an
   overlay), the line cannot travel with the open menu (§8.2). Weight and
   opacity untouched. ---- */
.nav_component .nav_line.is-horrizontal.is-mobile { position: absolute; inset: auto 0% 0%; }

/* ---- §2 the growing video section's ground: #skybridge is transparent
   and the first opaque layer behind it is charcoal, so the 3% inset
   margins during the grow-in read dark against the WHITE section two it
   emerges from — on desktop too, structurally (client reported mobile).
   The round-10 About precedent: the section carries the OUTGOING ground
   (#mission-vision got the off-white; this section's neighbour computes
   pure white). The pinned video covers the viewport for the whole
   section after the grow, and the quote white-out owns the exit. ---- */
[data-barba-namespace="home"] #skybridge { background-color: #ffffff; /* = section two's computed ground */ }

/* ============================================================ ROUND 25 ==
   §3 iPhone notch/island: the nav blur reaches the PHYSICAL top now.
   `viewport-fit=cover` joined the viewport meta on all eight source pages
   (the generated sets inherit it from their shells) — without it iOS keeps
   fixed elements below the status bar, so the nav's progressive-blur wrap
   stopped where the safe area began while the page painted on under it.
   Mechanism (two coordinated parts — the meta alone would put the logo,
   links and burger UNDER the island):
   - .nav_component (fixed, top 0, and always transformed by the
     hide-on-footer gsap y — which makes it the containing block for its
     "fixed" blur-wrap child) gets padding-top = the safe inset, so its box
     still starts at the physical top while the bar contents (.nav_wrap,
     whose bottom the mobile hairline is pinned to) sit exactly where they
     always did. The absolute vertical hairlines/top dots span the padded
     box and run up behind the status UI — 0.35px at 0.42 opacity,
     invisible there by construction.
   - the nav's own blur wrap grows by the same inset so the fade profile
     below the bar is unchanged. Scoped `>` — the .is-card copies inside
     research cards must not move. env() resolves to 0 everywhere without
     a notch, so desktop and Android are byte-identical no-ops. ---- */
/* ROUND 26 §2 — round 25's version still failed on iOS Safari (client).
   The meta and the growth were right; the ANCHOR was the problem. The
   blur wrap is position:FIXED inside .nav_component, and .nav_component
   carries `container-type: inline-size` (export) AND a permanent gsap
   transform — either one alone makes it the containing block for that
   fixed child. Blink resolves the child's top:0 against the PADDING box,
   so padding-top on .nav_component still left the blur at the physical
   top (verified live with the inset stubbed to 59px). WebKit resolves
   contained/transformed fixed children against the CONTENT box, so the
   very padding that made room for the island pushed the blur down by
   exactly the inset — "the blur only starts where the Nav starts".
   Fix: take the padding OFF the ambiguous element. With .nav_component
   unpadded its content, padding and border boxes are the same box, so
   top:0 is the physical top under BOTH engines and there is nothing left
   to disagree about. The inset moves to .nav_wrap, which is not a
   containing block for anything fixed — the bar contents, the mobile
   hairline pinned to .nav_wrap's bottom, and the Webflow menu overlay
   (top:100% of .nav_wrap) all land exactly where they did before.
   env() is 0 without a notch, so desktop/Android are byte-identical. */
.nav_component { padding-top: 0; }
.nav_component .nav_wrap { padding-top: env(safe-area-inset-top, 0px); }
.nav_component > .progressive-blur_wrap {
  top: 0;
  height: calc(15vw + env(safe-area-inset-top, 0px));
  /* ROUND 27.2 — `overflow: clip` (the export's value) is Safari 16+ and has
     a history of mis-clipping composited backdrop-filter layers. `hidden`
     clips identically for this box — it is fixed, pointer-events:none and
     never scrolls — while being the long-settled path in WebKit. Scoped to
     the NAV wrap: the .is-card copies inside research cards keep the export
     value. Paired with alignNavBlurToTop, which measures where this box
     actually landed rather than assuming. */
  overflow: hidden;
}
/* §3b the home-indicator edge: with viewport-fit=cover the page runs to the
   physical bottom, so the footer's last row (Site Credit / ©) could hug the
   indicator bar. Additive padding, 0 on everything without an inset. */
.footer_wrap { padding-bottom: env(safe-area-inset-bottom, 0px); }

/* ---- §5 the SkyBridge Voices quotes step DOWN at mobile (client: "smaller
   text now... might mean not having them as heading text" — the weight is
   the ask, the markup is not: the <blockquote> and its u-text-style-h4
   class stay, only the class's own tokens are re-pointed, the round-16/17
   pattern). h4 ran ~9 lines on a 367px card (round 24). Scoped under
   .enso-voices — index's founder quote shares .enso-quote_text and must
   not move. "Mobile" = the nav-collapse width, per Josh (round 24). ---- */
@media (max-width: 991px) {
  .enso-voices .enso-quote_text {
    --_text-style---font-size: var(--_typography---font-size--text-main);
    --_text-style---line-height: var(--_typography---line-height--1-5);
  }
}

/* ---- §7 Core Technologies MOBILE order: eyebrow → title → paragraph →
   slider → CTA. The export's .u-vflex-tablet-swap goes column-REVERSE at
   ≤991, which put the title (and the CTA) above the eyebrow+intro row —
   Josh read it as "the eyebrow is under the title". No markup moves (the
   desktop composition and its hand-tuned bottom-baseline alignment of the
   CTA to "Technologies" live entirely in the >991 rules above): at ≤991
   the three wrapper layers flatten with display:contents so the five
   pieces become direct flex children of the section contain, and `order`
   does the sequencing. The empty content-wrapper div defaults to order 0
   (first) — it renders nothing. #technologies exists only on index; the
   swap wrapper is unique to this section (verified). ---- */
@media screen and (max-width: 991px) {
  #technologies .section_contain { display: flex; flex-direction: column; }
  #technologies .u-vflex-tablet-swap,
  #technologies .u-vflex-tablet-swap > .u-display-contents,
  #technologies .u-vflex-tablet-swap .u-flex-horizontal-wrap,
  #technologies .enso-tech-header_row { display: contents; }
  #technologies .u-vflex-tablet-swap .eyebrow_wrap { order: 1; }
  /* the desktop row zeroes the heading's u-mb-text (baseline alignment);
     stacked, the paragraph sat on the title's descenders — restore a gap */
  #technologies .enso-tech-header_row .c-heading { order: 2; margin-bottom: 0.5em; }
  #technologies .u-vflex-tablet-swap .c-paragraph { order: 3; }
  #technologies .section_contain > .slider_wrap { order: 4; }
  #technologies .enso-tech-header_row .button_main_wrap { order: 5; margin-top: clamp(1.5rem, 4vh, 2.5rem); align-self: flex-start; }
}

/* ================================================== ROUND 26 — C&O ==
   COLLABORATION & OWNERSHIP (skybridge chapter 4 — the section deferred
   in round 19, back with partner logos). Centred like the rest of this
   page (standing client decision); heading on the h3 step via the
   w-variant hash (round-20 pattern) with a constrained measure ("without
   too much width"); two plain paragraphs at the demo-copy 52ch measure;
   then TWO drawn qbox cards side by side at desktop, STACKED ≤767 — the
   two-card row holds to well under tablet width (each card still carries
   ~36ch beside its partner at 768), so the phone breakpoint is the right
   one, not 991. Card interiors are left-aligned (the arch-card precedent:
   the PAGE composition is centred, card innards need not be). The content
   and logo are LIFTED above the absolute .enso-qbox_bg (the round-20
   paint-order rule: static content sharing a box with an absolute layer
   needs position:relative). Logos sized by a SHARED MAX-HEIGHT so the two
   marks read as the same visual weight (DPhi 2.85:1, HES-SO 2.93:1). */
.enso-collab { display: flex; flex-direction: column; align-items: center; width: 100%; }
.enso-collab_head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.enso-collab_head .eyebrow_wrap, .enso-collab_head .eyebrow_layout { justify-content: center; }
.enso-collab_head .c-heading p { max-width: 15em; margin-left: auto; margin-right: auto; }
.enso-collab_copy { max-width: 52ch; margin-left: auto; margin-right: auto; }
.enso-collab_cards { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.25rem, 2.5vw, 2rem); width: min(100%, 56rem); margin-top: clamp(2.5rem, 6vh, 4rem); }
/* width:auto overrides .enso-quotebox's own min(66rem,94vw) — the grid
   column sizes these cards, not the index founder-quote rule (found live:
   two 1056px cards side by side in a 896px row) */
/* ROUND 26.1 (client): justify-content space-between — title+copy live in
   their own container at the top, the logo in its own at the BOTTOM LEFT.
   Side by side the grid's default stretch makes both cards the height of
   the taller one, so the two logos sit on one line; stacked (≤767) the
   rows are auto-height and space-between collapses to natural flow —
   exactly "when they're vertical, they can be at different heights". */
.enso-collab_card { position: relative; width: auto; padding: clamp(1.5rem, 3vw, 2.25rem); text-align: left; min-width: 0; display: flex; flex-direction: column; justify-content: space-between; }
/* ROUND 26.1 (client): the outline sat UNDER the blur — .enso-qbox_bg's
   backdrop-filter blurred the drawn edges, corner dots and the resting
   ::before outline behind it (absolute siblings paint in DOM order).
   Lift the line-work above the surface; scoped to these cards only. */
.enso-collab_card .enso-quotebox_edge, .enso-collab_card .enso-quotebox_dot { z-index: 2; }
.enso-collab_card::before { z-index: 1; }
.enso-collab_card .enso-qbox_bg { z-index: 0; }
.enso-collab_card-content { position: relative; z-index: 1; }
.enso-collab_card-title { margin: 0 0 0.6em; }
.enso-collab_card-copy { margin: 0; color: color-mix(in srgb, var(--_theme---text) 82%, transparent); }
.enso-collab_card-logo { position: relative; z-index: 1; margin-top: clamp(1.5rem, 3.5vh, 2.25rem); }
.enso-collab_card-logo img { display: block; height: clamp(1.9rem, 4vh, 2.4rem); width: auto; max-width: 100%; }
@media (max-width: 767px) {
  .enso-collab_cards { grid-template-columns: 1fr; width: min(100%, 30rem); }
  .enso-collab_card-logo img { height: clamp(1.7rem, 5.5vw, 2.1rem); }
}

/* ============ ROUND 28 (client): splash button order when stacked ============
   The Webflow export flips the splash button row to `flex-flow: column` at
   ≤479px (ensolab-yolkk.webflow.css). DOM order is Sound off -> Full
   experience, which reads fine side by side but puts the secondary choice on
   top once they stack. `order` rather than a DOM swap or column-reverse: the
   row order at ≥480px is untouched, and tab order still follows the source. */
@media screen and (max-width: 479px) {
  .unicor_studio.is-hero #start { order: -1; }
  .unicor_studio.is-hero #sound-off { order: 0; }
}
