/*
  Bora — landing skeleton styles
  Depends on tokens.css (loaded first). Section-specific rules only —
  shared/base rules stay generic here, per-section rules are scoped by id.
*/

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--surface-ground);
  color: var(--ink-primary);
  font-family: var(--font-montserrat);
}

/* Flex column purely to stop sibling margins collapsing. In normal flow a
   section's bottom margin merges with the next section's top margin and the
   larger of the two wins, so two sections declaring 120px sat 120px apart
   rather than 240 — and the rhythm went inconsistent the moment a section
   used padding for its spacing instead (05→06 read 120, 07→08 read 120 from
   240 declared). Flex items don't collapse margins, so every gap is now
   exactly what the two sections declare.

   Nothing else is being laid out here: body height stays auto so no item is
   ever shrunk, and the fixed-position menu and the dialog aren't flex items
   at all. If the spacing now reads too generous, --section-margin is the
   single knob — don't reintroduce the collapse to compensate. */
body {
  display: flex;
  flex-direction: column;
}

/* ---------- Shared section scaffolding ---------- */

.bora-section {
  position: relative;
  width: 100%;
  padding-block: var(--section-margin);
}

.bora-section .container {
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

/* Vertical trim: box height becomes cap-height to baseline, so an element
   is as tall as its letters actually look instead of as tall as the font's
   line box.

   IMPORTANT — it only works on an element that owns a line box. Put it on
   a flex container and it silently does nothing: the container has flex
   items, not lines. That is why .bora-numbers-value is NOT in this list
   even though it is the Tektur element you'd reach for first — it is
   display:inline-flex, so the trim has to go on the digit/unit inside it.
   Same trap applies to .bora-dev-label and .bora-clients-label below;
   both are flex, both are inert here, both are left in place because
   removing them would change nothing and the next person will assume
   they were forgotten. See "still inert" note at the end of this block.

   .btn is deliberately absent — it carries its own trim in components.css
   so a button has the same metrics wherever it is used. */
.bora-dev-label,
.bora-nav-link,
.bora-clients-label,
.bora-quay-stop-helper,
.bora-numbers-digit,
.bora-numbers-unit,
.bora-numbers-label {
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

/* Still inert, kept honest rather than deleted: .bora-dev-label and
   .bora-clients-label are flex containers, .bora-nav-link is an inline
   <a>, and .btn is blockified into a flex item — none of them own a line
   box, so the rule above is a no-op on all four. Fixing them means moving
   the trim onto their text children, which visibly retightens the nav,
   the client strip and both hero buttons. Not done in this pass because
   the ask was section 04; do it deliberately, not by accident. */

/* dev-only label — remove once real content lands in a section */
.bora-dev-label {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  display: flex;
  align-items: baseline;
  gap: var(--gap-l3);
}
.bora-dev-label .bora-dev-index {
  color: var(--accent-signal);
}
.bora-dev-label .bora-dev-tag {
  color: var(--ink-muted);
  opacity: 0.6;
  font-weight: 400;
}

/* ---------- 01 — Notification strip: Bora turns 10 ---------- */

#section-01 {
  background-color: var(--color-success-500);
  padding-block: var(--gap-l4);
}
#section-01 .container {
  min-height: auto;
  justify-content: center;
}
#section-01 .bora-notification-text {
  margin: 0;
  color: var(--color-neutral-0);
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  text-align: center;
}

/* ==========================================================================
   02 — HERO
   ========================================================================== */

/* .bora-section .container (2 classes) sets align-items:center /
   justify-content:flex-start for the placeholder sections — that's
   wrong for hero's real layouts (it would center-shrink full-width
   columns). Override at matching specificity (.bora-hero .container,
   also 2 classes) rather than reaching for !important. */
/* Hero owns its own top/bottom rhythm via the nav (padding-top) and the
   quote block (padding-top/padding-bottom) — the generic .bora-section
   padding-block would double up with those, so it's zeroed here. */
#section-02 {
  padding-top: 20px;
  padding-bottom: 0;
}

.bora-hero .container {
  align-items: stretch;
  justify-content: flex-start;
  min-height: 0;
}

/* Ground treatment: gradient-two + the crosshair overlay from .bora-ground.
   Scoped with the #id so it wins over .bora-ground's own background rule
   without !important or fighting .bora-ground's specificity elsewhere. */
#section-02.bora-ground {
  background: var(--bg-gradient-two);
  background-color: var(--surface-ground);
  overflow: clip;
}

.bora-hero-nav-inner,
.bora-hero-content,
.bora-clients,
.bora-hero-quote-wrap {
  position: relative;
  z-index: 1;
}

/* ---------- Nav ---------- */

/* The nav bar sits flush against the top of its section.

   This used to be padding-top: var(--padding-l1) — 64px at desktop — and
   modules.html cancelled it with a one-off .spec-bora-hero-nav-inner carrying
   `padding-top: 0 !important`. The result was one page with a 293px header and
   fifty-six with a 357px one. Flush is the version that was wanted, so it is
   the default now and the override class is gone; do not add it back, set the
   padding here.

   The landing page keeps its own, smaller value further down (#section-02) —
   its nav sits over the hero rather than above page content. */
.bora-hero-nav-inner {
  padding-top: 0;
}

.bora-nav {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-l2);
}

.bora-nav-logo {
  display: inline-flex;
  line-height: 0;
  flex-shrink: 0;
}
.bora-nav-logo img {
  display: block;
  height: 48px;
  width: auto;
}

.bora-nav-links {
  display: none; /* hidden below desktop */
  list-style: none;
  margin: 0;
  padding: 0;
  gap: var(--gap-l1);
}

.bora-nav-link {
  width: max-content;
  font-family: var(--font-tektur);
  font-size: var(--text-button-m-link);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
  color: var(--color-primary-300);
  transition: color var(--motion-snap) var(--motion-ease);
}
.bora-nav-link:hover,
.bora-nav-link:focus-visible {
  color: var(--color-neutral-0);
}

.bora-nav-actions {
  flex-shrink: 0;
  gap: 8px;
}

/* mobile (base): hamburger only */
.bora-nav-demo-btn { display: none; }
.bora-nav-menu-btn { display: inline-flex; }
.bora-nav-menu-icon { width: var(--btn-s-icon-size); height: var(--btn-s-icon-size); display: block; }

@media (min-width: 768px) {
  /* tablet: request-demo + hamburger, nav links stay hidden */
  .bora-nav-demo-btn { display: inline-flex; }
}

@media (min-width: 1200px) {
  /* desktop: nav links return, hamburger goes away */
  .bora-nav-links { display: flex; }
  .bora-nav-menu-btn { display: none; }
}

/* ---------- Hero content: text column + visual column ---------- */

.bora-hero-content {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.bora-hero-text-col {
  order: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--gap-l1);
}

.bora-hero-text-group {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}

.bora-hero-h1 {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-light);
  font-size: var(--text-h1);
  line-height: var(--leading-tight);
  color: var(--color-neutral-0);
}

.bora-hero-subtitle {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-subtitle);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

.bora-hero-buttons {
  gap: var(--gap-l3);
  flex-direction: column;
  align-items: stretch;
  width: 100%;
}
.bora-hero-buttons .btn {
  width: 100%;
}

@media (min-width: 768px) {
  .bora-hero-buttons {
    flex-direction: row;
    align-items: center;
    width: auto;
  }
  .bora-hero-buttons .btn {
    width: auto;
  }
}

/* visual column: photo + an oversized blurred glow behind it that
   doesn't add to layout height (absolutely positioned) */
.bora-hero-visual-col {
  order: -1; /* image above text below desktop */
  position: relative;
  display: flex;
  justify-content: center;
  height: max-content;
}

.bora-hero-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  height: 680px;
  filter: blur(90px);
  opacity: 0.7;
  z-index: 0;
  pointer-events: none;
}

.bora-hero-photo {
  position: relative;
  z-index: 1;
  display: block;
  height: 340px;
  width: auto;
  margin-inline: auto;
}

@media (min-width: 768px) {
  .bora-hero-content { margin-top: 20px; margin-bottom: 30px; }
  .bora-hero-glow { height: 1080px; }
  .bora-hero-photo { height: 540px; }
}

@media (min-width: 1200px) {
  .bora-hero .bora-hero-content {
    flex-direction: row;
    align-items: center;
    gap: var(--gap-l1);
    margin-block: 40px;
  }
  .bora-hero-text-col,
  .bora-hero-visual-col {
    flex: 1 1 50%;
    min-width: 0;
  }
  .bora-hero-text-col { order: 0; }
  .bora-hero-visual-col { order: 1; }
  .bora-hero-glow { height: 1280px; }
  .bora-hero-photo { height: 640px; }
}

/* ---------- Clients strip ---------- */

.bora-clients {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
  padding-block: var(--padding-l2);
}

.bora-clients-label {
  margin: 0;
  display: flex;
  flex-direction: row;
  gap: 0.35em;
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-primary-200);
  white-space: nowrap;
}

/* mobile/tablet (base): scrolling marquee */
.bora-clients-track-wrap {
  width: 100%;
  display: flex; /* the two .bora-clients-track blocks sit side by side on one line instead of stacking */
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 5%, #000 95%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 5%, #000 95%, transparent 100%);
}
.bora-clients-track {
  display: flex;
  align-items: center;
  gap: var(--padding-l1);
  width: max-content;
  animation: bora-clients-marquee 24s linear infinite;
}
.bora-client-logo {
  height: 56px;
  width: auto;
  flex-shrink: 0;
}

@keyframes bora-clients-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(calc(-100% - var(--padding-l1))); }
}

@media (prefers-reduced-motion: reduce) {
  .bora-clients-track { animation: none; }
  .bora-clients-track-wrap { overflow-x: auto; }
}

@media (min-width: 768px) {
  /* tablet: label already single-line by default — nothing to change here */
}

@media (min-width: 1200px) {
  .bora-hero .bora-clients {
    flex-direction: row;
    align-items: center;
    gap: var(--gap-l1);
  }
  .bora-clients-label {
    flex-direction: column;
    gap: 0;
  }
  .bora-clients-track-wrap {
    overflow: visible;
    -webkit-mask-image: none;
            mask-image: none;
    width: auto;
  }
  .bora-clients-track {
    animation: none;
    width: auto;
    gap: var(--gap-l1);
  }
  .bora-clients-track[aria-hidden="true"] { display: none; } /* no marquee duplicate needed at desktop */
}

/* ---------- Pull-quote ---------- */

.bora-hero-quote-wrap {
  --quote-mark-size: 24px;
  /* extra space (one mark-height top, one bottom) added on top of the
     normal rhythm so this wrapper's own box — which the fade sizes
     itself against — genuinely includes the marks once they're
     pushed past the blockquote's own height below. */
  padding-top: calc(var(--section-margin) + var(--quote-mark-size));
  padding-bottom: calc(var(--padding-l1) + var(--quote-mark-size));
}

.bora-hero-quote {
  margin: 0;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: var(--gap-l2);
}

.bora-quote-text-group {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}

.bora-quote-mark {
  display: block;
  height: auto;
  flex-shrink: 0;
}
/* flush at the row's own top/bottom edge, then pushed out by exactly
   its own height via transform — visual-only, so it stays part of
   the same single row instead of opening a separate blank band; the
   wrap's padding above reserves the real space this needs. */
.bora-quote-mark--open {
  width: var(--quote-mark-size, 24px);
  align-self: flex-start;
  transform: translateY(-100%);
}
.bora-quote-mark--close {
  width: var(--quote-mark-size, 24px);
  align-self: flex-end;
  transform: translateY(100%);
}

.bora-quote-text {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}

.bora-quote-highlight {
  color: var(--color-primary-200);
}

@media (min-width: 768px) {
  .bora-hero-quote-wrap { --quote-mark-size: 32px; }
}

@media (min-width: 1200px) {
  .bora-hero-quote-wrap { --quote-mark-size: 40px; }
}

/* Fade behind the pull-quote. Spec gives identical top/bottom stops
   (primary/950 to primary/950), which renders as a flat block with no
   visible gradient — treated here as a transparent-to-ground vignette
   instead, matching the fade pattern used elsewhere in this system
   (effect-reticle-grid.css's .bora-ground-vignette-bottom). Flagging
   this interpretation for confirmation.

   Lives inside .bora-hero-quote-wrap (not as a section-wide bottom
   strip) so its height always equals that block's height — sitting
   behind the h4 quote text and its open/close quote marks — instead
   of a fixed slice at the bottom of the whole section. Still full-
   bleed edge to edge via 100vw + centering, cropped by #section-02's
   overflow:clip same as before. z-index:-1 keeps it behind the
   blockquote inside the wrap's own stacking context (the wrap is
   already position:relative/z-index:1, see rule above). */
.bora-hero-fade {
  position: absolute;
  top: 0;
  left: 50%;
  width: 100vw;
  height: 100%;
  transform: translateX(-50%);
  background: linear-gradient(to bottom, rgba(1, 7, 19, 0) 0%, var(--surface-ground) 100%);
  pointer-events: none;
  z-index: -1;
}

/* ==========================================================================
   MENU OVERLAY
   ==========================================================================
   Opened by the nav hamburger, closed by its own X, by Escape, or by
   clicking the scrim. Only reachable below 1200px, where the inline nav
   links are hidden — above that the links are on the bar and the
   hamburger is gone, so the sheet has nothing to add.

   [hidden] does the whole job: it removes the panel from the tab order and
   the accessibility tree, and removing it is also what triggers the entry
   animation below. There is no open/close class. */

