/* ==========================================================================
   frame.v1.css (bible section 5a)
   Section scaffolding, the ghost index numerals, the top chrome bar (return
   banner + language switcher), the in-flow expo announcement strip (z-index
   2, above page veils; it scrolls away under the bar) and the site footer. The
   drafting-frame layer this file was named for was retired in 2026 and
   removed on 4 August 2026 - see the note below.

   z-index ladder (site-wide): section scaffolding -1 | content auto |
   nav-drawer 85 | .nav 90 | .nav-drop 95 | chrome bar 125 | .skip-link 140.
   ========================================================================== */

/* The drafting-frame motif is GONE (owner decision 2026-06-21: the bordered
   "sheet" read as a gimmick to enterprise buyers). It was hidden site-wide with
   .sheet-frame{display:none} and its whole .sf-* subtree - crosshairs, head/left/
   mid/right rails, the scroll readout, the progress tick and its keyframes - was
   left in place behind that. Removed 4 August 2026 after proving zero render
   sites: no .tsx or .ts references .sheet-frame or any sf-* class, and the
   DraftingFrame.tsx the old comment deferred to does not exist in the repo.
   --frame is KEPT: it is the content edge inset used by .nav, .wrap and .footer.
   The matching initReadout() in reveal.v1.js went with it. */

/* ==========================================================================
   GHOST INDEX NUMERALS (bible 5f) - one per section header, hard limit.
   Markup: <span class="ghost-n" aria-hidden="true">01</span> as the first
   child of a .sheet-section.
   ========================================================================== */
.ghost-n{
  position:absolute;
  /* anchored to the section's own top edge, decoupled from --space-section. The
     old top:max(0px, calc(var(--space-section) - 1.1em)) resolved to 0 at every
     viewport width anyway (1.1em is 176-246px against a 72-96px token, so the
     max() floor always won), which made it look as though the watermark tracked
     the section rhythm when it never did. A literal states what it does, and the
     numeral no longer moves if the rhythm or its own font-size is retuned.
     NOTE: no page currently renders a .ghost-n - the markup was retired from
     every route, so this block styles nothing until it comes back. */
  top:0;
  right:clamp(8px, 4vw, 64px);
  z-index:-1;
  font-family:var(--font);
  font-size:clamp(10rem, 7rem + 8vw, 14rem);
  font-weight:900;
  line-height:1;
  letter-spacing:-.04em;
  font-variant-numeric:tabular-nums;
  color:transparent;
  -webkit-text-stroke:1px rgba(63,63,70,.55);
  pointer-events:none;
  user-select:none;
}
@supports not (-webkit-text-stroke: 1px #000){
  .ghost-n{color:rgba(63,63,70,.28)}
}

/* ==========================================================================
   SECTION SCAFFOLDING
   .sheet-section: standard vertical rhythm only. The exposed baseline grid
   band and full-height column-guide hairlines were removed (owner decision,
   grid scaffolding retired) - see the .sheet-frame removal note above. Below-
   fold rendering is deferred via content-visibility (bible section 4).
   ========================================================================== */
.sheet-section{
  position:relative;
  isolation:isolate;
  padding-block:var(--space-section);
  /* the sticky nav overlays whatever it lands on: it settles at top:42px and is
     64px tall, so the first 106px of the viewport is covered. A stacked
     section's lead-in is now the seam (~25px), well inside that, so an in-page
     jump to #migration / #how-it-works / #try-it would park the heading behind
     the nav. 120px clears it with air to spare. */
  scroll-margin-top:120px;
  content-visibility:auto;
  contain-intrinsic-size:auto 560px;
}

/* ==========================================================================
   COLLAPSE + PRINT
   ========================================================================== */
@media print{
  .ghost-n{display:none}
  .sheet-section{content-visibility:visible;contain-intrinsic-size:none}
}