/* Visible is the declared state; the entry values live in @starting-style,
   which fires when [hidden] is removed and the element goes from display:none
   to rendered. That is deliberately not a class toggled on the next animation
   frame: tie the sheet's opacity to a frame callback and anything that stops
   frames advancing leaves the menu open but invisible, with focus trapped
   inside it. Nothing here needs script to become visible. */
.bora-menu {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  opacity: 1;
  transition: opacity var(--motion-base) var(--motion-ease);
}
.bora-menu[hidden] { display: none; }

@starting-style {
  .bora-menu { opacity: 0; }
}

/* Full-screen sheet on a flat primary/800 — no bevel and no ground texture.
   At full width a leading-edge cut would have nothing to sit against, and
   the reticle competes with the link list rather than framing it. */
.bora-menu__sheet {
  position: relative;
  width: 100%;
  height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
  padding-block: var(--padding-l1);
  background: var(--color-primary-800);

  transform: none;
  transition: transform var(--motion-base) var(--motion-ease);
}

@starting-style {
  .bora-menu__sheet { transform: translateY(-1.5%); }
}

@media (prefers-reduced-motion: reduce) {
  .bora-menu,
  .bora-menu__sheet { transition: none; }
}

.bora-menu .container {
  min-height: 0;
  display: flex;
  align-items: center;
}

.bora-menu__head {
  justify-content: space-between;
  gap: var(--gap-l2);
}

/* ---------- links ---------- */

.bora-menu__nav {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
}

.bora-menu__link {
  display: flex;
  align-items: baseline;
  gap: var(--gap-l2);
  padding-block: var(--padding-l2);
  border-bottom: 1px solid var(--color-primary-800);

  font-family: var(--font-tektur);
  font-size: var(--text-h4);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
  color: var(--color-neutral-0);

  transition: color var(--motion-snap) var(--motion-ease);
}
.bora-menu__link:first-child { border-top: 1px solid var(--color-primary-800); }
.bora-menu__link:hover,
.bora-menu__link:focus-visible { color: var(--accent-signal); }

/* the index is a wayfinding marker, so it takes accent.signal, not
   accent.action — nothing here is clickable *because* it is coloured */
.bora-menu__index {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  color: var(--accent-signal);
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

/* ---------- actions and footer ---------- */

.bora-menu__actions {
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-l3);
}

.bora-menu__foot {
  margin-top: auto;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--gap-l4);
}
.bora-menu__meta {
  margin: 0;
  font-size: var(--text-helper);
  color: var(--ink-muted);
}
.bora-menu__mail {
  font-family: var(--font-tektur);
  font-size: var(--text-button-s);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
  color: var(--color-neutral-0);
  transition: color var(--motion-snap) var(--motion-ease);
}
.bora-menu__mail:hover,
.bora-menu__mail:focus-visible { color: var(--accent-signal); }

/* scroll lock while the sheet is open */
body.bora-menu-open { overflow: hidden; }

/* ==========================================================================
   03 — EVERY STEP FROM QUAY TO BRIDGE
   ========================================================================== */

/* Spec gives this section's own rhythm as margin, not padding — so the
   flat fill below only paints the content band itself, and the gap
   above/below (where body's own surface-ground already shows through,
   same color) stays free of .bora-ground's reticle overlay. */
/* No .bora-ground here on purpose — this section is a flat primary/950
   fill with no reticle crosshair overlay, unlike the hero. */
#section-03 {
  padding-block: 0;
  margin-block: var(--section-margin);
  background-color: var(--color-primary-950);
}

.bora-quay-inner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: var(--gap-l1);
  min-height: 0;
}

/* ---------- Shared section header: title (col-5) + body (col-7) ----------
   Every section from 03 on opens with this pair, so it lives here once
   rather than per section. .bora-quay-* are kept as aliases so section 03's
   markup didn't have to change. */

.bora-section-head,
.bora-quay-header {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}

.bora-section-head__title,
.bora-quay-title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-light);
  font-size: var(--text-h2);
  line-height: var(--leading-snug);
}

.bora-section-head__text,
.bora-quay-subtitle {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}

@media (min-width: 1200px) {
  /* col-5 / col-7 of a 12-column row. Body sits flush with the row's
     bottom edge (spec: "bottom: 0") — since the h2 is the taller of the
     two at this size, that's matched with align-items on the row rather
     than a per-item offset. */
  .bora-section-head,
  .bora-quay-header {
    flex-direction: row;
    align-items: flex-end;
    gap: var(--grid-gap);
  }
  .bora-section-head__title,
  .bora-quay-title {
    flex: 5 1 0%;
    min-width: 0;
  }
  .bora-section-head__text,
  .bora-quay-subtitle {
    flex: 7 1 0%;
    min-width: 0;
  }

  .spec-bora-section-head {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .spec-bsh__title {
    grid-column: span 6 !important;
    min-width: 0;
  }

  .spec-bsh__text {
    grid-column: span 6 !important;
    min-width: 0;
  }
}

/* ---------- Content row: 4 stops (col-3 each on the 12-col grid) ---------- */

.bora-quay-stops {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l1);
}

.bora-quay-stop {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}

.bora-quay-stop-helper {
  margin: 0;
  font-family: var(--font-tektur);
  /* spec calls for a "helper-2" text size — not yet defined in
     tokens.css, using --text-helper as the closest match; flagging
     for confirmation once the token exists. */
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-primary-200);
}

.bora-quay-stop-content {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l4);
}

.bora-quay-stop-title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}

.bora-quay-stop-text {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-100);
}

@media (min-width: 768px) {
  /* tablet: 2x2 — the 8-col grid doesn't divide evenly into four
     col-3 (12-col) items, so this pairs them up rather than trying
     to map the desktop span literally. */
  .bora-quay-stops {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--grid-gap);
  }
}

@media (min-width: 1200px) {
  .bora-quay-stops {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ==========================================================================
   04 — COMPANY NUMBERS
   ========================================================================== */

/* Spec: bg primary/950, gap padding-l1, margin-block padding-l1.
   Rhythm is margin, not padding, so the fill paints only the content
   band and the gap above/below stays plain ground. */
#section-04 {
  padding-block: 0;
  margin-block: var(--padding-l1);
  background-color: var(--color-primary-950);
}

.bora-numbers-inner {
  display: flex;
  flex-direction: column;
  gap: var(--padding-l1);
  min-height: 0;
}

/* Base Item Styles */
.bora-numbers-item {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
  width: max-content; /* Hug content */
}

/* Item 4: blue fill, no padding — the text sits flush with the plate edges */
.bora-numbers-item--4 {
  background-color: var(--color-primary-500);
  padding: 0;
}

.bora-numbers-value {
  display: inline-flex;
  align-items: baseline;
  line-height: var(--leading-tight);
  font-family: var(--font-tektur);
}

.bora-numbers-digit {
  font-size: var(--text-number-huge);
  font-weight: var(--font-weight-regular);
}

.bora-numbers-unit {
  font-size: var(--text-number-large);
  font-weight: var(--font-weight-regular);
  text-transform: uppercase;
}

.bora-numbers-label {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-subtitle);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}

/* ---------- Mobile (< 768px) ----------
   Scoped with the #id on purpose. `.bora-section .container` (two classes)
   sets display:flex, which outweighs a bare `.bora-numbers-inner` — the
   grid below was being computed and then ignored, so every item stacked
   into its own row at both mobile and tablet. */
@media (max-width: 767px) {
  #section-04 .bora-numbers-inner {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--padding-l1);
  }

  .bora-numbers-row {
    display: contents;
  }

  .bora-numbers-item {
    width: auto;
    min-width: 0; /* let the 1fr tracks shrink; without this the huge
                     Tektur numerals set a min-content wider than the
                     column and the whole section overflowed the viewport. */
  }

  /* Scale the figures to the viewport so a 4-glyph number like "99.9%"
     still fits a half-width column on a 360px phone. */
  .bora-numbers-digit { font-size: clamp(36px, 12vw, 64px); }
  .bora-numbers-unit  { font-size: clamp(24px, 8vw, 48px); }
  .bora-numbers-value { flex-wrap: wrap; }

  .bora-numbers-item--4 { grid-column: 1 / -1; order: 1; }
  .bora-numbers-item--1 { grid-column: span 1; order: 2; }
  .bora-numbers-item--3 { grid-column: span 1; order: 3; }
  .bora-numbers-item--5 { grid-column: span 1; order: 4; }
  .bora-numbers-item--2 { grid-column: span 1; order: 5; }
}

/* ---------- Tablet (768px — 1199px) ---------- */
@media (min-width: 768px) and (max-width: 1199px) {
  #section-04 .bora-numbers-inner {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: var(--padding-l1);
  }

  .bora-numbers-row {
    display: contents;
  }

  .bora-numbers-item {
    width: auto;
  }

  .bora-numbers-item--4 { grid-column: span 3; order: 1; }
  .bora-numbers-item--1 { grid-column: span 3; order: 2; }
  .bora-numbers-item--5 { grid-column: span 2; order: 3; }
  .bora-numbers-item--2 { grid-column: span 2; order: 4; }
  .bora-numbers-item--3 { grid-column: span 2; order: 5; }
}

/* ---------- Desktop (>= 1200px) ---------- */
@media (min-width: 1200px) {
  .bora-numbers-row--1 {
    display: flex;
    justify-content: space-between; /* gap: auto */
    align-items: flex-start;
    width: 100%;
  }

  .bora-numbers-row--2 {
    display: flex;
    gap: var(--gap-l1);
    align-items: stretch;
    width: 100%;
  }

  .bora-numbers-item--4 {
    flex: 1 1 0%; /* width: fill */
  }

  .bora-numbers-item--5 {
    flex: 0 0 auto; /* width: hug */
  }
}

/* #section-04 used to be in this rule too, which overrode the margin-block
   rhythm the spec asks for — it now owns its spacing in its own block above.

   #section-03 is left exactly as it renders today. Note it contradicts its
   own rule further up the file, which sets padding-block:0 / margin-block:
   section-margin and carries a paragraph explaining why margin is the right
   choice there. One of the two is wrong; nobody has said which, so this
   pass changes neither. */
#section-03 {
  padding-block: var(--padding-l1);
  margin-block: 0;
  background-color: var(--color-primary-950);
}
/* ==========================================================================
   Shared scaffolding for sections 05 and up
   ========================================================================== */

/* Rhythm is margin, not padding, so a section's fill paints only its own
   content band and the gap between sections stays plain ground. */
.bora-sec {
  padding-block: 0;
  margin-block: var(--section-margin);
  background-color: var(--color-primary-950);
}
.bora-sec > .container {
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-l1);
}

/* ==========================================================================
   05 — THE WORKING SURFACES
   ========================================================================== */

.bora-surfaces {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}

@media (min-width: 768px) {
  .bora-surfaces { grid-template-columns: repeat(2, 1fr); }
}

/* The plate with its top row switched off: flat top, bevel on the bottom
   right only. Same component as the module cards, different parameters. */
.bora-surface {
  --cut-s-top: 0px;
  --cut-s-bot: var(--cut-l);
  /* notch inherited from :root — responsive, see tokens.css */

  position: relative;
  aspect-ratio: 13 / 9;
  overflow: hidden;
  background-color: var(--color-primary-900);
}

.bora-surface__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--motion-base) var(--motion-ease);
}

/* Desktop only: a primary/900 wash in `color` blend mode, so the photo keeps
   its luminance and only takes the hue. Lifted on hover. Below 1200 the
   photos run untinted — a tap has no hover to reveal the real image. */
.bora-surface__tint {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: none;
  background-color: var(--color-primary-900);
  mix-blend-mode: color;
  transition: opacity var(--motion-base) var(--motion-ease);
}

@media (min-width: 1200px) {
  .bora-surface__tint { display: block; }
  .bora-surface:hover .bora-surface__tint { opacity: 0; }
  .bora-surface:hover .bora-surface__photo { transform: scale(1.04); }
}

/* Mobile: the same reveal, driven by the scroll instead of by a pointer.
   The tint is switched on here (it is desktop-only above) so there is
   something for the centred card to lift, exactly as hover does at 1200px.

   Gated on html.has-inview, which js/inview.js sets: without the script
   there is nothing to take the tint off again, and every photo on the page
   would sit permanently muted. No script, no tint — the untinted mobile
   photos this page has always had. */
@media (max-width: 767px) {
  .has-inview .bora-surface__tint { display: block; }
  .has-inview .bora-surface.is-inview .bora-surface__tint { opacity: 0; }
  .has-inview .bora-surface.is-inview .bora-surface__photo { transform: scale(1.04); }
}

@media (prefers-reduced-motion: reduce) {
  .bora-surface__photo { transition: none; }
  .bora-surface:hover .bora-surface__photo { transform: none; }
  .bora-surface.is-inview .bora-surface__photo { transform: none; }
}

.bora-surface__label {
  position: absolute;
  left: var(--padding-l3);
  bottom: var(--padding-l3);
  z-index: 1;
}

/* tablet and mobile put the label on two lines */
@media (max-width: 1199px) {
  .bora-surface__label .btn__label { white-space: pre-line; text-align: left; }
}

/* ==========================================================================
   06 — OVER 35 MODULES, ONE SYSTEM
   ========================================================================== */

/* Rhythm as padding here, not margin — the opposite of every other section,
   and deliberately so. The others are filled primary/950, the same colour as
   the ground, so whether their spacing sits inside or outside the fill is
   invisible. This one is a gradient: with margin the fill stopped dead at
   the last button and 120px of flat near-black followed, which read as a
   black band rather than a gap. Padding puts that space inside the gradient.

   Consequence worth knowing: neighbouring sections still carry their own
   margins, so the gaps either side of this section are wider than elsewhere
   (their margin plus this padding). That is the trade for a continuous
   fill — tune --section-margin here if it reads as too much air. */
#section-06 {
  position: relative;
  overflow: clip;
  padding-block: var(--section-margin);
  margin-block: 0;
  background: var(--bg-gradient-five);
  background-color: var(--color-primary-950);
}

/* The real animated hex mesh, rendered by js/mesh-waves.js into this canvas.
   Masked so it fades out toward the top, same as the source effect. */
.bora-mesh-block {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.bora-mesh-block canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0) 85%);
          mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0) 85%);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;   mask-size: 100% 100%;
}

/* Flat CSS stand-in, shown only until the canvas is actually drawing —
   mesh-waves.js adds .has-mesh once it is, and never if three.js failed to
   load or WebGL is unavailable. Keeps the section from reading as a bare
   gradient in that case, without ever stacking two meshes. */
#section-06.has-mesh::before { display: none; }

#section-06::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.16;
  background-image:
    repeating-linear-gradient(60deg,  rgba(185,201,242,.5) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(-60deg, rgba(185,201,242,.5) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(0deg,   rgba(185,201,242,.5) 0 1px, transparent 1px 30px);
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0) 85%);
          mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0) 85%);
}
#section-06 > .container { position: relative; z-index: 1; }

.bora-modules {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}

@media (min-width: 768px) {
  .bora-modules { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1200px) {
  .bora-modules { grid-template-columns: repeat(3, 1fr); }
}

.bora-module {
  --cut-step: var(--cut-l);
  --stripe-color: rgba(206, 218, 243, 0.02);
  background-color: var(--color-primary-900);
  /* Colours switch instantly on hover (no lag / no staged repaint); only the
     subtle lift is animated. */
  transition: transform 0.18s ease;
}

.bora-module__content {
  padding-inline: var(--padding-l2);
  padding-top: var(--padding-l1);
  padding-bottom: var(--padding-l2);
  gap: var(--gap-l1);
  height: 100%;
}

.bora-module__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--gap-l2);
}

.bora-module__index {
  font-family: var(--font-tektur);
  font-size: var(--text-number-huge);
  line-height: var(--leading-tight);
  color: var(--color-primary-850);
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

.bora-module__icon {
  width: var(--icon-category-size);
  height: var(--icon-category-size);
  flex-shrink: 0;
  display: block;
}

.spec-bora-module__icon {
  opacity: 0.2;
  width: calc( var(--icon-category-size) / 1.5 );
  height: calc( var(--icon-category-size) / 1.5 );
  flex-shrink: 0;
  display: block;
}

.bora-module__body {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
  margin-top: auto;
  align-items: flex-start;
}

.bora-module__text {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}

.bora-module__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--module-color);
}

.bora-module__desc {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}

/* No margin-top: the section container already puts gap-l1 between its
   children, and adding a matching margin here doubled the space above the
   button to 112px. */
.bora-modules-cta {
  display: flex;
}

/* ---------- "See all" affordance ----------
   A quiet text link that points from a homepage section to its full index
   page. Right-aligned under the grid, Tektur, with a trailing arrow. */
.bora-seemore {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  gap: var(--gap-l4);
  font-family: var(--font-tektur);
  font-size: var(--text-button-s);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
  color: var(--accent-signal);
  transition: gap var(--motion-base) var(--motion-ease), color var(--motion-snap) var(--motion-ease);
}
.bora-seemore::after { content: "\2192"; } /* → */
.bora-seemore:hover,
.bora-seemore:focus-visible { color: var(--color-neutral-0); gap: var(--gap-l3); }

/* ==========================================================================
   Shared link style
   ==========================================================================
   Tektur, uppercase, underlined. Used by the client tabs (07) and the
   footer (13). Distinct from .bora-nav-link, which is the bare bar nav. */

.bora-link {
  font-family: var(--font-tektur);
  font-size: var(--text-button-s);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--accent-signal);
  text-decoration: underline;
  text-underline-offset: 0.3em;
  transition: color var(--motion-snap) var(--motion-ease);
}
.bora-link:hover,
.bora-link:focus-visible { color: var(--color-neutral-0); }

/* ==========================================================================
   07 — IN SERVICE WITH
   ========================================================================== */

.bora-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-l2) var(--gap-l1);
  border: none;
  padding: 0;
}

.bora-tab {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

/* The selected tab is the muted one, not the loud one — it reads as
   "already here" while the others still read as somewhere to go. */
.bora-tab[aria-selected="true"] {
  color: var(--color-neutral-700);
  text-decoration: none;
  cursor: default;
}

.bora-client[hidden] { display: none; }

.bora-client {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
}

.bora-client__top {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}

.bora-client__text {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}

.bora-client__claim {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}

.bora-client__note {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-helper);
  line-height: var(--leading-normal);
  color: var(--color-primary-50);
}

/* tablet/mobile: logo left, CTA pushed right on one line */
.bora-client__aside {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-l3);
}
.bora-client__logo { height: 48px; width: auto; display: block; }

@media (min-width: 1200px) {
  /* col-7 / col-2 spacer / col-3 of the 12-column row */
  .bora-client__top { flex-direction: row; align-items: flex-start; gap: var(--grid-gap); }
  .bora-client__text { flex: 7 1 0%; min-width: 0; }
  .bora-client__spacer { flex: 2 1 0%; min-width: 0; }
  .bora-client__aside {
    flex: 3 1 0%;
    min-width: 0;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
  }
}

.bora-client__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-l2);
}
@media (max-width: 767px) {
  .bora-client__stats { grid-template-columns: 1fr; gap: var(--gap-l1); }
}

/* Landing tab cards, desktop only: fluid spacing between the three figures.

   The base rule is three equal 1fr columns, so each figure sits at the left
   edge of its own third and the air between them is only whatever that
   figure did not use — it does not change with the viewport, and on a wide
   screen the last figure stops well short of the right edge. Here the row
   hugs its content instead and the leftover width is dealt out evenly
   between the items, so the spacing stretches with the card.

   flex rather than `grid-template-columns: repeat(3, auto)` on purpose: a
   flex item may shrink below the `width: max-content` that
   .bora-numbers-item sets and let its label wrap, whereas an auto grid
   track shrinks while the item keeps its max-content width and overflows
   the cell. `gap` stays as the floor the distribution can never fall below.

   1280px, not the usual 1200px desktop breakpoint, and the number is
   measured rather than chosen: the widest panel (Kihnu Veeteed) needs
   1139px for its three figures plus two minimum gaps, and the row is the
   viewport less 2x64px of grid margin — so 1200px leaves it 1072px, one
   figure short. Below this the grid stays, which is the right layout there
   anyway: equal thirds, all three on one line, nothing to distribute. The
   case pages reuse this class inside the narrow col-8 detail body, which
   never has spare width either — they keep the grid at every size.

   nowrap is the guard for the day a figure or label gets longer than the
   ones measured here: the items shrink and a label wraps, rather than one
   figure dropping to a second row while space-between flings the other two
   to opposite edges. */
@media (min-width: 1280px) {
  #section-07 .bora-client__stats {
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-between;
    align-items: flex-start;
  }
}

/* ==========================================================================
   11 — QUESTIONS FROM THE BRIDGE
   ========================================================================== */

/* Two fixed columns rather than CSS multicol: an accordion in multicol
   reflows items between columns every time one opens, so the thing you
   just clicked jumps somewhere else. Questions alternate across the two
   stacks so each column stays roughly the same length. */
.bora-faq {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l2);
  align-items: start;
}
@media (min-width: 768px) {
  .bora-faq { grid-template-columns: repeat(2, 1fr); }
}

.bora-faq__col {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}

.bora-faq__item {
  --cut-step: var(--cut-l);
  --cut-notch: 0px;
  --cut-s-top: var(--cut-step);
  background-color: var(--color-primary-900);
}

/* Centred, not top-aligned: with a one-line question the chevron sat high
   against the text. The row is only ever as tall as its own content — the
   extra height a short question used to get came from .bora-plate's
   min-height, which the smaller mobile cut step now takes care of. */
.bora-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-l3);
  padding: var(--padding-l2);
  cursor: pointer;
  list-style: none;

  font-family: var(--font-montserrat);
  font-size: var(--text-subtitle);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
/* the default disclosure triangle, in both engines */
.bora-faq__q::-webkit-details-marker { display: none; }
.bora-faq__q::marker { content: ""; }

.bora-faq__icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  color: var(--color-primary-300);
  transition: transform var(--motion-base) var(--motion-ease),
              color var(--motion-snap) var(--motion-ease);
}
@media (min-width: 768px)  { .bora-faq__icon { width: 20px; height: 20px; } }
@media (min-width: 1200px) { .bora-faq__icon { width: 32px; height: 32px; } }

.bora-faq__q:hover .bora-faq__icon,
.bora-faq__q:focus-visible .bora-faq__icon { color: var(--status-success-solid); }

.bora-faq__item[open] .bora-faq__icon { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .bora-faq__icon { transition: none; }
}

/* No negative margin-top here. It used to pull the answer up into the
   summary's bottom padding to tighten the gap, which worked only as long as
   the answer shared a plain anonymous box with the question. Once the
   animation moved it inside ::details-content the same -12px started
   overlapping the question outright. The gap is now simply the summary's
   own padding-bottom: slightly wider than the spec's gap-l3, and worth it —
   matching that exactly means shrinking the summary's padding on open,
   which makes the question jump every time you click it. */
.bora-faq__a {
  margin: 0;
  padding: 0 var(--padding-l2) var(--padding-l2);
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}

/* ==========================================================================
   08 — MODERN, FREE OF LEGACY
   ========================================================================== */

/* Each wrapped line justifies on its own, so both rows reach edge to edge
   instead of the last one bunching to the left. */
.bora-tech {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--gap-l3) var(--gap-l2);
}
.bora-tech img {
  height: 80px;
  width: auto;
  display: block;
  flex: 0 0 auto;
}
@media (max-width: 1199px) { .bora-tech img { height: 64px; } }
@media (max-width: 767px)  { .bora-tech img { height: 48px; } }

.bora-benefits {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 1200px) { .bora-benefits { grid-template-columns: repeat(3, 1fr); } }

/* Corner chamfers rather than the slant plate: the notch pulled to zero
   cuts the shape back to top-left plus bottom-right corners. */
.bora-benefit {
  --cut-step: var(--cut-l);
  --cut-notch: 0px;
  --cut-s-top: var(--cut-step);
  background-color: var(--color-primary-900);
}

.bora-benefit__content {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
  padding: var(--padding-l2);
  height: 100%;
}

/* Shaped like a small button but deliberately not one — it is a count
   marker, so no hover, no focus ring, no pointer.

   Sits inside the card's padding like everything else, and stays in normal
   flow so the heading simply starts below it. Two earlier attempts are worth
   not repeating: absolute positioning took it out of flow and the heading ran
   underneath, and negative margins bled it past the padding into the corner. */
.bora-benefit__index {
  align-self: flex-end;
  padding-inline: var(--btn-s-padding-inline);
  padding-block: var(--btn-s-padding-block);
  background-color: var(--color-primary-850);
  color: var(--color-primary-300);
  font-family: var(--font-tektur);
  font-size: var(--text-button-s);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  clip-path: polygon(0 0, 100% 0, 100% 100%, var(--cut-m) 100%, 0 calc(100% - var(--cut-m)));
}

/* Plain stack, top down: plate, heading, body. No margin-top:auto pushing
   the text to the foot of the card — the content reads from the top edge
   like the rest of the page. */
.bora-benefit__body {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}

.bora-benefit__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-benefit__text {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}

/* ==========================================================================
   09 — BUILT ON OPEN PROTOCOLS
   ========================================================================== */

.bora-protocols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 1200px) { .bora-protocols { grid-template-columns: repeat(3, 1fr); } }

/* The one light surface on the page. Stripe colour and alpha are raised to
   suit it — the dark-card values would vanish on primary/100. */
.bora-protocol {
  --cut-step: var(--cut-l);
  --stripe-color: rgba(181, 199, 237, 0.32);
  background-color: var(--color-primary-100);
}

.bora-protocol__content {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
  padding: var(--padding-l2);
  height: 100%;
}

.bora-protocol__eyebrow {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-primary-500);
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

.bora-protocol__body {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
  margin-top: auto;
}
.bora-protocol__text {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-protocol__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-primary-950);
}
.bora-protocol__desc {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-950);
}
.bora-protocol__cta {
  display: flex;
  justify-content: flex-end;
}
.bora-protocol__cta .btn__label { white-space: pre-line; text-align: center; }

/* ==========================================================================
   10 — CERTIFIED, COMPLIANT, AUDITED
   ========================================================================== */

.bora-certs {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l1);
}
@media (min-width: 1200px) { .bora-certs { grid-template-columns: repeat(3, 1fr); gap: var(--grid-gap); } }

.bora-cert {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--gap-l3);
}
.bora-cert img {
  width: 128px;
  height: 128px;
  flex-shrink: 0;
  display: block;
}
.bora-cert p {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}

/* ==========================================================================
   07b — THE TEAM BEHIND BORA
   ========================================================================== */

/* Roles as a plain list, laid out as an auto-fit grid — one column on
   phones, filling to two then four as width allows. The seal echoes the
   deck's check-seal; loaded via <img>, so currentColor won't reach it —
   the signal accent is baked into seal.svg's stroke instead. */
.bora-team {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l2);
}
@media (min-width: 768px)  { .bora-team { grid-template-columns: repeat(2, 1fr); column-gap: var(--grid-gap); } }
@media (min-width: 1200px) { .bora-team { grid-template-columns: repeat(4, 1fr); } }