/* ==========================================================================
   TOP CHROME BAR - the return banner + language switcher, persistent across
   all pages (markup is in the root layout, so the CSS belongs here in the
   shared layer, never in a page-local <style>).

   ONE flex row, not two independently fixed boxes. The switcher used to be a
   separately fixed island parked in the banner's left gutter, which forced the
   banner to reserve a 138px pad and to SHRINK its type (10px at 560, 9px at
   400) to dodge it. As row siblings the two can never overlap at any width, so
   the banner label keeps its 11px register and truncates with an ellipsis
   instead.

   Band height is fixed at 40px at every width (it used to breathe between
   34.9px and 38px as the type shrank). The sticky nav is flush below it at
   top:40px in components.v1.css. The bottom hairline is an inset shadow rather
   than a border so the 40px box is 40px of usable content height, which is what
   gives the <select> its 40px tap target.

   z-index ladder unchanged: bar/banner 125 (above the drafting frame at 110,
   below .skip-link at 140), switcher 126 so the <select> stays hittable.
   ========================================================================== */
.chrome-bar{
  position:fixed;
  top:0;left:0;right:0;
  z-index:125;
  display:flex;align-items:stretch;
  height:40px;
  background:var(--chrome-bg);
  box-shadow:inset 0 -1px 0 var(--hairline);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
}
/* PHONES GET AN OPAQUE BAND, NO BLUR. Owner-reported on an iPhone: page text
   read straight through the top chrome while scrolling. Two causes, one fix.
   The band is 8% transparent, so high-contrast content behind it always ghosts
   through - the nav drawer already carries the same note and is fully opaque for
   exactly this reason. And a blurred position:fixed layer is expensive enough
   that mobile Safari defers recompositing it during momentum scroll, so the band
   lags behind the content it is supposed to be covering. Dropping the blur and
   going opaque removes the ghosting and the lag together. Desktop keeps the
   glass - it does not scroll under a collapsing toolbar. */
/* The strip ABOVE this bar (under the iOS status bar / Dynamic Island) is NOT
   fixable from CSS: Safari composites its own material there and clips fixed-
   element overflow, so an upward opaque cap never paints (probed on the
   simulator, 5 August 2026). What colours that strip is the page's single
   JS-owned theme-color meta - see THEME_BOOT_SCRIPT and syncThemeColorMeta
   in src/lib/theme.ts. */
@media (max-width:700px){
  .chrome-bar{
    background:var(--floor);
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }
}

.return-banner{
  flex:1 1 auto;
  min-width:0; /* required, or the nowrap label refuses to shrink and ellipsis never fires */
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding:0 12px;
  background:transparent;
  color:var(--ink-3);
  /* --t-label, not a literal 11px: this is global chrome and the only label on
     every page, so it follows the shared micro-type tier (12px on phones,
     11px on desktop - unchanged there) rather than pinning the phone floor. */
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;
  text-decoration:none;
  transition:color .2s var(--ease-monument), background .2s var(--ease-monument);
}
.return-banner:hover{color:var(--ink-1);background:var(--chrome-bg-strong)}
/* TRUE viewport centring above the phone boundary (owner report, 13 August
   2026: the label sat left of centre by half the switcher width, because
   flex:1 centres within the REMAINDER next to .lang-switcher). Absolute
   centring with symmetric clearance fixes it; below 701px the flex remainder
   behaviour stays, deliberately - symmetric clearance would spend ~120px of
   the ellipsis budget the de/nl labels need on phones. */