.bora-team__item {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-l3);
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-neutral-0);
}
.bora-team__seal {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin-top: 0.15em;
}
.bora-team__more {
  margin: var(--gap-l1) 0 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
}

/* ==========================================================================
   10b — SECURITY
   ========================================================================== */

/* Eight dark plate cards, 1 → 2 → 4 columns. Same striped-plate idiom as the
   modules/protocols grids, but on the dark card surface (the protocol cards
   are the page's one light surface; these are not). */
.bora-security {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 768px)  { .bora-security { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .bora-security { grid-template-columns: repeat(4, 1fr); } }

/* Padding, flex column and gap come from the shared .bora-plate__content
   wrapper (which clears the bevel rows) — the card only owns the cut size
   and the fill. */
.bora-security__card {
  --cut-step: var(--cut-m);
  background-color: var(--surface-card);
}
/* Mobile only: double the vertical padding inside the card. The shared plate
   padding is cut-step + padding-l3, sized to clear the bevel rows rather than
   to look right — at 24px on a phone the two lines of text sat almost against
   the top and bottom edges. Doubling keeps the bevel clearance in the sum, so
   the text still cannot land in the notch. Above 768px the cards sit in a two
   or four column grid and are wider than they are tall, where 24px reads
   correctly and more would just stretch them. */
@media (max-width: 767px) {
  .bora-security__card .bora-plate__content {
    padding-block: calc(2 * (var(--cut-step) + var(--padding-l3)));
  }
}
.bora-security__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-subtitle);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
  overflow-wrap: break-word;
}
.bora-security__desc {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

/* ==========================================================================
   12 — BUILT ASHORE IN TALLINN
   ========================================================================== */

/* Rhythm as padding for the same reason as section 06: the fill is a
   gradient, so spacing left outside it shows up as a hard band. */
#section-12 {
  position: relative;
  overflow: clip;
  padding-block: var(--section-margin);
  margin-block: 0;
  background: var(--bg-gradient-four);
  background-color: var(--color-primary-950);
}

#section-12::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: repeating-linear-gradient(
    135deg,
    rgba(206, 218, 243, 0.02) 0px 16px,
    transparent 16px 32px
  );
}
#section-12 > .container { position: relative; z-index: 1; }

.bora-close-actions,
.bora-close-contacts {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l2);
}
@media (min-width: 768px) {
  .bora-close-actions,
  .bora-close-contacts { grid-template-columns: repeat(2, 1fr); gap: var(--grid-gap); }
}

.bora-close-contact {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-close-contact__label {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-primary-200);
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
.bora-close-contact p { margin: 0; font-size: var(--text-body); color: var(--color-neutral-0); }
.bora-close-contact a { color: var(--color-neutral-0); text-decoration: none; }
.bora-close-contact a:hover,
.bora-close-contact a:focus-visible { color: var(--accent-signal); }

/* ==========================================================================
   13 — FOOTER
   ========================================================================== */

#section-13 { margin-block: var(--section-margin); padding-block: 0; }

.bora-footer {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l1);
  align-items: start;
}
@media (min-width: 768px) { .bora-footer { grid-template-columns: repeat(3, 1fr); gap: var(--grid-gap); } }

.bora-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
  height: 100%;
}
.bora-footer__logo { height: 32px; width: auto; display: block; }

.bora-footer__legal {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
  margin-top: auto;
}
/* Deliberately not .bora-link — legal links stay quiet by default and only
   pick up the accent on hover, the inverse of the loud list links. */
.bora-footer__legal a {
  font-family: var(--font-montserrat);
  font-size: var(--text-helper);
  color: var(--color-neutral-0);
  text-decoration: none;
  width: max-content;
  transition: color var(--motion-snap) var(--motion-ease);
}
.bora-footer__legal a:hover,
.bora-footer__legal a:focus-visible { color: var(--accent-action); }
.bora-footer__copy {
  margin: 0;
  font-size: var(--text-helper);
  color: var(--color-primary-300);
}

.bora-footer__list {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
  align-items: flex-start;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* ==========================================================================
   ANCHORS
   ==========================================================================
   Public link targets, separate from the #section-NN ids main.css styles by.
   scroll-margin keeps the heading off the very top edge on arrival. */

.bora-anchor {
  display: block;
  height: 0;
  scroll-margin-top: var(--padding-l1);
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* ==========================================================================
   CONTACT DIALOG
   ==========================================================================
   Native <dialog> opened with showModal(): focus trapping, Escape, inertness
   of the rest of the page and the backdrop are all browser behaviour rather
   than script we would have to write and keep correct. */

.bora-dialog {
  width: min(560px, calc(100vw - 2 * var(--grid-margin)));
  /* The breathing room around the dialog is the smallest gap, not the page's
     64px grid margin: at 64px a side the form ran out of height and scrolled
     on any laptop window under ~890px, which is most of them. This inset only
     comes into play once the window is too short for the form anyway, so it
     buys height exactly where height is the thing in short supply. */
  max-height: calc(100dvh - 2 * var(--gap-l3));
  padding: 0;
  border: none;
  color: var(--ink-primary);
  background: var(--color-primary-800);
  overflow: auto;

  /* the system bevel, top-left and bottom-right */
  clip-path: polygon(
    var(--cut-l) 0,
    100% 0,
    100% calc(100% - var(--cut-l)),
    calc(100% - var(--cut-l)) 100%,
    0 100%,
    0 var(--cut-l)
  );
}
.bora-dialog::backdrop {
  background: rgba(1, 7, 19, 0.72);
}

/* Tight vertical rhythm on purpose: at the section spacing this form ran
   842px against a 772px window and scrolled on an ordinary laptop. Even
   padding all round and gap-l3 between blocks brings it inside the viewport.
   overflow:auto stays as the floor for genuinely short screens — a seven
   field form cannot be made to fit a 500px window, and clipping it would be
   worse than scrolling it. */
.bora-dialog__inner {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
  padding: var(--padding-l2);
}

.bora-dialog__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--gap-l2);
}
.bora-dialog__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-light);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-dialog__lede {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

.bora-form {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
/* row-gap tight, column-gap full: stacked fields want to read as one group,
   side-by-side fields want clear separation */
.bora-form__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l3) var(--gap-l2);
}
@media (min-width: 768px) {
  .bora-form__row--split { grid-template-columns: repeat(2, 1fr); }
}

.bora-field {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l4);
}
.bora-field > span {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-primary-200);
}

.bora-field input,
.bora-field textarea {
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  color: var(--color-neutral-0);
  background: var(--color-primary-900);
  border: 1px solid var(--color-primary-700);
  border-radius: var(--bora-chamfer-hairline);
  padding: var(--padding-l3) var(--gap-l3);
  width: 100%;
  transition: border-color var(--motion-snap) var(--motion-ease);
}
/* Three rows, not six. The message box was the single tallest thing in the
   dialog (166px of the 772px total) and the form scrolled because of it; three
   rows still show the placeholder in full, and resize:vertical is right there
   for anyone who wants to write more. min-height has to come down with the
   rows attribute or it just re-imposes the old height. */
.bora-field textarea { resize: vertical; min-height: 4.5em; }
.bora-field input::placeholder,
.bora-field textarea::placeholder { color: var(--color-primary-300); }
.bora-field input:focus-visible,
.bora-field textarea:focus-visible {
  outline: none;
  border-color: var(--accent-signal);
}
/* only after the browser has decided the value is wrong, not while typing */
.bora-field input:user-invalid,
.bora-field textarea:user-invalid { border-color: var(--status-error-solid); }

/* Shown by dialog.js only when contact.php fails; the mailbox in it is the
   fallback, so the link has to stay readable on the dark dialog. */
.bora-form__error {
  margin: 0;
  font-size: var(--text-helper);
  line-height: var(--leading-normal);
  color: var(--status-error-fg-light);
}
.bora-form__error a { color: inherit; text-decoration: underline; }

.bora-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-l3);
  font-size: var(--text-helper);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}
.bora-consent input {
  flex-shrink: 0;
  margin-top: 0.15em;
  width: 1.1em;
  height: 1.1em;
  accent-color: var(--accent-action);
}

.bora-dialog__done {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-dialog__done p { margin: 0; line-height: var(--leading-normal); }
.bora-dialog__done p:first-child {
  font-size: var(--text-h4);
  color: var(--color-neutral-0);
}
.bora-dialog__done p:last-child { color: var(--color-primary-200); }

[hidden] { display: none !important; }

/* ==========================================================================
   MOTION
   ==========================================================================

   Three rules this layer sticks to, because they are what separates motion
   that reads as alive from motion that reads as jerky:

   1. Only `opacity`, `transform` and `filter` are animated. Those are the
      properties the compositor can run without re-laying-out the page.
      Animating height, top or margin drops everything onto the main thread
      and stutters the moment anything else is happening.

   2. Nothing reserves its effect by hiding content. The scroll reveal is
      built on scroll-driven CSS animation, not a JS observer that adds a
      class — so if the feature is unsupported, or a script fails, or the
      page is printed, the content is simply *there* rather than stuck at
      opacity 0 forever. That failure mode is the reason JS reveals are a
      bad trade on a marketing page.

   3. Everything sits inside prefers-reduced-motion: no-preference. Reduced
      motion gets the finished state immediately, not a shortened animation.
      ========================================================================== */

/* Focus is visible everywhere, and it is not a motion nicety — it is the
   only way a keyboard user can see where they are. Kept outside the
   reduced-motion guard for that reason. */
:where(a, button, summary, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-signal);
  outline-offset: 3px;
}
/* clip-path would slice an outline off at the bevel, so the shaped things
   signal focus with fill instead */
.bora-faq__q:focus-visible {
  outline: none;
  background-color: var(--color-primary-850);
}
.bora-faq__q:focus-visible .bora-faq__icon { color: var(--status-success-solid); }

@media (prefers-reduced-motion: no-preference) {

  /* ---------- Scroll reveal ----------
     Scroll-driven, so it costs no JS and cannot leave anything invisible.
     The range is deliberately short and starts almost immediately: a long
     reveal on a 10 000px page means the reader is always watching something
     arrive, which is the thing that reads as busy rather than alive. */
  @supports (animation-timeline: view()) {
    .bora-section-head,
    .bora-surface,
    .bora-module,
    .bora-benefit,
    .bora-protocol,
    .bora-cert,
    .bora-quay-stop,
    .bora-numbers-item,
    .bora-modules-cta,
    .bora-close-actions,
    .bora-close-contacts,
    .bora-footer > * {
      animation: bora-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }

    /* Stagger across a row by pushing each item's range a little later.
       Offsetting the range is how you get a delay out of a scroll-driven
       animation — animation-delay is time-based and does nothing here. */
    .bora-surfaces > *:nth-child(2),
    .bora-modules > *:nth-child(2),
    .bora-benefits > *:nth-child(2),
    .bora-protocols > *:nth-child(2),
    .bora-certs > *:nth-child(2),
    .bora-quay-stops > *:nth-child(2) { animation-range: entry 10% entry 80%; }

    .bora-surfaces > *:nth-child(3),
    .bora-modules > *:nth-child(3),
    .bora-benefits > *:nth-child(3),
    .bora-protocols > *:nth-child(3),
    .bora-certs > *:nth-child(3),
    .bora-quay-stops > *:nth-child(3) { animation-range: entry 20% entry 90%; }

    .bora-modules > *:nth-child(n+4),
    .bora-quay-stops > *:nth-child(4) { animation-range: entry 30% entry 100%; }
  }

  @keyframes bora-rise {
    from { opacity: 0; transform: translateY(40px); }
    to   { opacity: 1; transform: none; }
  }

  /* ---------- Card hover ----------
     2px, not 8px. Enough to answer the pointer, small enough that a grid of
     six doesn't feel like it is bouncing. */
  .bora-module,
  .bora-benefit,
  .bora-protocol {
    transition: transform var(--motion-base) var(--motion-ease),
                background-color var(--motion-base) var(--motion-ease);
  }
  .bora-module:hover,
  .bora-module.is-inview,
  .bora-benefit:hover,
  .bora-protocol:hover { transform: translateY(-2px); }
  /* Hover: invert the card dark -> light so the big ghost index (which is
     primary-850, i.e. the old hover bg) no longer disappears. On the light
     surface the index lights up in the module's category colour, the body
     text flips dark, and the white-stroked icon is inverted (keeping hue) so
     it stays visible. */
  .bora-module:hover,
  .bora-module.is-inview {
    background-color: var(--color-neutral-0);
    /* light card: the near-white stripe is invisible, so darken it to a faint
       brand tint that reads on white (stripes were "disappearing" on hover). */
    --stripe-color: rgba(8, 69, 194, 0.06);
  }
  .bora-module:hover .bora-module__index,
  .bora-module.is-inview .bora-module__index { color: var(--module-color); }
  .bora-module:hover .bora-module__desc,
  .bora-module.is-inview .bora-module__desc { color: var(--color-primary-950); }
  /* the icon's white strokes vanish on the white card — drop it to ~950 dark
     so the linework stays visible. Instant (no transition) = no lag. */
  .bora-module:hover .bora-module__icon,
  .bora-module:hover .spec-bora-module__icon,
  .bora-module.is-inview .bora-module__icon,
  .bora-module.is-inview .spec-bora-module__icon { filter: brightness(0); }
  /* the whole card being hovered flips its "learn more" outline -> filled
     (shape swap + fill handled in components.css); text goes white on the fill */
  .bora-module:hover .btn--outline,
  .bora-module.is-inview .btn--outline { color: var(--btn-color-text-hover, var(--color-neutral-0)); }
  .bora-benefit:hover { background-color: var(--color-primary-850); }

  /* ---------- Mobile: the lit card follows the scroll, not the finger ----------
     Everything above answers :hover, and .is-inview rides along with it — one
     visual, two triggers, because the class is only ever put on below 768px
     (js/inview.js decides, the stylesheet does not).

     What is left is the tap. A phone has no pointer, so :hover fires on touch
     and then stays on that card until the next tap lands somewhere else,
     which would leave two cards lit at once — the one being scrolled past and
     the one touched a minute ago. So the tap state is put back to rest here.
     Only the card's own properties are restated; the "learn more" button
     keeps its fill on touch, where it reads as ordinary button feedback for
     the length of the tap rather than as a stuck card.

     Gated on html.has-inview so this only applies where the script is
     actually running — without it, mobile keeps the hover it has today
     rather than losing the state altogether. */
  @media (max-width: 767px) {
    /* The card is lit by scrolling past it rather than by a pointer arriving,
       so this happens to every card in turn instead of to one the reader
       chose. That matters for the 300ms the background takes to fade up to
       white: the description flips to its dark hover colour at once (colours
       are not animated, only the background and the lift are), so for those
       300ms dark text sits on a still-dark card and cannot be read. Scrolling
       a column of cards, that reads as text flickering out. Switch the card
       instantly here, the way the colours inside it already switch; the lift
       stays animated. */
    .has-inview .bora-module { transition: transform var(--motion-base) var(--motion-ease); }

    .has-inview .bora-module:hover:not(.is-inview) {
      transform: none;
      background-color: var(--color-primary-900);
      --stripe-color: rgba(206, 218, 243, 0.02);
    }
    .has-inview .bora-module:hover:not(.is-inview) .bora-module__index { color: var(--color-primary-850); }
    .has-inview .bora-module:hover:not(.is-inview) .bora-module__desc  { color: var(--color-neutral-0); }
    .has-inview .bora-module:hover:not(.is-inview) .bora-module__icon,
    .has-inview .bora-module:hover:not(.is-inview) .spec-bora-module__icon { filter: none; }
  }

  /* ---------- Accordion ----------
     Native <details> snaps open. interpolate-size lets the intrinsic height
     be interpolated, which is the only way to animate it without measuring
     in script; allow-discrete carries content-visibility across so the
     answer isn't clipped away mid-transition. */
  @supports (interpolate-size: allow-keywords) {
    :root { interpolate-size: allow-keywords; }

    /* The open state is declared outright — block-size:auto, opacity:1 — and
       only the *entry* values live in @starting-style. That ordering matters
       more than it looks: put the collapsed values in the base rule and the
       answer depends on a transition completing to become readable, so
       anything that stops the transition advancing leaves it permanently
       clipped. This way the resting state is always the visible one and the
       animation is decoration on top of it.

       Nothing is needed for the closed state: <details> already hides its
       own content. The zero height here exists purely as somewhere to
       animate from. */
    .bora-faq__item::details-content {
      overflow: hidden;
      transition:
        block-size var(--motion-base) var(--motion-ease),
        opacity var(--motion-base) var(--motion-ease),
        content-visibility var(--motion-base) allow-discrete;
    }
    .bora-faq__item[open]::details-content {
      block-size: auto;
      opacity: 1;
    }
    @starting-style {
      .bora-faq__item[open]::details-content {
        block-size: 0;
        opacity: 0;
      }
    }
  }

  /* ---------- Tab panels ----------
     A fade on switch, so the eye follows the change instead of being
     teleported to different copy in the same box. */
  /* Same fail-safe shape as the accordion: visible is the declared state,
     the entry values are in @starting-style. Un-hiding a panel takes it from
     display:none to rendered, which is exactly when @starting-style fires —
     so no keyframes and no fill-mode holding it at opacity 0. */
  .bora-client {
    opacity: 1;
    transform: none;
    transition:
      opacity var(--motion-base) var(--motion-ease),
      transform var(--motion-base) var(--motion-ease);
  }
  @starting-style {
    .bora-client { opacity: 0; transform: translateY(6px); }
  }

  /* ---------- Dialog ----------
     @starting-style gives the opening frame; allow-discrete keeps `display`
     and `overlay` animatable so the close transition is allowed to finish
     instead of being cut off the instant the dialog is removed. */
  /* Entry only, for the same reason as above — an open dialog is never left
     depending on a transition to become visible. The exit therefore snaps
     rather than fading, which is a fair trade: a dialog that vanishes a
     frame early is a nothing, a dialog that opens invisible is a dead end. */
  .bora-dialog {
    opacity: 1;
    transform: none;
    transition:
      opacity var(--motion-base) var(--motion-ease),
      transform var(--motion-base) var(--motion-ease);
  }
  @starting-style {
    .bora-dialog[open] { opacity: 0; transform: translateY(8px) scale(0.99); }
  }

  .bora-dialog::backdrop {
    opacity: 1;
    transition: opacity var(--motion-base) var(--motion-ease);
  }
  @starting-style {
    .bora-dialog[open]::backdrop { opacity: 0; }
  }

  /* ---------- Menu links ----------
     The index nudges rather than the label, so the text stays put and stays
     readable while the row still answers the pointer. */
  .bora-menu__index {
    display: inline-block;
    transition: transform var(--motion-base) var(--motion-ease);
  }
  .bora-menu__link:hover .bora-menu__index,
  .bora-menu__link:focus-visible .bora-menu__index { transform: translateX(4px); }

  /* ---------- Photo cards ----------
     Already zoom on hover; this eases the tint out on the same curve so the
     two read as one gesture rather than two. */
  .bora-surface__photo,
  .bora-surface__tint {
    transition-duration: var(--motion-base);
    transition-timing-function: var(--motion-ease);
  }
}

/* ==========================================================================
   MOTION — scroll, part two
   ==========================================================================
   Same three rules as the block above: compositor-only properties, nothing
   depends on an animation completing to become visible, and the whole lot
   sits behind prefers-reduced-motion.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {

    /* ---------- More things that rise in ----------
       Added to the first list rather than replacing it. Kept to blocks that
       arrive as a unit; animating every paragraph makes a long page feel
       like it is constantly loading. */
    .bora-tabs,
    .bora-faq__item,
    .bora-tech,
    .bora-clients-label,
    .bora-hero-quote {
      animation: bora-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }

    /* FAQ items alternate down two columns, so stagger by column position
       rather than by index — otherwise the left column lands in order and
       the right one looks random. */
    .bora-faq__col:last-child .bora-faq__item { animation-range: entry 10% entry 80%; }

    /* ---------- Tech logos ----------
       A short cascade across the wall. Four buckets, not fourteen: past a
       handful of steps the eye stops reading it as one gesture. */
    .bora-tech img {
      animation: bora-pop linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
    .bora-tech img:nth-child(4n+2) { animation-range: entry 8% entry 68%; }
    .bora-tech img:nth-child(4n+3) { animation-range: entry 16% entry 76%; }
    .bora-tech img:nth-child(4n)   { animation-range: entry 24% entry 84%; }

    @keyframes bora-pop {
      from { opacity: 0; transform: translateY(24px) scale(0.94); }
      to   { opacity: 1; transform: none; }
    }

    /* ---------- Big figures ----------
       The Tektur numerals lift a little further and a little later than the
       label under them, so the figure reads as arriving first. */
    .bora-numbers-value {
      animation: bora-rise-far linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 75%;
    }
    @keyframes bora-rise-far {
      from { opacity: 0; transform: translateY(56px); }
      to   { opacity: 1; transform: none; }
    }

    /* ---------- Hero parallax ----------
       Tied to the document scroll rather than to the element's own view
       progress, because the hero starts on screen: a view() timeline would
       already be part-way through at load and the photo would jump.
       Small distances — this is depth, not a ride. */
    .bora-hero-photo {
      animation: bora-drift linear both;
      animation-timeline: scroll(root);
      animation-range: 0 90vh;
    }
    @keyframes bora-drift {
      from { transform: translateY(0); }
      to   { transform: translateY(-28px); }
    }

    .bora-hero-glow {
      animation: bora-glow-drift linear both;
      animation-timeline: scroll(root);
      animation-range: 0 90vh;
    }
    @keyframes bora-glow-drift {
      from { transform: translate(-50%, -50%) scale(1); opacity: 0.7; }
      to   { transform: translate(-50%, -46%) scale(1.06); opacity: 0.45; }
    }

    /* ---------- Section 06 mesh ----------
       The hex mesh drifts up fractionally as the section crosses, which
       gives the WebGL layer somewhere to sit rather than looking pasted on. */
    .bora-mesh-block {
      animation: bora-mesh-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    @keyframes bora-mesh-drift {
      from { transform: translateY(3%); }
      to   { transform: translateY(-3%); }
    }

    /* ---------- Photo cards ----------
       The image drifts against its frame on scroll. The frame is
       overflow:hidden, so it reads as depth through a window rather than as
       the card moving.

       The drift animates `translate` only, and the overscan that gives it
       room to move is a static `scale`. That split matters: the hover zoom
       already uses `transform`, and an animation touching the same property
       would win outright and kill the hover. On separate properties the two
       compose instead of fighting. */
    .bora-surface__photo {
      scale: 1.06;
      animation: bora-photo-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    @keyframes bora-photo-drift {
      from { translate: 0 -2%; }
      to   { translate: 0 2%; }
    }
  }
}

/* ==========================================================================
   INNER PAGES — shared scaffolding
   ==========================================================================
   Used by modules.html, the module detail template, cases.html and blog.html.
   The landing keeps its own hero; these pages open with a plainer masthead
   because they are read, not pitched. */

.bora-masthead {
  padding-block: var(--section-margin) 0;
  margin-block: 0;
}
.bora-masthead > .container {
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-l2);
}

.bora-crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap-l4);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
}
.bora-crumbs a { color: var(--accent-signal); text-decoration: none; }
.bora-crumbs a:hover, .bora-crumbs a:focus-visible { color: var(--color-neutral-0); }
.bora-crumbs li::after { content: "/"; margin-inline-start: var(--gap-l4); color: var(--color-primary-700); }
.bora-crumbs li:last-child::after { content: none; }

.bora-page-title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-light);
  font-size: var(--text-h1);
  /* line-height: var(--leading-tight); */
}
.bora-page-lede {
  margin: 0;
  max-width: 62ch;
  font-size: var(--text-subtitle);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

/* ---------- Category block (modules page) ---------- */

.bora-category {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}
.bora-category__head {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l4);
  padding-bottom: var(--gap-l3);
  border-bottom: 1px solid var(--color-primary-800);
}
.bora-category__name {
  margin: 0;
  font-family: var(--font-tektur);
  font-size: var(--text-h4);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--category-color);
}
.bora-category__note {
  margin: 0;
  max-width: 70ch;
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

.bora-category__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 768px)  { .bora-category__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .bora-category__grid { grid-template-columns: repeat(3, 1fr); } }

/* ---------- Pending-content notice ----------
   Deliberately visible rather than a code comment. The module list is
   incomplete and anyone reading the page — or scraping it — should be able
   to tell, instead of inferring that seven is the whole system. */
.bora-notice {
  --cut-step: var(--cut-m);
  --cut-notch: 0px;
  --cut-s-top: var(--cut-step);
  background-color: var(--color-primary-900);
  border-left: 2px solid var(--status-warning-solid);
}
.bora-notice__inner {
  padding: var(--padding-l2);
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-notice__label {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--status-warning-solid);
}
.bora-notice p { margin: 0; line-height: var(--leading-normal); color: var(--color-primary-100); }

/* ---------- Prose (module detail, blog) ---------- */

.bora-prose {
  max-width: 68ch;
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}
.bora-prose h2 {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-light);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-prose h3 {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-prose p,
.bora-prose li {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
  color: var(--color-primary-100);
}
.bora-prose ul { margin: 0; padding-left: 1.2em; display: flex; flex-direction: column; gap: var(--gap-l4); }
.bora-prose strong { color: var(--color-neutral-0); font-weight: var(--font-weight-medium); }

/* ---------- Card grid + numbered steps + note (how-we-work) ----------

   Generic, used on the how-we-work page. The card grid is the same dark
   striped-plate idiom as the landing's §10b security grid, but named
   generically so it can carry any title+desc set (work process, testing).
   The `--2` modifier caps it at two columns for shorter sets. */
.bora-cardgrid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 768px)  { .bora-cardgrid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .bora-cardgrid { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1200px) { .bora-cardgrid--2 { grid-template-columns: repeat(2, 1fr); } }

/* Padding/flex/gap come from the shared .bora-plate__content wrapper, which
   clears the bevel rows; the card owns only its cut size and fill. */
.bora-cardgrid__card {
  --cut-step: var(--cut-m);
  background-color: var(--surface-card);
}
.bora-cardgrid__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-subtitle);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
  overflow-wrap: break-word;
}
.bora-cardgrid__desc {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

/* Work-process cards — a deliberately different treatment from the striped
   Testing/Security plates: same signature bevel, but a lighter panel fill,
   no stripes, and a short accent bar at the top of each card. */
.bora-workproc {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 768px)  { .bora-workproc { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .bora-workproc { grid-template-columns: repeat(4, 1fr); } }

.bora-workproc__item {
  --cut-step: var(--cut-m);
  background-color: var(--surface-panel);
}
.bora-workproc__bar {
  display: block;
  width: 36px;
  height: 3px;
  background-color: var(--accent-signal);
}
.bora-workproc__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-subtitle);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
  overflow-wrap: break-word;
}
.bora-workproc__desc {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

/* Numbered process steps — two columns on wide screens, filled row by row.
   The blue square badge echoes the deck's numbered process slide and the
   nav's index numerals. */
.bora-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-l1);
}
@media (min-width: 768px) { .bora-steps { grid-template-columns: repeat(2, 1fr); column-gap: var(--grid-gap); } }