@media (min-width:701px){
  .chrome-bar{justify-content:flex-end}
  .return-banner{
    position:absolute;
    left:50%;top:0;bottom:0;
    transform:translateX(-50%);
    flex:none;
    /* symmetric: budget the switcher's width on BOTH sides, or a long label
       slides under it on one side while claiming to be centred */
    max-width:calc(100% - 340px);
  }
}
.return-banner svg{flex-shrink:0}
/* the label is the only shrinkable part: it truncates, the arrow never does */
.return-banner__label{
  min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* Switcher segment: full band height (40px tap target), divided from the
   banner by the same hairline as the band's own edge. */
.lang-switcher{
  flex:0 0 auto;
  position:relative;z-index:126;
  display:flex;align-items:stretch;
  box-shadow:inset 1px 0 0 var(--hairline);
}
/* two-class specificity throughout: the select also carries .annot for the
   drafting-register typography, and components.v1.css loads AFTER this file,
   so a single-class .lang-switcher-select would lose font-size, letter-spacing
   and colour to .annot on source order */
.lang-switcher .lang-switcher-select{
  height:100%;
  min-height:40px; /* tap target; the band is sized to hold it */
  /* right pad reserves room for the native chevron so the last letter of the
     endonym is never overlapped by the arrow */
  padding:0 24px 0 12px;
  border:0;
  border-radius:0;
  background:transparent;
  /* colour and the native dropdown's own light/dark rendering both come from
     the theme: color-scheme is set per theme on :root in tokens.v1.css */
  color:var(--ink-2);
  /* Desktop register only. On a coarse pointer the 16px iOS auto-zoom floor in
     components.v1.css wins (it is !important): an 11px <select> zoomed the whole
     page on focus and left it zoomed, with the nav clipped off both edges. */
  font-size:11px;
  letter-spacing:.5px;
  cursor:pointer;
  transition:background .2s var(--ease-monument), color .2s var(--ease-monument);
}
.lang-switcher .lang-switcher-select:hover{background:var(--sheen);color:var(--ink-1)}
.lang-switcher .lang-switcher-select:focus-visible{outline:2px solid var(--link);outline-offset:-3px}
.lang-switcher .lang-switcher-select:disabled{cursor:progress;opacity:.6}

/* Narrow widths: recover horizontal room for the label WITHOUT dropping its
   type size. Tighter tracking first, then the decorative arrow. */
@media (max-width:560px){
  .return-banner{letter-spacing:.04em;padding:0 10px}
  .lang-switcher .lang-switcher-select{padding:0 22px 0 10px;letter-spacing:.3px}
}
@media (max-width:420px){
  .return-banner svg{display:none}
  .return-banner{gap:0}
}

/* the whole band is screen chrome; paper gets none of it */
@media print{
  .chrome-bar{display:none}
}

/* ==========================================================================
   EXPO ANNOUNCEMENT STRIP - the site-wide trade-show band (ExpoBanner.tsx,
   rendered by the root layout between .chrome-bar and the page, and only while
   isExpoAnnouncementLive() holds). Global markup, so its CSS lives here.

   IN FLOW, NOT FIXED. The 40px chrome bar, the nav's top:40px and the 106px /
   120px clearances elsewhere are hard-coded, so this band never joins the fixed
   stack: it scrolls away and the nav then sticks under the bar exactly as
   before. What clears the fixed bar today is the nav's own sticky inset - its
   in-flow box starts at y=0 UNDER the bar and top:40px holds it down - so the
   band clears the bar with its own 40px margin (nothing else in flow precedes
   it), and the nav that follows gives that 40px back (margin-bottom:-40px,
   beside the .nav rule in components.v1.css) so content keeps the overlap
   under the nav every hero was composed for. Net effect at scroll 0: bar
   0-40, band 40-80, nav from 80, the page one band lower; after 40px of scroll
   every offset is exactly the pre-band geometry. The phone drawer clears the
   nav's measured bottom (--nav-bottom, published by setDrawer in reveal.v1.js),
   since the nav sits lower than 40px while the band is in view.

   Brand pair in BOTH themes: CI orange band, CI navy ink (5.7:1). --amber and
   --navy are theme-stable in tokens.v1.css, so neither theme remaps them.
   Two copies ride the track and the keyframes move it by -50%, exactly one
   copy (each copy's trailing gap is part of the copy), so the loop is
   seamless; each copy is at least 100vw wide so a wide screen never shows the
   track's empty tail. ExpoBanner sets --expo-marquee-duration from the measured
   track (70px/s); 28s is the pre-hydration fallback. Reduced motion (html.no-
   motion AND the media query, separate rules) renders one centred static copy
   that wraps, with no toggle.
   ========================================================================== */
.expo-strip{
  --expo-band:var(--amber);
  --expo-ink:var(--navy);
  /* z-index 2: above every fixed PAGE veil, below every fixed bar (80+). The
     #glhero field and body::after grain (z 0) and the enquiry page's .eq-veil
     (z 1) are all fixed layers LATER in paint order: at z-index auto they
     painted over the band - computed background #f59c00, composited pixel
     near-black, 1.03:1 measured at 1440 on /privacy, 1.33:1 on the enquiry
     page at z-index 1. Contrast is only true of the composited pixel. */
  position:relative;z-index:2;
  display:flex;align-items:center;
  height:40px;
  margin-top:40px; /* clears the fixed .chrome-bar; see the rationale above */
  background:var(--expo-band);
  color:var(--expo-ink);
  font-family:var(--font);font-size:14px;font-weight:600;
  letter-spacing:.01em;line-height:1.3;
  contain:content;
}

.expo-strip__viewport{
  flex:1 1 auto;
  min-width:0; /* or the max-content track widens the band and the page */
  align-self:stretch;
  overflow:hidden;
  overflow:clip; /* not a scroll container, so focus can never scroll it */
  -webkit-mask-image:linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
          mask-image:linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
}
.expo-strip__track{
  display:flex;
  width:max-content;
  height:100%;
  animation:expo-strip-marquee var(--expo-marquee-duration, 28s) linear infinite;
  will-change:transform;
}
.expo-strip__copy{
  flex:none;
  display:flex;align-items:center;
  min-width:100vw;
  padding-inline:16px 64px;
  white-space:nowrap;
}
@keyframes expo-strip-marquee{
  from{transform:translate3d(0, 0, 0)}
  to{transform:translate3d(-50%, 0, 0)}
}
/* The toggle's pause. Deliberately NOT paused on :focus-within: a tap or click
   focuses the link, and the band then stayed frozen after the visitor came
   back from the new tab while the toggle still offered "Pause" (phone QA,
   Chromium touch). Keyboard focus is covered by the park below. */
.expo-strip[data-paused="true"] .expo-strip__track{animation-play-state:paused}
/* KEYBOARD FOCUS shows the whole sentence, statically. Parking the moving
   track at its start was not enough: wherever the sentence is wider than the
   band it hid the end, stand number included, for as long as the link held
   focus (desktop QA: 175px hidden in en at 820, "stand B45" still hidden in
   nl/de/es/fr at 1024). So focus renders the reduced-motion static frame - one
   centred copy that wraps - and the marquee returns on blur. The band may grow
   while focused; it is in flow. Unlike reduced motion, the actions cluster and
   the toggle stay: they are the next Tab stops, and pausing still means
   something once focus moves on. */
.expo-strip[data-focus-snap="true"]{height:auto;min-height:40px}
.expo-strip[data-focus-snap="true"] .expo-strip__viewport{-webkit-mask-image:none;mask-image:none}
.expo-strip[data-focus-snap="true"] .expo-strip__track{animation:none;width:auto;will-change:auto;justify-content:center}
.expo-strip[data-focus-snap="true"] .expo-strip__copy{flex:0 1 auto;min-width:0;padding:6px 12px;white-space:normal}
.expo-strip[data-focus-snap="true"] .expo-strip__copy[aria-hidden="true"]{display:none}
.expo-strip[data-focus-snap="true"] .expo-strip__link{height:auto;min-height:28px;text-align:center}
/* static-frame finish, shared with reduced motion: the inset ring was drawn
   for the 40px marquee link; around text that wraps it cut the glyphs, so it
   moves 2px out (inside the copy's 6px padding, so nothing clips it). The dot
   beside centred wrapped lines read as a stray bullet. */
.expo-strip[data-focus-snap="true"] .expo-strip__link:focus-visible{outline-offset:2px}
.expo-strip[data-focus-snap="true"] .expo-strip__dot{display:none}
/* phones: beside the pill and toggle the focused sentence was a 6-8 line
   column (121-157px at 390); give it the full width and centre the actions
   under it - they are the next Tab stops, so they stay */
@media (max-width:520px){
  .expo-strip[data-focus-snap="true"]{flex-wrap:wrap}
  .expo-strip[data-focus-snap="true"] .expo-strip__viewport{flex-basis:100%}
  .expo-strip[data-focus-snap="true"] .expo-strip__actions{margin-inline:auto;padding:0 0 4px}
}
/* fine pointers only: a phone tap must not freeze the band */
@media (hover:hover) and (pointer:fine){
  .expo-strip__viewport:hover .expo-strip__track{animation-play-state:paused}
}

.expo-strip__link{
  display:inline-flex;align-items:center;gap:10px;
  height:40px;
  color:inherit;
  text-decoration:none;
}
.expo-strip__link:hover .expo-strip__text{text-decoration:underline;text-underline-offset:3px}
.expo-strip__dot{
  flex:none;
  width:6px;height:6px;
  border-radius:50%;
  background:currentColor;
}
/* the new-tab suffix: part of each link's accessible name, never painted (the
   shared layer has no visually-hidden utility; .sr-only is page-local on
   /photon/pricing and /photon/demo only) */
.expo-strip__sr{
  position:absolute;width:1px;height:1px;
  padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

.expo-strip__actions{
  flex:none;
  display:flex;align-items:center;gap:4px;
  padding-inline-start:8px;
}
.expo-strip__register{
  flex:none;
  display:inline-flex;align-items:center;
  height:36px;
  padding:0 16px;
  border-radius:var(--r-pill);
  background:var(--expo-ink);
  color:#fff;
  font-size:13px;font-weight:600;letter-spacing:.01em;
  white-space:nowrap;
  text-decoration:none;
}
.expo-strip__register:hover{text-decoration:underline;text-underline-offset:3px}
.expo-strip__toggle{
  flex:none;
  display:inline-grid;place-items:center;
  width:40px;height:40px;
  padding:0;
  border:0;border-radius:0;
  background:transparent;
  color:var(--expo-ink);
  cursor:pointer;
}
.expo-strip__toggle:hover{background:rgba(19,48,112,.12)}
.expo-strip__toggle svg{width:16px;height:16px}

/* focus: navy on the orange band; the pill is navy itself, so its ring is an
   inset white (10.9:1+) that the band's paint containment cannot clip */
.expo-strip :focus-visible{outline:2px solid var(--expo-ink);outline-offset:-3px}
.expo-strip .expo-strip__register:focus-visible{outline-color:#fff;outline-offset:-4px}

/* align the actions with the nav's outer edge where the nav is inset */
@media (min-width:701px){
  .expo-strip{padding-inline-end:calc(var(--frame) + 1px)}
}
@media (max-width:520px){
  .expo-strip__actions{gap:2px;padding-inline-start:6px}
  .expo-strip__register{padding:0 12px;font-size:14px}
}
/* below 380px the pill would leave under 140px of marquee in the longer
   locales (nl "Gratis aanmelden" measured 155px, leaving 117px at 320); the
   message link still reaches the event site, which leads with registration */
@media (max-width:379px){
  .expo-strip__register{display:none}
}

/* REDUCED MOTION - one static, centred, wrapping copy; no toggle. The media
   query covers the frames before reveal.v1.js stamps html.no-motion. */
@media (prefers-reduced-motion:reduce){
  .expo-strip{height:auto;min-height:40px}
  .expo-strip__viewport{-webkit-mask-image:none;mask-image:none}
  .expo-strip__track{animation:none;width:auto;will-change:auto;justify-content:center}
  .expo-strip__copy{flex:0 1 auto;min-width:0;padding:6px 12px;white-space:normal}
  .expo-strip__copy[aria-hidden="true"]{display:none}
  .expo-strip__link{height:auto;min-height:28px;text-align:center}
  .expo-strip .expo-strip__link:focus-visible{outline-offset:2px}
  .expo-strip__dot{display:none}
  .expo-strip__toggle{display:none}
}
/* phones: the message alone, full width. Beside the pill the sentence was a
   4-6 line column, and stacked under it the band was 111px in every locale
   (measured at 390); the message link still reaches the exhibitor page. The
   cluster goes whole: the toggle is already hidden, so the pill was all of it. */
@media (prefers-reduced-motion:reduce) and (max-width:520px){
  .expo-strip__actions{display:none}
}
html.no-motion .expo-strip{height:auto;min-height:40px}
html.no-motion .expo-strip__viewport{-webkit-mask-image:none;mask-image:none}
html.no-motion .expo-strip__track{animation:none;width:auto;will-change:auto;justify-content:center}
html.no-motion .expo-strip__copy{flex:0 1 auto;min-width:0;padding:6px 12px;white-space:normal}
html.no-motion .expo-strip__copy[aria-hidden="true"]{display:none}
html.no-motion .expo-strip__link{height:auto;min-height:28px;text-align:center}
html.no-motion .expo-strip__link:focus-visible{outline-offset:2px}
html.no-motion .expo-strip__dot{display:none}
html.no-motion .expo-strip__toggle{display:none}
@media (max-width:520px){
  html.no-motion .expo-strip__actions{display:none}
}

@media print{
  .expo-strip{display:none}
}