.bora-step {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-l3);
}
.bora-step__num {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  color: var(--color-neutral-0);
  background-color: var(--accent-action);
  border-radius: 4px;
}
.bora-step__body { display: flex; flex-direction: column; gap: var(--gap-l4); }
.bora-step__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-body);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-step__desc {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-200);
}

/* Note / callout — a bevelled panel for a single highlighted line, echoing
   the deck's "NOTE:" band on the project-plan slide. */
.bora-note {
  --cut-step: var(--cut-m);
  background-color: var(--surface-panel);
  padding: var(--padding-l2);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut-step)), calc(100% - var(--cut-step)) 100%, 0 100%);
  margin-bottom: var(--grid-gap);
}
.bora-note p {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-100);
}
.bora-note strong { color: var(--accent-signal); font-weight: var(--font-weight-medium); }

/* ---------- Card list (cases, blog) ---------- */

.bora-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
@media (min-width: 768px) { .bora-list { grid-template-columns: repeat(2, 1fr); } }

.bora-entry {
  --cut-step: var(--cut-l);
  background-color: var(--color-primary-900);
  text-decoration: none;
  display: block;
  transition: background-color var(--motion-base) var(--motion-ease);
}
.bora-entry:hover { background-color: var(--color-primary-850); }
.bora-entry__inner {
  padding-block: calc(var(--cut-step) + var(--padding-l3));
  padding-inline: var(--padding-l2);
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-entry__meta {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent-signal);
}
.bora-entry__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-entry__text {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-100);
}

/* ==========================================================================
   MODULE DETAIL PAGE
   ==========================================================================
   Structure per bora-module-pages.md. Category colour stops at the grid, so
   nothing here carries one — same dark ground, white ink, Tektur labels as
   every other page. Reuses .bora-prose, .bora-masthead, the FAQ accordion
   and the close block; only the section label and the two module-specific
   layouts (how-it-works steps, limits) are new. */

/* Restructured to match the approved Figma detail page: each section is two
   columns — a large light-weight display heading on the left, the body on the
   right. Not the small Tektur label used before. Category colour still stops
   at the grid; nothing here carries one. */

.bora-detail-row {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}
.bora-detail-heading {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-light);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}

@media (min-width: 1200px) {
  /* col-4 heading / col-8 body of the 12-column row, top-aligned so a short
     heading sits level with the first line of body, as in the reference.
     Targeted as `.bora-detail > .container` (two classes) so it can beat the
     `.bora-sec > .container` column rule these sections also match — a bare
     `.bora-detail-row` (one class) loses that specificity fight. */
  .bora-detail > .container {
    flex-direction: row;
    align-items: flex-start;
    gap: var(--grid-gap);
  }
  .bora-detail-row__head { flex: 4 1 0%; min-width: 0; }
  .bora-detail-row__body { flex: 8 1 0%; min-width: 0; }
}

/* ---------- How it works: plain sub-moments, not cards ----------
   Sub-heading + paragraph stacked in the body column, matching the reference
   (At the ramp / On board / At departure / For crew & contractors). */
.bora-substeps {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
}
.bora-substep {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l4);
}
.bora-substep__title {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-substep__text {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-100);
}

/* ---------- Frequently asked: plain Q + A, not an accordion ----------
   The approved detail page shows every answer open, as running Q/A in the
   body column. (This deviates from bora-module-pages.md §3, which specified
   the site-wide accordion; the Figma the user pointed at is plainer, and it
   wins. Flagged in the re-audit.) */
.bora-qa-list {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
}
.bora-qa {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l4);
}
.bora-qa__q {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-subtitle);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-qa__a {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--color-primary-100);
}

/* ---------- Limits: scope honesty ----------
   Redesigned: the old version was a bordered box with default round bullets —
   a bad, generic combination. Now each limit is a clean row with a small
   Tektur "✕" marker in muted ink: reads as a considered list of "nos", no
   rail, no discs. Overrides the .bora-prose ul padding/bullets. */
/* .bora-prose .bora-limits (two classes) so it beats `.bora-prose ul`'s
   padding-left and default bullets. */
.bora-prose .bora-limits {
  list-style: none;
  margin: 0;
  padding: 0;
  border: none;
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-limits li {
  display: grid;
  grid-template-columns: 1.4em 1fr;
  align-items: baseline;
  gap: var(--gap-l3);
  color: var(--color-primary-100);
}
.bora-limits li::before {
  content: "\00D7"; /* × */
  font-family: var(--font-tektur);
  font-size: 0.9em;
  color: var(--color-primary-400);
}

/* ==========================================================================
   INNER-PAGE GROUND
   ==========================================================================
   REDESIGNED. The first pass put the marketing hero glow (bg-gradient-two)
   across the whole content page AND kept the nav's own bg-gradient-one on
   top — two radial smears stacked behind text, with no focal point. A
   content page is a document, not a pitch: it wants a calm, flat ground with
   structure drawn by the reticle grid and hairlines, and at most one
   restrained, contained glow behind the masthead. Linear docs / Stripe API
   reference, not a landing hero. */

body.bora-inner {
  background-color: var(--surface-ground); /* flat — no page-wide gradient */
}
/* reticle grid, fixed, one field behind the whole page. Dropped to a lower
   opacity than the landing's hero so it reads as quiet graph paper. */
body.bora-inner::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.14;
  mix-blend-mode: plus-lighter;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='45' height='45' viewBox='0 0 45 45'%3E%3Crect y='14.7' width='30' height='.6' fill='%23fff' fill-opacity='.16'/%3E%3Crect x='14.7' width='.6' height='30' fill='%23fff' fill-opacity='.16'/%3E%3Crect x='14' y='10' width='2' height='10' fill='%23242424'/%3E%3Crect x='10' y='14' width='10' height='2' fill='%23242424'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 45px 45px;
}
/* Suppress the nav's marketing glow and its own reticle on content pages —
   the body already carries the grid, and the double glow was half the mess. */
body.bora-inner .bora-hero.bora-ground {
  background: none;
  background-color: transparent;
  overflow: visible;
}
body.bora-inner .bora-hero.bora-ground::before { display: none; }

/* ---------- Charted rhythm ----------
   The reference reads as a stack of plated bands, not sections floating in
   whitespace: a hairline separates the hero from the body and each section
   from the next, with even padding rather than large collapsing margins.
   This is what gives the detail pages the "charted / plated" character
   (bora-brand §10) the flat-margin version was missing. */
body.bora-inner .bora-masthead > .container {
  padding-bottom: var(--gap-l1);
}
body.bora-inner .bora-detail {
  margin-block: 0;
}
body.bora-inner .bora-detail > .container {
  padding-block: var(--padding-l1);
  border-top: 1px solid var(--color-primary-850);
}
/* the close block keeps its own breathing room and takes no rule — it is the
   page's outward turn, not another read-section */
body.bora-inner .bora-detail + .bora-sec:not(.bora-detail) {
  margin-top: 0;
}
/* every section already carries its own z-index:0/1 stacking where needed;
   lift real content above the two fixed layers */
body.bora-inner > * { position: relative; z-index: 1; }
/* the inner pages' own sections are transparent so the body ground shows
   through, instead of the flat primary/950 fill .bora-sec paints */
body.bora-inner .bora-sec,
body.bora-inner .bora-masthead { background-color: transparent; }

/* ---------- Two-office contact block (Europe + North America + Contact) ----
   The reference close carries both offices. Widen the close grid to three. */
@media (min-width: 768px) {
  .bora-close-contacts--three { grid-template-columns: repeat(3, 1fr); }
}

/* ==========================================================================
   MASTHEAD — redesigned as a real component
   ==========================================================================
   Was: crumb + big H1 + lede floating on a gradient smear. Now: a two-column
   header with an eyebrow tag, title, lede and a backlink on the left, and a
   brand-DNA graphic (a "schematic plate") on the right — so the top of every
   inner page has a focal point and reads as considered, not templated. */

.bora-masthead { position: relative; overflow: clip; }
/* one contained, low glow behind the header — accent, not a page-wide wash */
.bora-masthead::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(62% 120% at 12% -10%, rgba(8, 69, 194, 0.20), transparent 68%);
}
.bora-masthead > .container { position: relative; z-index: 1; }

.bora-masthead__grid {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
}
.bora-masthead__text {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}
@media (min-width: 1200px) {
  .bora-masthead__grid { flex-direction: row; align-items: center; gap: var(--grid-gap); }
  .bora-masthead__text { flex: 7 1 0%; min-width: 0; }
  .bora-masthead__art  { flex: 5 1 0%; min-width: 0; }
}

/* eyebrow — a small Tektur tag over the title, with a leading tick, naming
   what kind of page this is (MODULE · PLATFORM, CASE · BALTIC, …) */
.bora-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-l4);
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent-signal);
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
.bora-eyebrow::before {
  content: "";
  width: 18px;
  height: 2px;
  background: var(--accent-signal);
}

.bora-backlink {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--gap-l4);
  font-family: var(--font-tektur);
  font-size: var(--text-button-s);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
  color: var(--ink-muted);
  transition: gap var(--motion-base) var(--motion-ease), color var(--motion-snap) var(--motion-ease);
}
.bora-backlink::before { content: "\2190"; } /* ← */
.bora-backlink:hover, .bora-backlink:focus-visible { color: var(--color-neutral-0); gap: var(--gap-l3); }

/* ---------- Schematic plate: the masthead graphic ----------
   A NEW brand element. A chamfered plate carrying the module/section glyph,
   a large ghost index numeral, a reticle field and a category (or accent)
   tag — built entirely from the existing brand language. Candidate for the
   library. Colour comes from --module-color (defaults to accent). */
.bora-artplate {
  --module-color: var(--accent-signal);
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background-color: var(--color-primary-900);
  display: grid;
  place-items: center;
}
.bora-artplate::before { /* internal reticle, tighter than the page grid */
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  opacity: 0.5;
  background-image:
    repeating-linear-gradient(90deg, rgba(160,192,253,.10) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(0deg,  rgba(160,192,253,.10) 0 1px, transparent 1px 32px);
}
.bora-artplate::after { /* a faint category wash from the corner */
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(80% 80% at 85% 15%, color-mix(in srgb, var(--module-color) 22%, transparent), transparent 70%);
}
.bora-artplate__icon { position: relative; z-index: 1; width: 42%; height: auto; }
.bora-artplate__index {
  position: absolute; top: var(--padding-l3); left: var(--padding-l3); z-index: 1;
  font-family: var(--font-tektur);
  font-size: var(--text-number-large);
  line-height: 1;
  color: var(--color-primary-850);
  text-box-trim: trim-both; text-box-edge: cap alphabetic;
}
.bora-artplate__tag {
  position: absolute; bottom: var(--padding-l3); left: var(--padding-l3); z-index: 1;
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--module-color);
}
.bora-artplate__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.bora-artplate__logo  { position: relative; z-index: 1; width: 58%; max-height: 40%; object-fit: contain; }
/* integration glyph drawn inline; keep strokes crisp */
.bora-artplate svg.bora-artplate__glyph { position: relative; z-index: 1; width: 46%; height: auto; }

/* ---------- Section index numeral ----------
   A large ghost Tektur numeral above each detail-row heading — the module-card
   index device, reused to give the sparse left column an instrument feel and
   tie the pages to the brand. */
.bora-detail__num {
  display: block;
  font-family: var(--font-tektur);
  font-size: var(--text-number-large);
  line-height: 1;
  color: var(--color-primary-800);
  margin-bottom: var(--gap-l3);
  text-box-trim: trim-both; text-box-edge: cap alphabetic;
}

/* ---------- Spec strip: an instrument readout ----------
   A NEW brand element. A row of Tektur key / value cells behind a hairline
   grid — a module's "at a glance" facts (category, APIs, offline, compliance).
   Reads like a spec plate on real hardware. Candidate for the library. */
.bora-spec {
  display: grid;
  grid-template-columns: 1fr;
  border: 1px solid var(--color-primary-800);
  background-color: var(--color-primary-900);
}
@media (min-width: 600px)  { .bora-spec { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .bora-spec { grid-template-columns: repeat(4, 1fr); } }
.bora-spec__cell {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l4);
  padding: var(--padding-l3) var(--padding-l2);
  border-top: 1px solid var(--color-primary-800);
}
.bora-spec__cell:first-child { border-top: none; }
@media (min-width: 600px) {
  .bora-spec__cell { border-top: none; border-left: 1px solid var(--color-primary-800); }
  .bora-spec__cell:nth-child(-n+2) { border-top: none; }
  .bora-spec__cell:nth-child(odd) { border-left: none; }
}
@media (min-width: 1000px) {
  .bora-spec__cell { border-left: 1px solid var(--color-primary-800); }
  .bora-spec__cell:first-child { border-left: none; }
  .bora-spec__cell:nth-child(odd) { border-left: 1px solid var(--color-primary-800); }
  .bora-spec__cell:nth-child(4n+1) { border-left: none; }
}
.bora-spec__k {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-muted);
}
.bora-spec__v {
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  color: var(--color-neutral-0);
}
/* Sourced-facts caption under the spec grid — carries the confirmed figures,
   the entity name and the outbound authoritative link (SEO/GEO fact density
   and trust signals). Reads as a quiet footnote, not a second panel. */
.bora-spec__src {
  margin: var(--gap-l3) 0 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-helper);
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 68ch;
}
.bora-spec__src strong { color: var(--color-neutral-0); font-weight: 500; }

/* FAQ question inside a documentation section — a question-form subheading
   (GEO), styled a step down from the section h2. */
.bora-doc-q {
  font-family: var(--font-montserrat);
  font-weight: 500;
  font-size: var(--text-body-large);
  color: var(--color-neutral-0);
  margin: var(--gap-l2) 0 var(--gap-l4);
}
.bora-doc-q:first-child { margin-top: 0; }

/* Related-reading line under a blog entry — quiet internal links. */
.bora-entry__more { margin: var(--gap-l3) 0 0; font-size: var(--text-helper); color: var(--ink-muted); }

/* Case-page stat row. The section-04 hero scale (number-huge, 160px) is far
   too wide for a three-up row inside the col-8 detail body — it overflowed
   the page. Step it down to number-large here, and let the grid cells shrink
   (min-width:0) so a wide value can never push past the column. */
body.bora-inner .bora-client__stats { min-width: 0; }
/* min-width:0 alone was not enough: .bora-numbers-item is width:max-content,
   which pins the item to the width of its longest unbroken line and lets it
   hang out of the 1fr cell however narrow the column gets. On the landing
   page there is room for that; in the col-8 detail body there is not, and a
   long label ("Estonian islands inter-connected") pushed the row past the
   column and gave the whole page a horizontal scrollbar. width:auto hands the
   cell width back to the grid and lets the label wrap instead. */
body.bora-inner .bora-client__stats .bora-numbers-item { min-width: 0; width: auto; }
body.bora-inner .bora-client__stats .bora-numbers-digit { font-size: var(--text-number-large); }
body.bora-inner .bora-client__stats .bora-numbers-unit { font-size: var(--text-h3); }

/* ==========================================================================
   ARCHITECTURE DIAGRAM  (bora-components.md §3d)
   ==========================================================================
   The confirmed real "many inputs → one system → many outputs" figure from
   new-high-level-app-architecture.html, rebuilt on Bora tokens: channels in,
   an accent API-gateway funnel narrowing into a light-surface module
   registry, then an outlet fanning back out to data & integrations. A reusable
   brand component — a library candidate. Structurally accurate, not decoration
   (bora-visuals §2). */

.bora-arch {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
  max-width: 100%;
}
/* Grid/flex children must be allowed to shrink below their content width, or
   the 6- and 7-up rows and the funnel push the diagram past the container. */
.bora-arch__row, .bora-arch__table, .bora-arch__gateway { min-width: 0; }
.bora-arch__cell, .bora-arch__mod { min-width: 0; overflow-wrap: anywhere; }
.bora-arch__label {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-align: center;
  color: var(--color-neutral-0);
}

/* ---- channels / data: glass cells ---- */
.bora-arch__row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap-l3);
}
@media (min-width: 600px)  { .bora-arch__row { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) { .bora-arch__row--6 { grid-template-columns: repeat(6, 1fr); }
                             .bora-arch__row--5 { grid-template-columns: repeat(5, 1fr); } }

.bora-arch__cell {
  min-height: 96px;
  padding: var(--padding-l3) var(--padding-l2);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gap-l4);
  text-align: center;
  background: rgba(1, 7, 19, 0.32);
  border: 1px solid var(--color-primary-700);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.bora-arch__cell h3 {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-helper);
  color: var(--color-primary-200);
}
.bora-arch__cell p {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: 10px;
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
/* data cells take the a/b/c chamfer rotation, like the report card family */
.bora-arch__cell--a { clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px)); }
.bora-arch__cell--b { clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 12px 100%, 0 calc(100% - 12px)); }
.bora-arch__cell--c { clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px); }
.bora-arch__cell--a, .bora-arch__cell--b, .bora-arch__cell--c { background: var(--color-primary-800); border: none; }

/* ---- funnel → registry → outlet ---- */
.bora-arch__gateway { display: flex; flex-direction: column; }

.bora-arch__funnelhead {
  padding-top: var(--gap-l3);
  padding-bottom: var(--gap-l4);
  text-align: center;
  background: var(--accent-action);
  font-family: var(--font-tektur);
  font-size: var(--text-button-m-link);
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-neutral-0);
}
.bora-arch__shoulders { display: flex; height: 16px; }
.bora-arch__shoulders .sp { flex: 1; }
.bora-arch__shoulders .stem { flex: 0 1 400px; min-width: 0; background: var(--accent-action); }
.bora-arch__shoulders .cut { flex: none; width: 16px; height: 16px; background: var(--accent-action); }
.bora-arch__shoulders .cut.l { clip-path: polygon(0 0, 100% 0, 100% 100%); }
.bora-arch__shoulders .cut.r { clip-path: polygon(0 0, 100% 0, 0 100%); }

.bora-arch__mouth { display: flex; height: 16px; margin-top: -1px; }
.bora-arch__mouth .fill { flex: 1; background: var(--color-primary-100); }
/* shrinkable like the stem, so the funnel scales down instead of the fixed
   void overflowing a narrow column */
.bora-arch__mouth .void { flex: 0 1 404px; min-width: 0; }
.bora-arch__mouth .tri { flex: none; width: 16px; height: 16px; }
.bora-arch__mouth .tri.bl { background: linear-gradient(to bottom left,  transparent 49.6%, var(--color-primary-100) 50%); }
.bora-arch__mouth .tri.br { background: linear-gradient(to bottom right, transparent 49.6%, var(--color-primary-100) 50%); }

.bora-arch__registry {
  background: var(--color-primary-100);
  padding: var(--padding-l2);
  display: flex;
  flex-direction: column;
  gap: var(--gap-l3);
}
.bora-arch__registry h3 {
  margin: 0;
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-align: center;
  color: var(--ink-on-light);
}
.bora-arch__table {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap-l4);
}
@media (min-width: 600px)  { .bora-arch__table { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1000px) { .bora-arch__table { grid-template-columns: repeat(7, 1fr); } }
.bora-arch__mod {
  min-height: 64px;
  padding: var(--padding-l3);
  border-radius: var(--bora-chamfer-hairline);
  background: var(--color-neutral-0);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: var(--font-montserrat);
  font-size: var(--text-helper);
  line-height: var(--leading-snug);
  color: var(--ink-on-light);
}

.bora-arch__outlet { display: flex; height: 16px; }
.bora-arch__outlet .sp { flex: 1; }
.bora-arch__outlet .stem { flex: 0 1 400px; min-width: 0; background: var(--color-primary-100); }
.bora-arch__outlet .tri { flex: none; width: 16px; height: 16px; background: var(--color-primary-100); }
.bora-arch__outlet .tri.l { clip-path: polygon(0 0, 100% 0, 100% 100%); }
.bora-arch__outlet .tri.r { clip-path: polygon(0 0, 100% 0, 0 100%); }

.bora-arch__flow { display: flex; justify-content: center; gap: var(--gap-l4); color: var(--color-neutral-0); }
.bora-arch__flow svg { display: block; }

/* ==========================================================================
   DOCUMENTATION ARCHETYPE
   ==========================================================================
   A deliberately distinct layout so a reader knows instantly they are in the
   technical docs, not on a marketing page: a sticky "on this page" rail, a
   monospace code aesthetic, method badges and parameter tables. Stripe /
   Twilio / Linear docs, in Bora's language. Marked on <body class="bora-docs">. */

.bora-docs { --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace; }

/* the doc shell: sticky TOC rail + main column */
.bora-doc {
  display: flex;
  flex-direction: column;
  gap: var(--gap-l1);
}
@media (min-width: 1000px) {
  .bora-doc { flex-direction: row; align-items: flex-start; gap: var(--grid-gap); }
  .bora-toc { position: sticky; top: 84px; flex: 0 0 208px; }
  .bora-doc__main { flex: 1 1 0%; min-width: 0; }
}

.bora-toc__title {
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
  margin-bottom: var(--gap-l3);
}
.bora-toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.bora-toc a {
  display: block;
  padding: var(--gap-l4) 0 var(--gap-l4) var(--gap-l3);
  border-left: 2px solid var(--color-primary-800);
  font-size: var(--text-helper);
  color: var(--color-primary-300);
  text-decoration: none;
  transition: color var(--motion-snap) var(--motion-ease), border-color var(--motion-snap) var(--motion-ease);
}
.bora-toc a:hover, .bora-toc a.is-active {
  color: var(--color-neutral-0);
  border-left-color: var(--accent-signal);
}

.bora-doc__main { display: flex; flex-direction: column; }
.bora-doc-section {
  scroll-margin-top: 84px;
  padding-block: var(--padding-l1);
  border-top: 1px solid var(--color-primary-850);
  display: flex;
  flex-direction: column;
  gap: var(--gap-l2);
}
.bora-doc-section:first-child { border-top: none; padding-top: 0; }
.bora-doc-section > h2 {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
  scroll-margin-top: 84px;
}
.bora-doc-section > h3 {
  margin: 0;
  font-family: var(--font-montserrat);
  font-weight: var(--font-weight-medium);
  font-size: var(--text-h4);
  color: var(--color-neutral-0);
}
.bora-doc-section p, .bora-doc-section li {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
  color: var(--color-primary-100);
}
.bora-doc-section ul { margin: 0; padding-left: 1.2em; display: flex; flex-direction: column; gap: var(--gap-l4); }
.bora-doc-section strong { color: var(--color-neutral-0); font-weight: var(--font-weight-medium); }
.bora-doc-section code:not([class]) {
  font-family: var(--font-mono);
  font-size: 0.9em;
  color: var(--accent-signal);
  background: var(--color-primary-900);
  padding: 0.1em 0.4em;
  border-radius: var(--bora-chamfer-hairline);
}

/* endpoint header: method badge + path */
.bora-endpoint {
  display: flex;
  align-items: center;
  gap: var(--gap-l3);
  padding: var(--padding-l3) var(--padding-l2);
  background: var(--color-primary-900);
  border: 1px solid var(--color-primary-800);
  clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
  font-family: var(--font-mono);
  font-size: var(--text-body);
  color: var(--color-neutral-0);
  overflow-x: auto;
}
.bora-method {
  flex: none;
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  letter-spacing: 0.06em;
  padding: 0.25em 0.6em;
  color: var(--color-neutral-0);
}
.bora-method--get   { background: var(--color-info-700); }
.bora-method--post  { background: var(--color-success-700); }
.bora-method--tool  { background: var(--color-primary-600); }

/* code block */
.bora-code {
  background: var(--color-primary-950);
  border: 1px solid var(--color-primary-800);
}
.bora-code__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--gap-l4) var(--gap-l3);
  border-bottom: 1px solid var(--color-primary-800);
  font-family: var(--font-tektur);
  font-size: var(--text-helper);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
}
.bora-code pre {
  margin: 0;
  padding: var(--padding-l2);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--text-helper);
  line-height: 1.7;
  color: var(--color-primary-100);
  tab-size: 2;
}
.bora-code .k { color: var(--accent-signal); }   /* key / keyword */
.bora-code .s { color: var(--color-success-300); } /* string */
.bora-code .c { color: var(--color-primary-300); } /* comment */

/* parameter table */
.bora-params {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-helper);
  table-layout: fixed;
}

.bora-params th,
.bora-params td {
  text-align: left;
  padding: var(--gap-l3);
  border-bottom: 1px solid var(--color-primary-850);
  vertical-align: top;
}

/* 2 / 12 */
.bora-params th:nth-child(1),
.bora-params td:nth-child(1) {
  width: 16.6667%;
}

/* 4 / 12 */
.bora-params th:nth-child(2),
.bora-params td:nth-child(2) {
  width: 33.3333%;
}

/* 6 / 12 */
.bora-params th:nth-child(3),
.bora-params td:nth-child(3) {
  width: 50%;
}

.bora-params th {
  font-family: var(--font-tektur);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  font-weight: var(--font-weight-regular);
}

.bora-params td {
  color: var(--color-primary-100);
}

.bora-params td:first-child {
  font-family: var(--font-mono);
  color: var(--color-neutral-0);
  white-space: nowrap;
}

.bora-params td:nth-child(2) {
  font-family: var(--font-mono);
  color: var(--accent-signal);
}

.bora-params .req {
  color: var(--status-error-fg-light);
  font-family: var(--font-tektur);
  font-size: 0.85em;
}

.bora-doc-tablewrap {
  overflow-x: auto;
}

.bora-doc-tablewrap { overflow-x: auto; }

/* ==========================================================================
   ARCHETYPE SIGNATURES
   ==========================================================================
   Each page type reads differently at a glance, within the one-accent rule —
   the difference is layout + which accent leads, never a new brand colour. */

/* ---- Module pages carry their category colour ----
   --kind-accent is set on <body> from the module's category. The eyebrow and
   the contained masthead glow take it, so an Access-control page glows platform
   gold and a Payment page glows revenue teal — you feel which category you're in. */
.bora-kind-module .bora-eyebrow { color: var(--kind-accent, var(--accent-signal)); }
.bora-kind-module .bora-eyebrow::before { background: var(--kind-accent, var(--accent-signal)); }
.bora-kind-module .bora-masthead::before {
  background: radial-gradient(62% 120% at 12% -10%,
    color-mix(in srgb, var(--kind-accent, var(--accent-action)) 24%, transparent), transparent 68%);
}
.bora-kind-module .bora-detail__num { color: color-mix(in srgb, var(--kind-accent) 22%, var(--color-primary-900)); }

/* ---- Working-surface pages are photo-forward (a hardware datasheet) ----
   The photo art leads the masthead, wider than the text and in a broad
   datasheet ratio, so a touchpoint reads as "real equipment", not an article. */
@media (min-width: 1200px) {
  .bora-kind-surface .bora-masthead__text { flex: 5 1 0%; }
  .bora-kind-surface .bora-masthead__art  { flex: 7 1 0%; }
}
.bora-kind-surface .bora-artplate { aspect-ratio: 16 / 9; }
.bora-kind-surface .bora-eyebrow,
.bora-kind-surface .bora-eyebrow::before { color: var(--color-primary-200); }
.bora-kind-surface .bora-eyebrow::before { background: var(--color-primary-200); }

/* ---- Case pages are editorial ----
   A larger lede and a lead-paragraph "The route" story, so a client story
   reads like an editorial page, not a spec sheet. */
.bora-kind-case .bora-page-lede {
  font-size: var(--text-h4);
  line-height: var(--leading-snug);
  color: var(--color-primary-100);
  max-width: 30ch;
}
/* the story is the only .bora-prose on a case page; lift its opening line to
   a lead paragraph. (`:first-of-type` here would test the <section> element
   type, not the class, so it never matched — this targets the prose directly.) */
.bora-kind-case .bora-prose p:first-child {
  font-size: var(--text-subtitle);
  line-height: var(--leading-relaxed);
  color: var(--color-neutral-0);
}

/* ==========================================================================
   01b — FESTIVE PROMO CAROUSEL (top strip)
   Taller celebratory strip with two auto-rotating banners, a moving sheen
   and twinkling sparks. JS (promo.js) handles rotation + pause.
   ========================================================================== */
#section-01 {
  padding-block: 0;
  background-image: linear-gradient(100deg,
    var(--color-success-700) 0%, var(--color-success-500) 45%, var(--color-success-700) 100%);
}
.bora-promo {
  position: relative;
  overflow: hidden;
  min-height: clamp(108px, 13vh, 148px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gap-l3);
  padding-block: var(--padding-l3);
}
/* moving diagonal sheen */
.bora-promo__sheen {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,0.28) 48%, rgba(255,255,255,0.05) 55%, transparent 70%);
  background-size: 250% 100%;
  animation: bora-promo-sheen 6s linear infinite;
}
@keyframes bora-promo-sheen { 0% { background-position: 150% 0; } 100% { background-position: -80% 0; } }
/* twinkling sparks */
.bora-promo__sparks { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.bora-promo__sparks i {
  position: absolute; left: var(--x); top: 50%;
  width: 7px; height: 7px; margin: -3px 0 0 -3px;
  background: #fff; border-radius: 50%;
  transform: scale(var(--s, 1));
  opacity: 0;
  box-shadow: 0 0 6px 1px rgba(255,255,255,0.9);
  animation: bora-promo-spark 3.2s ease-in-out var(--d, 0s) infinite;
}
.bora-promo__sparks i:nth-child(2n) { top: 30%; }
.bora-promo__sparks i:nth-child(3n) { top: 68%; }
@keyframes bora-promo-spark {
  0%, 100% { opacity: 0; transform: scale(0) rotate(0deg); }
  50%      { opacity: 1; transform: scale(var(--s, 1)) rotate(90deg); }
}
/* The section container is display:flex, so a shrink-to-fit child would not
   reach the container edges on wide screens — force full width so the banner
   uses the same grid as every other block, at every width incl. >1440. */
.bora-promo__viewport { position: relative; z-index: 1; width: 100%; }
.bora-promo__viewport { flex: 1 1 100%; }
.bora-promo__slides { position: relative; width: 100%; }
.bora-promo__slide {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--gap-l2) var(--gap-l1); text-align: center;
  position: absolute; inset: 0; margin: auto;
  opacity: 0; visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s linear 0.5s;
  /* the whole slide is the click target now (no separate button) */
  width: 100%; padding: 0; border: 0; background: none; color: inherit;
  font: inherit; text-decoration: none; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.bora-promo__arrow {
  flex-shrink: 0;
  font-family: var(--font-tektur);
  font-size: 1.4em; line-height: 1; color: var(--color-neutral-0);
  transition: transform 0.2s ease;
}
.bora-promo__slide:hover .bora-promo__arrow,
.bora-promo__slide:focus-visible .bora-promo__arrow { transform: translateX(5px); }
.bora-promo__slide:hover .bora-promo__text { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.bora-promo__slide:focus-visible { outline: 2px solid var(--color-neutral-0); outline-offset: -4px; }
/* Align banner content to the same container grid as the sections below:
   copy to the left gutter, CTA to the right, on one line. */
@media (min-width: 768px) {
  .bora-promo__slide { flex-wrap: nowrap; justify-content: space-between; text-align: left; }
  .bora-promo__text { flex: 1 1 auto; }
}
.bora-promo__slide.is-active {
  position: relative;
  opacity: 1; visibility: visible; transform: none;
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.bora-promo__text {
  margin: 0;
  font-family: var(--font-montserrat);
  font-size: var(--text-body);
  line-height: var(--leading-snug);
  color: var(--color-neutral-0);
}
.bora-promo__text strong { font-weight: 700; }
.bora-promo__badge {
  display: inline-block; vertical-align: middle;
  margin-right: 0.5em; padding: 0.2em 0.6em;
  font-family: var(--font-tektur); font-size: var(--text-helper);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-success-700); background: var(--color-neutral-0);
  border-radius: 2px;
}
.bora-promo__cta { flex-shrink: 0; }
.bora-promo__dots { position: relative; z-index: 1; display: flex; gap: 8px; }
.bora-promo__dot {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(255,255,255,0.4); cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
}
.bora-promo__dot.is-active { background: #fff; transform: scale(1.25); }
/* Falling confetti — festive layer in place of the switch dots. */
.bora-promo__confetti { position: absolute; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }
.bora-promo__confetti i {
  position: absolute; left: var(--x); top: -14px;
  width: 7px; height: 11px; border-radius: 1px;
  background: var(--c, #fff); opacity: 0;
  animation: bora-confetti 4.5s linear var(--d, 0s) infinite;
}
@keyframes bora-confetti {
  0%   { transform: translateY(-14px) rotate(0deg); opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: translateY(170px) rotate(calc(var(--r, 20deg) * 9)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .bora-promo__sheen, .bora-promo__sparks i, .bora-promo__confetti i { animation: none; }
  .bora-promo__sheen, .bora-promo__confetti { display: none; }
  .bora-promo__slide { transition: opacity 0.2s ease; transform: none; }
}

/* Module-card "learn more": on hover the button fills with the category colour,
   so its text must flip to white to stay legible (was module-colour on
   module-colour = invisible). */
.bora-module .btn--outline { --btn-color-text-hover: var(--color-neutral-0); }

/* ---------- Hero spacing compensation (homepage only) ----------
   The promo strip is ~3x taller now; claw the height back so the client
   logos stay in the first viewport, by tightening the hero's vertical rhythm.
   Scoped to #section-02 so inner-page heroes are untouched. */
#section-02 .bora-hero-nav-inner { padding-top: var(--padding-l3); }

/* Landing only: with the demo button switched off, the actions wrapper holds
   nothing at desktop width — the burger inside it is display:none from 1200px
   up. An empty flex item still takes a slot, and .bora-nav is space-between,
   so the free space was split evenly into two 228px holes: one before the nav
   links and one after them, with the links stranded in the middle and the
   right-hand end of the bar empty. Dropping the wrapper leaves two items, and
   the links land flush right where the button used to sit.

   Below 1200px the wrapper *is* the burger, so it has to stay — and every
   inner page still has its "request a demo" button in it, which is why this
   is scoped to the landing hero rather than written against .bora-nav. */
@media (min-width: 1200px) {
  #section-02 .bora-nav-actions { display: none; }
}
/* `--padding-l4` was never defined in tokens.css, so this resolved to nothing
   and the strip sat flush against "become a partner" on a phone — no gap at
   all. Kept at zero for tablet and desktop, where the tighter rhythm is the
   point of this block; on mobile the strip takes the same 40px the hero puts
   above its buttons, so the spacing above and below them matches. */
#section-02 .bora-clients { padding-block: 0; }
@media (max-width: 767px) {
  #section-02 .bora-clients { padding-top: var(--gap-l1); }
}
@media (min-width: 768px) {
  #section-02 .bora-hero-content { margin-top: 8px; margin-bottom: 12px; }
}
@media (min-width: 1200px) {
  #section-02.bora-hero .bora-hero-content { margin-block: 16px; }
}

/* ---------- Clickable client logos ---------- */
.bora-client-logo-link {
  display: inline-flex; align-items: center;
  transition: opacity var(--motion-snap, 0.15s) var(--motion-ease, ease), transform var(--motion-snap, 0.15s) var(--motion-ease, ease);
}
.bora-client-logo-link:hover,
.bora-client-logo-link:focus-visible { opacity: 0.65; }

/* ==========================================================================
   FLOATING ACTIONS — Telegram + back-to-top (bottom-right, offset)
   ========================================================================== */
.bora-floats {
  position: fixed; z-index: 60;
  right: clamp(16px, 3vw, 32px);
  bottom: clamp(16px, 3vw, 32px);
  display: flex; flex-direction: column; gap: 12px;
}
/* On inner pages, `body.bora-inner > * { position: relative; z-index: 1 }`
   (specificity 0,1,1) outweighs the bare `.bora-floats` (0,1,0) and was
   dropping the buttons back into the document flow at the end of the page.
   Re-assert fixed positioning + the stacking order with a matching-or-higher
   specificity selector. */
body.bora-inner .bora-floats { position: fixed; z-index: 60; }
.bora-float {
  width: 48px; height: 48px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-primary-500);
  border-radius: 50%;
  background: var(--color-primary-500);
  color: var(--color-neutral-0);
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(1, 7, 19, 0.45);
  transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
}
.bora-float:hover { background: var(--color-primary-400); transform: translateY(-2px); }
.bora-float:focus-visible { outline: 2px solid var(--color-neutral-0); outline-offset: 2px; }
.bora-float[hidden] { display: none; }
.bora-float--top {
  background: var(--color-primary-800); border-color: var(--color-primary-700);
}
.bora-float--top:hover { background: var(--color-primary-700); }
@media (prefers-reduced-motion: reduce) {
  .bora-float:hover { transform: none; }
}

/* ==========================================================================
   LEGAL PAGES — long-form readable prose
   ========================================================================== */
.bora-legal { max-width: 80ch; }
.bora-legal__h {
  margin: var(--gap-l1) 0 var(--gap-l3);
  font-family: var(--font-montserrat); font-weight: 500;
  font-size: var(--text-h3); color: var(--color-neutral-0);
}
.bora-legal__h:first-child { margin-top: 0; }
.bora-legal__sub {
  margin: var(--gap-l2) 0 var(--gap-l4);
  font-family: var(--font-montserrat); font-weight: 500;
  font-size: var(--text-body-large); color: var(--color-neutral-0);
}
.bora-legal p, .bora-legal li { color: var(--color-primary-100); }
.bora-legal ul { margin: 0 0 var(--gap-l2); padding-left: 1.4em; }
.bora-legal li { margin-bottom: var(--gap-l4); }

/* Modules-index intro: two balanced columns on desktop instead of two
   full-width paragraphs stacked. */
.bora-modules-intro { max-width: none; }
@media (min-width: 1000px) {
  .bora-modules-intro { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-l1); }
  .bora-modules-intro p { margin: 0; }
}

/* ==========================================================================
   CASE CARDS — logo-led design (cases index). Deliberately different from the
   text-only .bora-entry: a dark logo band on top, then region / name / line /
   read-the-case, with the brand top-right bevel on the whole card.
   ========================================================================== */
.bora-cases-grid { display: grid; grid-template-columns: 1fr; gap: var(--grid-gap, 24px); }
@media (min-width: 768px)  { .bora-cases-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .bora-cases-grid { grid-template-columns: repeat(3, 1fr); } }
.bora-casecard {
  display: flex; flex-direction: column;
  background-color: var(--color-primary-900);
  text-decoration: none; color: inherit;
  clip-path: polygon(0 0, calc(100% - var(--cut-m)) 0, 100% var(--cut-m), 100% 100%, 0 100%);
  transition: transform 0.2s ease, background-color 0.2s ease;
}
.bora-casecard:hover { transform: translateY(-4px); background-color: var(--color-primary-850); }
.bora-casecard__logo {
  display: flex; align-items: center; justify-content: center;
  min-height: 132px; padding: var(--padding-l2);
  background-color: var(--color-primary-950);
  border-bottom: 1px solid var(--color-primary-800);
}
.bora-casecard__logo img { max-height: 56px; max-width: 80%; width: auto; height: auto; object-fit: contain; }
.bora-casecard__body { display: flex; flex-direction: column; gap: var(--gap-l3); padding: var(--padding-l2); flex: 1; }
.bora-casecard__region {
  font-family: var(--font-tektur); font-size: var(--text-helper);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent-signal);
}
.bora-casecard__title { margin: 0; font-family: var(--font-montserrat); font-weight: 500; font-size: var(--text-h3); color: var(--color-neutral-0); }
.bora-casecard__text { margin: 0; font-family: var(--font-montserrat); font-size: var(--text-body); line-height: var(--leading-normal); color: var(--color-primary-100); flex: 1; }
.bora-casecard__more {
  margin-top: var(--gap-l4);
  font-family: var(--font-tektur); font-size: var(--text-helper);
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-neutral-0);
  display: inline-flex; gap: 0.5em; align-items: center;
}
.bora-casecard:hover .bora-casecard__more { color: var(--accent-signal); }
.bora-casecard__more span { transition: transform 0.2s ease; }
.bora-casecard:hover .bora-casecard__more span { transform: translateX(4px); }
