/* ═══════════════════════════════════════════════════════════
   os-product.css — components for /organisation-security
   Loaded after os-base.css (tokens, buttons, sections, split,
   showcase, comparison, licenser, timeline). This file adds the
   section chrome, motion primitives, 3D containers and the
   content components the nine pages share.
   ═══════════════════════════════════════════════════════════ */

/* ── Tokens and contrast corrections ─────────────────────── */
:root {
  --c-brand-fill: #c43c18;   /* white text on this passes WCAG AA */
  --c-brand-text: #f0704d;   /* brand-coloured text on dark surfaces */
  --c-text-dim:   #9a9aa6;   /* replaces #71717a (3.7:1) */
  --c-ok:   #3ab07a;
  --c-warn: #e0a53a;
  --c-info: #5a96ff;
  --os-subnav-h: 52px;
  --ease-out: cubic-bezier(.2, .7, .2, 1);
  /* Text tones for status chips and states (dark theme values; light ones below). */
  --c-ok-text:   #8fd3b0;
  --c-warn-text: #f0c674;
  --c-info-text: #9cc4ff;
  --c-bad-text:  #ff7468;
  --c-accent-soft: #ffd6ca;  /* text on a brand-tinted surface (chosen pill, active tab) */
  /* The three.js frame: page-tone backdrop under a warm glow. */
  --c-scene-bg: var(--c-bg-alt);
  --c-scene-glow: rgba(215, 69, 31, .18);
}
/* Light theme tokens of this file (contract A). Contrast is measured on --c-bg #f7f5f3 and --c-bg-alt #efebe7 and on
   the tinted chip surfaces: brand text 5.5:1 / 4.7:1 on a brand chip, green 5.0:1, amber 5.7:1, blue 5.2:1. */
html[data-theme="light"] {
  --c-brand-text: #a8371f;
  --c-text-dim:   #65656e;
  --c-ok-text:    #1a6b44;
  --c-warn-text:  #7a4e00;
  --c-info-text:  #2557b5;
  --c-bad-text:   #b8231b;
  --c-accent-soft: #9a3418;
  --c-scene-bg: linear-gradient(180deg, #fcfbf9 0%, #f1ede8 100%);
  --c-scene-glow: rgba(215, 69, 31, .10);
}
.os-page .btn--primary,
.os-page .segmented__item.is-active,
.os-page .preset-card__badge { background: var(--c-brand-fill); border-color: var(--c-brand-fill); color: #fff; }
.os-page .btn--primary:hover,
.os-page .btn--primary:focus-visible { background: var(--c-brand-dark); border-color: var(--c-brand-dark); }
.os-page .licenser__included,
.os-page .licenser__summary-tag,
.os-page .preset-card__price,
.os-page .licenser__summary-note,
.os-page .section__tag,
.os-page .hero__badge { color: var(--c-brand-text); }
.os-page .licenser__group-title,
.os-page .licenser__preset-hint,
.os-page .licenser__total-label,
.os-page .licenser__annual,
.os-page .licenser__fineprint,
.os-page .module-toggle__desc,
.os-page small { color: var(--c-text-dim); }
.os-page a:focus-visible,
.os-page button:focus-visible,
.os-page [tabindex]:focus-visible { outline: 2px solid var(--c-brand-text); outline-offset: 2px; }
.os-main { display: block; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ── Section sub navigation ──────────────────────────────── */
.os-subnav {
  position: sticky; top: 72px; z-index: 1020;
  background: rgb(var(--c-bg-rgb) / .82);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--c-surface-border);
  transition: box-shadow .25s ease;
}
.os-subnav.is-stuck { box-shadow: 0 10px 30px rgb(var(--c-shadow-rgb) / calc(.35 * var(--c-shadow-k))); }
.os-subnav__inner { display: flex; align-items: center; gap: var(--sp-3); min-height: var(--os-subnav-h); }
.os-subnav__brand { display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; font-size: var(--fs-sm); white-space: nowrap; color: var(--c-text); }
.os-subnav__links { display: flex; gap: .2rem; flex: 1; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.os-subnav__links::-webkit-scrollbar { display: none; }
.os-subnav__link { white-space: nowrap; padding: .42rem .6rem; border-radius: 999px; font-size: .82rem; font-weight: 500; color: var(--c-text-muted); transition: color .2s, background .2s; }
/* OrgSec logo from 1100 px; below that only the symbol, so the nine links keep their width. The wordmark exists in
   two colours (SPEC "Logotyp"): orgsec-logo-on-dark.svg (light wordmark) in the dark theme, orgsec-logo.svg (dark
   wordmark) in the light theme; .os-logo swaps them, the colour symbol is the same in both. */
.os-subnav__logo { display: block; height: 22px; }
.os-subnav__logo img { height: 22px; width: auto; } /* display comes from img (block) and the .os-logo swap below */
.os-subnav__symbol { display: none; height: 22px; width: 22px; }
@media (max-width: 1099.98px) { .os-subnav__logo { display: none; } .os-subnav__symbol { display: block; } }
.os-logo--on-light { display: none; }
html[data-theme="light"] .os-logo--on-dark { display: none; }
html[data-theme="light"] .os-logo--on-light { display: block; }
.os-subnav__link:hover { color: var(--c-text); background: rgb(var(--c-tint-rgb) / .05); }
.os-subnav__link.is-active { color: var(--c-text); background: rgba(215, 69, 31, .16); box-shadow: inset 0 0 0 1px rgba(215, 69, 31, .4); }
.os-subnav__actions { position: relative; display: flex; align-items: center; gap: .5rem; margin-left: auto; }
.os-subnav__cta { white-space: nowrap; }
@media (max-width: 767.98px) {
  .os-subnav__cta { display: none; }
}

/* ── Sound toggle ────────────────────────────────────────── */
/* State comes from html.os-sound-off / html.os-reduce (os-head.js sets them before first paint, os-sound.js keeps
   them in step), so a stored "off" shows "Ljud: av" at once. The visible label is the button's name. Without
   JavaScript there is no sound, so there is no toggle either. */
.sound-toggle {
  display: inline-flex; align-items: center; gap: .5rem;
  background: rgb(var(--c-tint-rgb) / .05); border: 1px solid var(--c-surface-border);
  color: var(--c-text-muted); border-radius: 999px; padding: .42rem .85rem;
  font: 600 var(--fs-xs)/1 var(--ff); cursor: pointer;
  transition: color .2s, border-color .2s, background .2s;
}
html:not(.js) .sound-toggle { display: none; }
.sound-toggle:hover { color: var(--c-text); border-color: rgb(var(--c-tint-rgb) / .22); }
html:not(.os-sound-off):not(.os-reduce) .sound-toggle { color: var(--c-text); border-color: rgba(215, 69, 31, .55); }
/* Reduced motion: off and not switchable; muted text keeps 4.5:1 in both themes (no opacity). */
.sound-toggle[aria-disabled="true"] { cursor: default; border-style: dashed; color: var(--c-text-muted); }
.sound-toggle__label--off { display: none; }
html.os-sound-off .sound-toggle__label--on, html.os-reduce .sound-toggle__label--on { display: none; }
html.os-sound-off .sound-toggle__label--off, html.os-reduce .sound-toggle__label--off { display: inline; }
/* Why the toggle is off under reduced motion: a bubble under it, shown for 4 s after a press (os-sound.js). It stays
   rendered while empty, so the status region is announced when the text arrives. */
.sound-toggle__note {
  position: absolute; right: 0; top: calc(100% + 8px); width: max-content; max-width: min(260px, 80vw);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-xs); font-weight: 500; line-height: 1.55; text-align: left;
  color: var(--c-text); background: var(--c-bg-raised, #1c1c1e);
  border: 1px solid var(--c-surface-border); border-radius: var(--radius);
  box-shadow: 0 12px 32px rgb(var(--c-shadow-rgb) / calc(.45 * var(--c-shadow-k)));
  white-space: normal; z-index: 40; pointer-events: none;
}
.sound-toggle__note:empty { padding: 0; border: 0; box-shadow: none; }
.sound-toggle__icon { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; }
.sound-toggle__icon i { display: block; width: 2px; height: 5px; background: currentColor; border-radius: 1px; transform-origin: bottom; transform: scaleY(.45); }
.sound-toggle__icon i:nth-child(2) { height: 9px; }
.sound-toggle__icon i:nth-child(3) { height: 12px; }
.sound-toggle__icon i:nth-child(4) { height: 7px; }
@keyframes os-eq { 0%, 100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }

/* ── Hero ────────────────────────────────────────────────── */
.os-hero {
  position: relative; overflow: hidden;
  padding: var(--sp-16) 0 var(--sp-16);
  min-height: calc(100vh - 72px - var(--os-subnav-h));
  display: flex; align-items: center;
  background:
    radial-gradient(1100px 560px at 82% 18%, rgba(215, 69, 31, .16), transparent 62%),
    radial-gradient(700px 400px at 10% 90%, rgba(215, 69, 31, .08), transparent 60%),
    var(--c-bg);
}
html[data-theme="light"] .os-hero {
  background:
    radial-gradient(1100px 560px at 82% 18%, rgba(215, 69, 31, .10), transparent 62%),
    radial-gradient(700px 400px at 10% 90%, rgba(215, 69, 31, .05), transparent 60%),
    var(--c-bg);
}
/* Hub: the OrgSec logo stands above the H1 in place of the text eyebrow (the product name stays for screen readers). */
.os-hero__brand { display: block; margin: 0 0 var(--sp-2); }
.os-hero__brand img { height: 46px; width: auto; }
@media (max-width: 575.98px) { .os-hero__brand img { height: 38px; } }
.os-hero__grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: var(--sp-10); align-items: center; }
.os-hero__eyebrow { display: inline-flex; align-items: center; gap: .5rem; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--c-brand-text); }
.os-hero__title { font-size: clamp(2.2rem, 5vw, 3.6rem); line-height: 1.08; letter-spacing: -.02em; font-weight: 800; margin: var(--sp-4) 0; color: var(--c-text); }
.os-hero__title .accent { color: var(--c-brand-text); }
.os-hero__lead { font-size: var(--fs-lg); color: var(--c-text-muted); max-width: 52ch; margin-bottom: var(--sp-6); }
.os-hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.os-hero__proof { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: var(--sp-6); list-style: none; padding: 0; } /* flush with the lead, no default list indent */
.proof-chip { display: inline-flex; align-items: center; gap: .45rem; padding: .42rem .8rem; border-radius: 999px; background: rgb(var(--c-tint-rgb) / .04); border: 1px solid var(--c-surface-border); font-size: var(--fs-xs); color: var(--c-text-muted); }
/* Light theme: chips are white pills on the warm page, so they read as chips and not as grey holes. */
html[data-theme="light"] .proof-chip { background: var(--c-surface); border-color: var(--c-line); }
.proof-chip i { color: var(--c-brand-text); }
.os-hero__visual { position: relative; min-height: 340px; }
@media (max-width: 991.98px) {
  .os-hero { min-height: 0; padding-top: var(--sp-12); }
  .os-hero__grid { grid-template-columns: 1fr; gap: var(--sp-8); }
  .os-hero__visual { min-height: 260px; }
}

/* ── Motion primitives ───────────────────────────────────── */
html.js .os-page .words .w {
  display: inline-block; opacity: 0; filter: blur(10px); transform: translateY(.3em);
  animation: os-word .7s var(--ease-out) forwards;
  animation-delay: calc(var(--i, 0) * 60ms + .15s);
}
@keyframes os-word { to { opacity: 1; filter: blur(0); transform: none; } }

.os-page [data-reveal] { opacity: 1; transform: none; }
html.js .os-page [data-reveal] {
  transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 80ms);
}
html.js .os-page [data-reveal]:not(.is-in) { opacity: 0; transform: translateY(26px); }

.showcase-panel { transition: opacity .18s ease, transform .18s ease; }
.showcase-panel.is-switching { opacity: 0; transform: translateY(6px); }

@media (prefers-reduced-motion: reduce) {
  html.js .os-page [data-reveal]:not(.is-in) { opacity: 1; transform: none; }
  html.js .os-page .words .w { opacity: 1; filter: none; transform: none; animation: none; }
  .os-page *, .os-page *::before, .os-page *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* ── Platform stack (CSS 3D) ─────────────────────────────── */
.os-stack-scene { perspective: 1100px; display: grid; place-items: center; min-height: 320px; }
.os-stack {
  --lift: 1;
  position: relative; width: min(300px, 78vw); aspect-ratio: 250 / 190;
  transform-style: preserve-3d; transform: rotateX(58deg) rotateZ(-36deg);
  list-style: none; margin: 0; padding: 0;
}
.os-stack__base {
  position: absolute; inset: 0; border-radius: 10px;
  background: linear-gradient(135deg, #232328, #17171b);
  border: 1px solid rgb(var(--c-tint-rgb) / .14);
  box-shadow: 0 50px 70px rgb(var(--c-shadow-rgb) / calc(.5 * var(--c-shadow-k)));
}
/* Light theme: a pale slab under the coloured blocks (the blocks keep their module colours in both themes). */
html[data-theme="light"] .os-stack__base { background: linear-gradient(135deg, #ffffff, #e9e4de); }
.os-stack__base-label { position: absolute; left: 4%; bottom: 5%; font-size: .55rem; letter-spacing: .08em; text-transform: uppercase; color: rgb(var(--c-tint-rgb) / .55); white-space: nowrap; }
html[data-theme="light"] .os-stack__base-label { color: var(--c-text-muted); }
.os-stack__mod {
  position: absolute; width: 41%; height: 40%; border-radius: 7px; display: block;
  background: var(--mod-color, var(--c-brand));
  /* The inset top highlight sits on the module colour, so it stays white in both themes. */
  box-shadow: 0 16px 26px rgb(var(--c-shadow-rgb) / calc(.4 * var(--c-shadow-k))), inset 0 1px 0 rgba(255, 255, 255, .25);
  transform: translateZ(calc(26px * var(--lift)));
  transition: transform .5s var(--ease-out), translate .45s var(--ease-out), filter .5s ease, opacity .5s ease;
  cursor: pointer;
}
.os-stack__mod::before { content: ""; position: absolute; inset: 0; border-radius: 7px; background: linear-gradient(160deg, rgba(255, 255, 255, .18), transparent 60%); }
.os-stack__mod.m1 { left: 5%; top: 6%; }
.os-stack__mod.m2 { left: 54%; top: 6%; }
.os-stack__mod.m3 { left: 5%; top: 54%; }
.os-stack__mod.m4 { left: 54%; top: 54%; }
.os-stack__mod.m5 { left: 29.5%; top: 30%; transform: translateZ(calc(60px * var(--lift))); }
/* backwards: keyframe 0 % (translateZ 26 px) already holds during each block's delay. Without it a block sat on its
   base transform (26 px × --lift, from the scroll progress) and jumped to 26 px in one frame when its turn came. */
.os-stack--auto .os-stack__mod { animation: os-lift 7.5s ease-in-out infinite backwards; }
.os-stack--auto .m1 { animation-delay: 0s; }
.os-stack--auto .m2 { animation-delay: 1.5s; }
.os-stack--auto .m3 { animation-delay: 3s; }
.os-stack--auto .m4 { animation-delay: 4.5s; }
.os-stack--auto .m5 { animation-delay: 6s; animation-name: os-lift2; }
@keyframes os-lift  { 0%, 16%, 100% { transform: translateZ(26px); filter: brightness(1); } 5%, 11% { transform: translateZ(50px); filter: brightness(1.25); } }
@keyframes os-lift2 { 0%, 16%, 100% { transform: translateZ(60px); filter: brightness(1); } 5%, 11% { transform: translateZ(86px); filter: brightness(1.2); } }
/* Hover and focus lift (contract C, 2026-11-03): the auto-lift animation pauses where it stands and the block glides
   up 32 px on top of it through the separate translate property (it composes with the animated transform), so the
   block no longer jumps to a fixed height. Without the animation, 26 px × lift + 32 px = 58 px, as before.
   Hover class: html.is-scrolling (pointer-events: none) arrives one frame after the first scroll frame, so a block
   that slid under a still pointer could still get :hover. When os-motion.js sets html.js-hover it puts .is-hover on
   a block only on real pointer movement outside a scroll, and the raw :hover rule stops applying; without that class
   (no JS, or an older os-motion.js) :hover works as before. Same pattern for a.module-card below. */
html:not(.js-hover) .os-stack__mod:not(.is-off):hover, .os-stack__mod:not(.is-off).is-hover, .os-stack__mod:not(.is-off):focus-visible { translate: 0 0 32px; filter: brightness(1.28); animation-play-state: paused; }
.os-stack--controlled .os-stack__mod { animation: none; }
.os-stack__mod.is-off { opacity: .18; transform: translateZ(6px) !important; filter: grayscale(1); }
.os-stack__mod.is-on { filter: brightness(1.15); }
/* Light theme: brightening the light module colours on the pale slab washed them out (m6 1.7:1 against the slab) and
   lit and unlit blocks looked alike. Lit blocks keep their own colour and get a thin dark edge; unlit ones stay a
   little more visible. The hover rule repeats the brighten, because this one is more specific. */
html[data-theme="light"] .os-stack__mod.is-on { filter: none; box-shadow: 0 16px 26px rgb(var(--c-shadow-rgb) / calc(.4 * var(--c-shadow-k))), inset 0 0 0 1.5px rgba(110, 35, 15, .45); }
html[data-theme="light"]:not(.js-hover) .os-stack__mod.is-on:hover, html[data-theme="light"] .os-stack__mod.is-on.is-hover, html[data-theme="light"] .os-stack__mod.is-on:focus-visible { filter: brightness(1.08); }
html[data-theme="light"] .os-stack__mod.is-off { opacity: .34; }
/* A block that holds two licences (m1 va+sg, m4 cy+ca) with only one of them chosen: half lit (OSStack.set). */
.os-stack__mod.is-part { filter: none; }
.os-stack__mod.is-part::after { content: ""; position: absolute; inset: 0; border-radius: 7px; background: linear-gradient(90deg, transparent 50%, rgb(var(--c-bg-rgb) / .62) 50%); pointer-events: none; }
/* While the page scrolls (os-motion.js sets html.is-scrolling, removed 200 ms after the last scroll event) nothing
   changes its hover state because content slides under a still pointer (contract C). */
html.is-scrolling .os-stack__mod, html.is-scrolling .scene-steps__item, html.is-scrolling .module-card, html.is-scrolling .scene { pointer-events: none; }
[data-scrub-progress] .os-stack { --lift: calc(.35 + var(--p, 1) * .65); }
.os-stack-legend { display: grid; gap: .45rem; list-style: none; padding: 0; margin: 0; font-size: var(--fs-sm); }
.os-stack-legend li { display: flex; align-items: center; gap: .6rem; color: var(--c-text-muted); }
.os-stack-legend i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; background: var(--mod-color, var(--c-brand)); flex: none; }
.os-stack-legend a { color: var(--c-text); }
.os-stack-legend a:hover { color: var(--c-brand-text); }

/* Six-block stack (2026-11-03): three columns × two rows, all blocks on the same plane.
   m1 Bedömning & signaturer · m2 Risk Score & screening · m3 Riskhantering
   m4 Cyberexponering · m5 Avtal och licenser · m6 Varumärkesanpassning. Base = Grundplattform. */
.os-stack--six .os-stack__mod { width: 27%; height: 40%; }
.os-stack--six .os-stack__mod.m1 { left: 5%;    top: 6%; }
.os-stack--six .os-stack__mod.m2 { left: 36.5%; top: 6%; }
.os-stack--six .os-stack__mod.m3 { left: 68%;   top: 6%; }
.os-stack--six .os-stack__mod.m4 { left: 5%;    top: 54%; }
.os-stack--six .os-stack__mod.m5 { left: 36.5%; top: 54%; transform: translateZ(calc(26px * var(--lift))); }
.os-stack--six .os-stack__mod.m6 { left: 68%;   top: 54%; }
.os-stack--auto.os-stack--six .m1 { animation-delay: 0s; }
.os-stack--auto.os-stack--six .m2 { animation-delay: 1.2s; }
.os-stack--auto.os-stack--six .m3 { animation-delay: 2.4s; }
.os-stack--auto.os-stack--six .m4 { animation-delay: 3.6s; }
.os-stack--auto.os-stack--six .m5 { animation-delay: 4.8s; animation-name: os-lift; }
.os-stack--auto.os-stack--six .m6 { animation-delay: 6s; }
/* The bottom row ends at 94 %; a one-line label fits in the strip below it (keep white-space: nowrap). */
.os-stack--six .os-stack__base-label { left: 4%; bottom: .8%; max-width: 92%; line-height: 1.1; font-size: .75rem; letter-spacing: .12em; overflow: hidden; text-overflow: ellipsis; }
/* The module icon is printed on each block (same icon as the module card), so the stack does not rely on colour alone. */
.os-stack__mod > .bi { position: absolute; inset: 0; display: grid; place-items: center; font-size: 1.45rem; color: rgba(255, 255, 255, .92); text-shadow: 0 1px 2px rgba(0, 0, 0, .25); pointer-events: none; }
.os-stack__mod.m2 > .bi, .os-stack__mod.m6 > .bi { color: rgba(60, 20, 10, .72); text-shadow: none; }
.licenser__stack .os-stack__mod > .bi { font-size: 1.1rem; }
.os-stack-legend i.bi { width: 1.4rem; height: 1.4rem; border-radius: .35rem; display: inline-grid; place-items: center; font-size: .78rem; color: #fff; }
.os-stack-legend i.bi.is-light { color: rgba(60, 20, 10, .78); }

/* Card price pair: month first, year as secondary small text (decision 2026-11-03). */
.price-pair { display: inline-flex; flex-direction: column; align-items: flex-start; gap: .1rem; line-height: 1.25; font-variant-numeric: tabular-nums; }
.price-main { font-weight: 800; color: var(--c-brand-text); }
.price-sub { font-size: var(--fs-xs); color: var(--c-text-dim); font-weight: 500; }
.licenser__adjusted-note { font-size: var(--fs-xs); color: var(--c-text-dim); margin: 0; line-height: 1.5; }
.licenser__adjusted-note[hidden] { display: none; }

/* ── three.js scene container ────────────────────────────── */
.scene {
  position: relative; width: 100%; aspect-ratio: 16 / 10;
  border-radius: var(--radius-xl); overflow: hidden;
  /* Follows the theme (contract B): dark page tone in the dark theme, a pale studio backdrop in the light one. */
  background: radial-gradient(600px 300px at 70% 30%, var(--c-scene-glow), transparent 60%), var(--c-scene-bg);
  border: 1px solid var(--c-surface-border);
}
.scene__canvas { position: absolute; inset: 0; width: 100% !important; height: 100% !important; display: block; opacity: 0; transition: opacity .6s ease; }
.scene--live .scene__canvas { opacity: 1; }
.scene__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: opacity .6s ease; }
.scene--live .scene__poster { opacity: 0; pointer-events: none; }
.scene__start { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 2; }
.scene__start.is-hidden { display: none; }
.scene__hint { position: absolute; left: var(--sp-4); bottom: var(--sp-3); font-size: var(--fs-xs); color: var(--c-text-dim); opacity: 0; transition: opacity .4s ease; pointer-events: none; }
.scene--live .scene__hint { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .scene__hint { display: none; } }
/* Active caption as an overlay inside the scene on small screens (filled by os-scene.js bindSteps). */
.scene__caption { display: none; }
@media (max-width: 991.98px) and (prefers-reduced-motion: no-preference) {
  .scene--live .scene__caption.is-on {
    display: flex; gap: .55rem; align-items: flex-start; position: absolute; left: .5rem; right: .5rem; bottom: .5rem; z-index: 3;
    padding: .5rem .65rem; border-radius: var(--radius); background: rgba(12, 12, 14, .86); border: 1px solid rgba(215, 69, 31, .45);
    font-size: .8rem; line-height: 1.35; pointer-events: none;
  }
  html[data-theme="light"] .scene--live .scene__caption.is-on { background: rgb(255 255 255 / .92); box-shadow: 0 6px 18px rgb(var(--c-shadow-rgb) / calc(.4 * var(--c-shadow-k))); }
  .scene__caption i { flex: none; width: 1.35rem; height: 1.35rem; border-radius: 50%; display: grid; place-items: center; font: 800 .7rem/1 var(--ff); font-style: normal; background: var(--c-brand-fill); color: #fff; }
  .scene__caption b { display: block; color: var(--c-text); font-size: .85rem; }
  .scene__caption b .status { margin-left: .35rem; font-size: .68rem; vertical-align: 1px; }
  .scene__caption div > span { display: block; color: var(--c-text-muted); }
  .scene--live:has(.scene__caption.is-on) .scene__hint { display: none; }
}
.scene__fallback-note { display: none; position: absolute; left: var(--sp-4); bottom: var(--sp-3); font-size: var(--fs-xs); color: var(--c-text-dim); }
.scene--fallback .scene__fallback-note { display: block; }
.scene__label { position: absolute; left: var(--sp-4); top: var(--sp-3); font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--c-text-dim); }
.scene--hero { aspect-ratio: auto; position: absolute; inset: 0; border: none; background: transparent; border-radius: 0; overflow: visible; }
@media (hover: none) { .scene__hint { display: none; } }

/* ── Status badges ───────────────────────────────────────── */
.status { display: inline-flex; align-items: center; gap: .35rem; padding: .22rem .62rem; border-radius: 999px; font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; border: 1px solid transparent; white-space: nowrap; }
.status--always, .status--included { color: var(--c-ok-text); background: rgba(58, 176, 122, .12); border-color: rgba(58, 176, 122, .3); }
.status--module, .status--optional { color: var(--c-brand-text); background: rgba(215, 69, 31, .12); border-color: rgba(215, 69, 31, .32); }
.status--request { color: var(--c-warn-text); background: rgba(224, 165, 58, .12); border-color: rgba(224, 165, 58, .32); }
.status--rollout { color: var(--c-info-text); background: rgba(90, 150, 255, .12); border-color: rgba(90, 150, 255, .32); }
/* Light theme: stronger chip edges, the tinted fill alone is too faint on the warm page. */
html[data-theme="light"] .status--always, html[data-theme="light"] .status--included { border-color: rgba(26, 107, 68, .35); }
html[data-theme="light"] .status--module, html[data-theme="light"] .status--optional { border-color: rgba(168, 55, 31, .35); }
html[data-theme="light"] .status--request { border-color: rgba(122, 78, 0, .32); }
html[data-theme="light"] .status--rollout { border-color: rgba(37, 87, 181, .32); }

/* ── Module cards ────────────────────────────────────────── */
.module-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--sp-5); }
.module-card {
  position: relative; display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-6); background: var(--c-surface); border: 1px solid var(--c-surface-border);
  border-radius: var(--radius-lg); color: inherit;
  transition: border-color .25s ease, transform .25s ease, box-shadow .25s ease;
}
/* html.js-hover: os-motion.js owns the hover state (.is-hover on real pointer movement, never during a scroll); see the stack. */
html:not(.js-hover) a.module-card:hover, a.module-card.is-hover, a.module-card:focus-visible { border-color: rgba(215, 69, 31, .5); transform: translateY(-3px); box-shadow: 0 20px 40px rgb(var(--c-shadow-rgb) / calc(.35 * var(--c-shadow-k))); }
.module-card__top { display: flex; justify-content: space-between; align-items: flex-start; gap: .5rem; }
.module-card__icon { width: 2.6rem; height: 2.6rem; border-radius: .75rem; display: grid; place-items: center; background: rgba(215, 69, 31, .14); color: var(--c-brand-text); font-size: 1.3rem; }
.module-card__title { font-size: var(--fs-xl); font-weight: 700; line-height: 1.25; }
.module-card__text { color: var(--c-text-muted); font-size: var(--fs-sm); } /* the list follows the text directly; .module-card__meta (margin-top: auto) takes the slack */
.module-card__list { display: grid; gap: .35rem; font-size: var(--fs-sm); color: var(--c-text-muted); list-style: none; padding: 0; }
.module-card__list li { padding-left: 1.1rem; position: relative; }
.module-card__list li::before { content: ""; position: absolute; left: 0; top: .55em; width: .45rem; height: .45rem; border-radius: 50%; background: var(--c-brand); }
.module-card__meta { display: flex; justify-content: space-between; align-items: center; gap: .5rem; font-size: var(--fs-sm); color: var(--c-text-dim); margin-top: auto; }
.module-card__title, .module-card__text, .module-card__list { margin: 0; } /* the card's flex gap already spaces the rows */
.module-card__arrow { color: var(--c-brand-text); transition: transform .25s ease; }
html:not(.js-hover) a.module-card:hover .module-card__arrow, a.module-card.is-hover .module-card__arrow { transform: translateX(4px); }

/* ── Stat band ───────────────────────────────────────────── */
.stat-band { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-4); }
.stat { padding: var(--sp-5); border-radius: var(--radius-lg); background: var(--c-tint-1); border: 1px solid var(--c-surface-border); text-align: center; }
.stat__value { font-size: var(--fs-3xl); font-weight: 800; color: var(--c-brand-text); font-variant-numeric: tabular-nums; line-height: 1.1; }
.stat__label { display: block; margin-top: .4rem; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--c-text-dim); }

/* ── Steps (vertical, line draws on scroll via [data-draw]) ── */
.steps { position: relative; display: grid; gap: var(--sp-6); padding-left: 2.6rem; }
.steps__line { position: absolute; left: .78rem; top: .4rem; bottom: .4rem; width: 2px; background: rgba(215, 69, 31, .2); border-radius: 1px; overflow: hidden; }
.steps__line i { position: absolute; inset: 0; background: var(--c-brand); transform-origin: top; display: block; }
html:not(.js) .steps__line i, .os-page.no-gsap .steps__line i { transform: none; }
.step { position: relative; }
.step__num { position: absolute; left: -2.6rem; top: .05rem; width: 1.7rem; height: 1.7rem; border-radius: 50%; background: var(--c-surface); border: 2px solid var(--c-brand); display: grid; place-items: center; font-size: .72rem; font-weight: 800; color: var(--c-brand-text); }
.step h3 { font-size: var(--fs-lg); margin-bottom: .3rem; }
.step p { color: var(--c-text-muted); font-size: var(--fs-sm); max-width: 62ch; }
.step__meta { font-size: var(--fs-xs); color: var(--c-text-dim); text-transform: uppercase; letter-spacing: .06em; margin-bottom: .25rem; }
/* .step p (0,1,1) would otherwise win: the meta label stays small and dim under the H3's weight. */
.step p.step__meta { font-size: var(--fs-xs); color: var(--c-text-dim); }

/* ── FAQ ─────────────────────────────────────────────────── */
.faq { display: grid; gap: .75rem; }
.faq details { background: var(--c-surface); border: 1px solid var(--c-surface-border); border-radius: var(--radius); padding: 0 var(--sp-5); }
.faq summary { cursor: pointer; padding: var(--sp-4) 0; font-weight: 600; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font-size: 1.3rem; line-height: 1; color: var(--c-brand-text); transition: transform .25s ease; }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details p, .faq details ul { padding-bottom: var(--sp-4); color: var(--c-text-muted); font-size: var(--fs-sm); }
.faq details ul { padding-left: 1.2rem; list-style: disc; }

/* ── Sources chips ───────────────────────────────────────── */
.sources { display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; padding: 0; margin: 0; }
.sources li { padding: .38rem .75rem; border-radius: 999px; border: 1px solid var(--c-surface-border); background: var(--c-tint-1); font-size: var(--fs-xs); color: var(--c-text-muted); }
.sources li strong { color: var(--c-text); font-weight: 600; }

/* ── Price teaser ────────────────────────────────────────── */
.price-teaser { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-6); border-radius: var(--radius-lg); background: linear-gradient(135deg, rgba(215, 69, 31, .14), var(--c-tint-1)); border: 1px solid rgba(215, 69, 31, .3); }
.price-teaser__amount { font-size: var(--fs-2xl); font-weight: 800; font-variant-numeric: tabular-nums; }
.price-teaser__amount small { font-size: var(--fs-sm); color: var(--c-text-muted); font-weight: 500; margin-left: .3rem; }
.price-teaser__note { font-size: var(--fs-xs); color: var(--c-text-dim); margin-top: .25rem; }
.price-teaser > div { flex: 1 1 320px; min-width: 0; } /* text column shrinks beside the button instead of wrapping it below */
.price-teaser__annual { font-size: var(--fs-sm); color: var(--c-text-muted); font-variant-numeric: tabular-nums; margin: .1rem 0 0; } /* annual amount under the monthly headline */

/* ── Split frames: keep screenshots stable ───────────────── */
.os-page .split__frame img, .os-page .showcase-panel__media img { width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover; object-position: top; }
/* The media cell is a <figure>: Bootstrap's reboot gives it margin-bottom 1rem, which left a 16 px strip of the panel
   under the dark screenshot (bright in the light theme). Same in both themes, so a theme switch never changes the
   panel's height. */
.os-page .showcase-panel__media { margin: 0; }
.os-page .governance__shot img { aspect-ratio: 5 / 3; }
.shot-note { font-size: var(--fs-xs); color: var(--c-text-dim); margin-top: .5rem; }
/* In a showcase panel .showcase-panel__content p (os-base) would make the note look like a second caption. */
.showcase-panel__content .shot-note { font-size: var(--fs-xs); color: var(--c-text-dim); line-height: 1.5; }

/* ── Two-column feature rows with CSS visuals ────────────── */
.feature-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--sp-8); align-items: center; }
.feature-row__visual { position: relative; min-height: 240px; border-radius: var(--radius-xl); overflow: hidden; background: var(--c-surface); border: 1px solid var(--c-surface-border); }

/* ── CTA band (shared) ───────────────────────────────────── */
.os-cta { position: relative; overflow: hidden; padding: var(--sp-16) 0; background: var(--c-bg-alt); text-align: center; border-top: 1px solid var(--c-surface-border); }
.os-cta__glow { position: absolute; left: 50%; top: 50%; width: 900px; height: 900px; transform: translate(-50%, -50%); background: radial-gradient(closest-side, rgba(215, 69, 31, .22), transparent 70%); pointer-events: none; }
.os-cta__inner { position: relative; }
.os-cta__title { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 800; margin-bottom: var(--sp-3); }
.os-cta__subtitle { color: var(--c-text-muted); max-width: 60ch; margin: 0 auto var(--sp-8); }
.os-cta__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; }

/* ── Layout helpers ──────────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--sp-6); }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--sp-5); }
.card { padding: var(--sp-6); background: var(--c-surface); border: 1px solid var(--c-surface-border); border-radius: var(--radius-lg); }
.card h3 { font-size: var(--fs-lg); margin-bottom: .4rem; }
.card p, .card li { color: var(--c-text-muted); font-size: var(--fs-sm); }
.card__icon { width: 2.4rem; height: 2.4rem; border-radius: .7rem; display: grid; place-items: center; background: rgba(215, 69, 31, .14); color: var(--c-brand-text); font-size: 1.2rem; margin-bottom: var(--sp-3); }
.lede { font-size: var(--fs-lg); color: var(--c-text-muted); max-width: 70ch; }
.muted { color: var(--c-text-muted); }
.mt-2 { margin-top: var(--sp-2); } .mt-4 { margin-top: var(--sp-4); } .mt-6 { margin-top: var(--sp-6); } .mt-8 { margin-top: var(--sp-8); } .mt-12 { margin-top: var(--sp-12); }
.mb-4 { margin-bottom: var(--sp-4); } .mb-6 { margin-bottom: var(--sp-6); } .mb-8 { margin-bottom: var(--sp-8); }
.text-center { text-align: center; }

/* ── Shared corrections after the first page builds (2026-11-02) ── */
/* Bootstrap's global .card (dark text, flex, margin) leaks into the section's .card helper. */
.os-page .card { display: block; color: var(--c-text); margin-bottom: 0; background: var(--c-surface); border: 1px solid var(--c-surface-border); border-radius: var(--radius-lg); }
.os-page .card h3, .os-page .card h4, .os-page .card h5, .os-page .card strong { color: var(--c-text); }
/* os-base resets ul but not ol: no native markers next to .step__num. */
.os-page .steps, .os-page ol.steps { list-style: none; margin: 0; }
/* custom.css has a site-wide #hero { margin-top: -52px; padding-top: 0 } for the home page. */
.os-page #hero.os-hero { margin-top: 0; padding-top: var(--sp-16); }
/* Sticky configurator summary must clear the fixed header and the sub navigation. */
.os-page .licenser__summary { top: calc(72px + var(--os-subnav-h) + 10px); }
/* Split sections: single, shrinkable column on phones. */
@media (max-width: 768px) { .os-page .split, .os-page .split-feature .split { grid-template-columns: minmax(0, 1fr); } }
/* A single 16:10 image should not be letterboxed by the old min-height. */
.os-page .showcase-panel { min-height: 0; }
/* One section rhythm for the whole section instead of per-page overrides. */
.os-page .section { padding: var(--sp-16) 0; }
.os-page .section__header { margin-bottom: var(--sp-10); }
.os-page .section--tight { padding: var(--sp-10) 0; }
/* Sub navigation: nine links plus actions must fit from 1200 px in both languages (brand text breakpoint above). */
.os-subnav__link { padding: .4rem .5rem; font-size: .8rem; }
/* 992–1099 px: the logo is only the symbol, but the English labels still needed 36 px more than the row has at 992 px
   ("Pricing" was cut to "Pri"). Tighter link padding and gap give 47 px back; the type size stays. */
@media (min-width: 992px) and (max-width: 1099.98px) {
  .os-subnav__links { gap: .1rem; }
  .os-subnav__link { padding-inline: .38rem; }
}

/* ── Review round 1 (2026-11-02): shared corrections ── */
/* The site header is 68 px on desktop and 110 px on phones; os-motion.js measures it. */
:root { --site-header-h: 68px; }
.os-subnav { top: var(--site-header-h); }
/* The fixed site header and the sticky sub navigation cover the top of the window. scroll-padding on the root keeps
   both anchor jumps and the browser's scroll to a focused element (Tab and Shift+Tab, WCAG 2.4.11) below them;
   scroll-margin on [id] only helped anchors. os-product.css is loaded only in this section. */
html { scroll-padding-top: calc(var(--site-header-h) + var(--os-subnav-h) + 16px); }
/* The subnav itself is sticky inside that padding zone; without this, focusing or clicking its links and buttons
   mid-page made Chrome scroll the page up to "reveal" them. */
.os-subnav a, .os-subnav button { scroll-margin-top: calc(-1 * (var(--site-header-h) + var(--os-subnav-h) + 16px)); }
.os-page .licenser__summary { top: calc(var(--site-header-h) + var(--os-subnav-h) + 10px); }
/* Scene chrome stays above the canvas. */
.scene__canvas { z-index: 1; }
.scene__label, .scene__hint, .scene__fallback-note, .scene__start { z-index: 2; }
.scene__label, .scene__hint, .scene__fallback-note { text-shadow: 0 1px 2px rgba(0, 0, 0, .6); }
html[data-theme="light"] .scene__label, html[data-theme="light"] .scene__hint, html[data-theme="light"] .scene__fallback-note { text-shadow: 0 1px 2px rgb(255 255 255 / .85); }
/* One scene + caption component for every page. */
.scene-row { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(280px, 1fr); gap: var(--sp-8); align-items: center; }
@media (max-width: 991.98px) { .scene-row { grid-template-columns: 1fr; } }
/* Module pages: numbered flow beside a sticky status ladder (assessments, agreements). */
.how-grid { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: var(--sp-10); align-items: start; }
/* A wrapper around the ladder (agreements) must span the whole row, otherwise the sticky ladder has no room to travel. */
.how-grid > .how-aside { align-self: stretch; }
.how-grid .steps { gap: var(--sp-4); }
.how-grid .step p { max-width: none; }
.status-ladder { position: sticky; top: calc(var(--site-header-h) + var(--os-subnav-h) + var(--sp-6)); }
.status-ladder__list { list-style: none; display: grid; gap: .5rem; margin: var(--sp-4) 0 0; padding: 0; }
.status-ladder__list li { display: flex; align-items: center; gap: .75rem; padding: .55rem .75rem; border-radius: var(--radius); background: var(--c-tint-1); border: 1px solid var(--c-surface-border); font-size: var(--fs-sm); color: var(--c-text-muted); }
.status-ladder__list .status { flex: none; min-width: 9.5rem; justify-content: center; }
@media (max-width: 991.98px) { .how-grid { grid-template-columns: minmax(0, 1fr); } .status-ladder { position: static; } .status-ladder__list li { flex-wrap: wrap; } }
/* Phones: every rung the same shape, the status above its explanation. */
@media (max-width: 575.98px) {
  .status-ladder__list li { flex-direction: column; align-items: flex-start; gap: .35rem; }
  .status-ladder__list .status { min-width: 0; }
}
.scene-steps { display: grid; gap: .5rem; list-style: none; margin: 0; padding: 0; }
.scene-steps__item { display: grid; grid-template-columns: 1.6rem 1fr; gap: .75rem; align-items: start; padding: var(--sp-4) var(--sp-5); border: 1px solid var(--c-surface-border); border-radius: var(--radius-lg); background: var(--c-surface); transition: border-color .25s ease, background .25s ease; }
.scene-steps__item i { width: 1.6rem; height: 1.6rem; border-radius: 50%; display: grid; place-items: center; font: 800 .72rem/1 var(--ff); font-style: normal; color: var(--c-brand-text); border: 1px solid rgba(215, 69, 31, .45); }
.scene-steps__item b { display: block; color: var(--c-text); font-size: var(--fs-base); margin-bottom: .15rem; }
.scene-steps__item div > span { color: var(--c-text-muted); font-size: var(--fs-sm); }
.scene-steps__item a { color: var(--c-brand-text); font-size: var(--fs-sm); }
.scene-steps__item.is-active { border-color: rgba(215, 69, 31, .55); background: rgba(215, 69, 31, .08); }
.scene-steps__item.is-active i { background: var(--c-brand-fill); color: #fff; border-color: transparent; }
@media (hover: none) { .scene__hint { display: none; } }
/* Sound toggle: static bars; a short pulse only while a sound actually plays. */
/* (No "animation: none" here: this selector outranks .is-playing and would stop the pulse.) One .55 s pulse; the
   last bar starts .24 s late, and os-sound.js keeps .is-playing 800 ms, so no bar snaps back mid-swing. */
html:not(.os-sound-off):not(.os-reduce) .sound-toggle .sound-toggle__icon i { transform: scaleY(.6); }
.sound-toggle.is-playing .sound-toggle__icon i { animation: os-eq .55s ease-in-out 1; }
.sound-toggle.is-playing .sound-toggle__icon i:nth-child(2) { animation-delay: .08s; }
.sound-toggle.is-playing .sound-toggle__icon i:nth-child(3) { animation-delay: .16s; }
.sound-toggle.is-playing .sound-toggle__icon i:nth-child(4) { animation-delay: .24s; }
/* Six cards become 3 + 3 instead of 4 + 2. */
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
/* Make the dark/light rhythm visible. */
:root { --c-bg-alt: #141418; }
.os-page .section--dark { border-top: 1px solid var(--c-surface-border); border-bottom: 1px solid var(--c-surface-border); }
/* Phones: the hero signature should start within the first screen. */
@media (max-width: 575.98px) {
  .os-hero { padding-top: var(--sp-8); }
  .os-hero__lead { font-size: var(--fs-base); }
  .os-hero__proof { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin-top: var(--sp-4); padding-bottom: 2px; }
  .os-hero__proof::-webkit-scrollbar { display: none; }
  .proof-chip { white-space: nowrap; }
  /* The row scrolls sideways without a scrollbar: os-motion.js marks the side where chips are hidden and that edge
     fades out, so the clipped chip reads as "more this way". No fade when everything fits or a page wraps the row. */
  .os-hero__proof { --fade-l: 0px; --fade-r: 0px; }
  .os-hero__proof.has-more-start { --fade-l: 2rem; }
  .os-hero__proof.has-more-end { --fade-r: 3rem; }
  .os-hero__proof.has-more-start, .os-hero__proof.has-more-end {
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent);
  }
  .os-hero__visual { min-height: 0; margin-top: var(--sp-6); }
}
/* Stack: larger in the hero, legible label. */
.os-hero__visual .os-stack { width: min(360px, 78vw); }
.os-stack__base-label { font-size: .68rem; letter-spacing: .06em; }
/* Comparison table on phones scrolls instead of breaking words. */
@media (max-width: 767.98px) {
  .os-page .comparison-table { overflow-x: auto; }
  .os-page .comparison-table table { table-layout: auto; min-width: 640px; }
  .os-page .comparison-table th, .os-page .comparison-table td { word-break: normal; overflow-wrap: normal; }
}
/* Highlighted comparison column: os-base paints it #d7451f (3.9:1 on the tinted cells). Body text stays light,
   the header keeps the brand colour in the lighter text tone (about 5.4:1). */
.os-page .comparison-table__highlight { color: var(--c-text) !important; }
.os-page .comparison-table th.comparison-table__highlight { color: var(--c-brand-text) !important; }
/* Configurator: a quieter "on" state so the primary CTA keeps the hierarchy. */
.os-page .module-toggle[aria-checked="true"] { border-color: rgba(215, 69, 31, .3); background: rgba(215, 69, 31, .05); }
.os-page .licenser__core { border-color: var(--c-surface-border); }
.licenser__stack { min-height: 200px; display: grid; place-items: center; }
.licenser__stack .os-stack { width: min(240px, 100%); }

/* ── Review round 2 (2026-11-02): regressions reported by the page fixers ── */
/* The nowrap proof-chip row must not widen the hero text column on phones. */
.os-hero__grid > div { min-width: 0; }
@media (max-width: 991.98px) { .os-hero__grid { grid-template-columns: minmax(0, 1fr); } }
.os-hero { min-height: calc(100vh - var(--site-header-h) - var(--os-subnav-h)); }
/* Nothing below 11 px. */
.status { font-size: .72rem; }
.os-stack__base-label { font-size: .72rem; }
/* The configurator summary is static on tablets and phones (os-base sets position: relative there). */
@media (max-width: 1024px) { .os-page .licenser__summary { top: 0; } }
/* Denser caption list so six items match a 16:10 scene. */
.scene-steps__item { padding: var(--sp-3) var(--sp-4); }
/* Below 992 px: tighter rows. The list keeps every text; the active step shows as an overlay in the scene
   (.scene__caption, os-scene.js), so the list's height never changes while the visitor scrolls (hiding the other
   texts moved everything below the scene at each step change: CLS 0.2 on the hub at 390 px). */
@media (max-width: 991.98px) {
  .scene-row { gap: var(--sp-4); }
  .scene-steps { gap: .35rem; }
  .scene-steps__item { position: relative; padding: .55rem .8rem; }
}

/* ── Pinned scene (SPEC §4, Eliaz 2026-11-05) ──────────────────────────────────────────────────────────────────────
   <section class="section scene-pin" data-scene-pin><div class="scene-pin__stick"><div class="container">…
   The section is taller than the window and its content sticks below the header while the visitor scrolls through
   it; os-scene.js turns that scroll into the story (io.pin, helpers.story), 70 % of a window per step (--pin-step, in
   % of the window; set the number of steps with data-pin-steps="3".."7"; 4 by default) and half a step more at the end
   (--pin-tail), where the finished picture stays before the content lets go. A page may set its own --pin-step and
   --pin-tail (the hub's simple blocks: shorter steps, a longer hold). Not under
   reduced motion, not on windows lower than 560 px, and not when the content does not fit (.is-unpinned, set by
   os-scene.js): the section is then an ordinary one and the scene plays on time. Low windows drop the description
   (the scene's hint says the same) and tighten the spacing so the scene keeps its size. */
.scene-pin { --pin-steps: 4; --pin-step: 70; --pin-tail: .5; --pin-top: calc(var(--site-header-h) + var(--os-subnav-h)); --pin-ar: 1.6; --pin-chrome: 245px; }
.scene-pin[data-pin-steps="3"] { --pin-steps: 3; } .scene-pin[data-pin-steps="5"] { --pin-steps: 5; }
.scene-pin[data-pin-steps="6"] { --pin-steps: 6; } .scene-pin[data-pin-steps="7"] { --pin-steps: 7; }
@media (prefers-reduced-motion: no-preference) and (min-height: 560px) {
  .os-page .scene-pin:not(.is-unpinned) {
    padding: 0; height: calc(100vh - var(--pin-top) + (var(--pin-steps) + var(--pin-tail)) * var(--pin-step) * 1vh);
    height: calc(100svh - var(--pin-top) + (var(--pin-steps) + var(--pin-tail)) * var(--pin-step) * 1svh);
  }
  .scene-pin:not(.is-unpinned) > .scene-pin__stick {
    position: sticky; top: var(--pin-top); display: flex; align-items: center; box-sizing: border-box; padding: var(--sp-6) 0;
    min-height: calc(100vh - var(--pin-top)); min-height: calc(100svh - var(--pin-top));
  }
  .scene-pin:not(.is-unpinned) > .scene-pin__stick > .container { width: 100%; }
  .scene-pin:not(.is-unpinned) .section__header { margin-bottom: var(--sp-6); }
  /* The scene never grows taller than the room the rest of the stuck content leaves (--pin-chrome; --pin-ar is the
     scene's width / height). */
  .scene-pin:not(.is-unpinned) .scene { width: min(100%, (100svh - var(--pin-top) - var(--pin-chrome)) * var(--pin-ar)); margin-inline: auto; }
  /* The step buttons only fade in: a slide would push the last row past the stuck area for a moment. */
  html.js .os-page .scene-pin:not(.is-unpinned) .scene-steps [data-reveal]:not(.is-in) { transform: none; }
}
@media (prefers-reduced-motion: no-preference) and (min-height: 560px) and (max-height: 820px) {
  .scene-pin { --pin-chrome: 145px; }
  .scene-pin:not(.is-unpinned) .section__desc { display: none; }
  .scene-pin:not(.is-unpinned) > .scene-pin__stick { padding: var(--sp-4) 0; }
  .scene-pin:not(.is-unpinned) .section__header { margin-bottom: var(--sp-4); }
  .scene-pin:not(.is-unpinned) .scene-steps { gap: .35rem; }
  .scene-pin:not(.is-unpinned) .scene-steps__btn { gap: .6rem; padding: .5rem .8rem; }
  .scene-pin:not(.is-unpinned) .scene-steps__item b { font-size: var(--fs-sm); }
  .scene-pin:not(.is-unpinned) .scene-steps__body > span { font-size: var(--fs-xs); line-height: 1.45; }
}
/* Steps that are buttons (data-steps-click): a click or Enter scrolls to the step; passing over them changes nothing
   but a border tone, and not while the page scrolls. The active step's button has aria-current="step". */
.scene-steps[data-steps-click] .scene-steps__item { display: block; padding: 0; }
.scene-steps__btn {
  display: grid; grid-template-columns: 1.6rem 1fr; gap: .75rem; align-items: start; width: 100%; margin: 0;
  padding: var(--sp-3) var(--sp-4); border: 0; border-radius: inherit; background: none; color: inherit; font: inherit;
  text-align: left; cursor: pointer;
}
.scene-steps__btn:focus-visible { outline: 2px solid var(--c-brand-text); outline-offset: 2px; }
.scene-steps__body > span { display: block; color: var(--c-text-muted); font-size: var(--fs-sm); }
html.is-scrolling .scene-steps[data-steps-click] .scene-steps__item { pointer-events: auto; }
@media (hover: hover) {
  html:not(.is-scrolling) .scene-steps[data-steps-click] .scene-steps__item:not(.is-active):hover { border-color: rgb(var(--c-tint-rgb) / .22); }
}
@media (max-width: 991.98px) {
  .scene-steps__btn { padding: .55rem .8rem; }
}
/* Phones and tablets, pinned: the steps become one row of four numbered buttons; the active step's text is the
   caption inside the scene (os-scene.js), so nothing below the scene changes height while the story plays. */
@media (max-width: 991.98px) and (prefers-reduced-motion: no-preference) and (min-height: 560px) {
  .scene-pin { --pin-chrome: 320px; }
  .scene-pin:not(.is-unpinned) .scene-steps { grid-template-columns: repeat(var(--pin-steps), minmax(0, 1fr)); gap: .35rem; }
  .scene-pin:not(.is-unpinned) .scene-steps__btn { grid-template-columns: 1fr; justify-items: center; gap: .3rem; padding: .45rem .25rem; text-align: center; }
  .scene-pin:not(.is-unpinned) .scene-steps__body > span { display: none; }
  .scene-pin:not(.is-unpinned) .scene-steps__item b { font-size: .78rem; margin: 0; }
}
@media (max-width: 991.98px) and (prefers-reduced-motion: no-preference) and (min-height: 560px) and (max-height: 820px) {
  .scene-pin { --pin-chrome: 215px; }
}
/* Phones with five or more steps: the buttons show only their number (the caption in the scene names the step); the
   title stays in the button's name for screen readers. */
@media (max-width: 575.98px) and (prefers-reduced-motion: no-preference) and (min-height: 560px) {
  .scene-pin:is([data-pin-steps="5"], [data-pin-steps="6"], [data-pin-steps="7"]):not(.is-unpinned) .scene-steps__item b {
    display: block; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}
.mt-10 { margin-top: var(--sp-10); }
/* Stack blocks are decorative status elements (the legend, module cards and the 3D scene carry the links). */
.os-stack__mod { cursor: default; }
a.os-stack__mod { cursor: pointer; }
/* Hero text paints immediately. The lead paragraph is usually the largest contentful paint,
   and any entrance delay (JS reveal or CSS animation) pushes LCP behind the scripts in the
   Lighthouse simulation. The H1 word intro and the hero visual carry the motion instead. */
html.js .os-page .os-hero [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; animation: none !important; }

/* ── Review round 3 (2026-11-03) ── */
/* The site's custom.css sets p, li to 1rem below 768 px. Inside the section a list item inherits its list's size,
   so card lists and hero compositions keep their scale; every class rule in the section still wins over this. */
@media (max-width: 768px) { :where(.os-main) li { font-size: inherit; } }

/* ── Stack filter "Visa för" (hub hero, 2026-11-03; SPEC §3 Stack filter, os-motion.js §8) ──
   Choices above a .os-stack, one area below it for the legend and the panels. New classes only:
   the stack's .is-on / .is-off / .os-stack--controlled rules above are unchanged. */
.os-filter { display: grid; justify-items: center; gap: .4rem; width: min(100%, 540px); justify-self: center; }
.os-filter__label { font-size: var(--fs-xs); font-weight: 600; letter-spacing: .06em; color: var(--c-text-dim); }
.os-filter__pills { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .4rem; width: 100%; }
.os-page .os-filter__pill {
  display: flex; align-items: center; justify-content: center; min-height: 36px; padding: .3rem .7rem;
  border: 1px solid var(--c-surface-border); border-radius: 999px; background: var(--c-tint-1);
  color: var(--c-text-muted); font-size: .8rem; font-weight: 600; line-height: 1.2; text-align: center; text-decoration: none;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.os-page .os-filter__pill:hover { color: var(--c-text); border-color: rgba(215, 69, 31, .45); }
/* Tinted, never filled: "Boka demo" stays the only filled button in the hero. */
.os-page .os-filter__pill.is-active { color: var(--c-accent-soft); background: rgba(215, 69, 31, .18); border-color: rgba(215, 69, 31, .6); }
@media (pointer: coarse) { .os-page .os-filter__pill { min-height: 44px; } }
/* "core" choice: every block is off (.is-off) and the base slab glows. */
[data-stack-filter] .os-stack__base { transition: border-color .5s ease, box-shadow .5s ease, background .5s ease; }
.os-stack--core .os-stack__base {
  background: linear-gradient(135deg, #3b1e16, #1c1210); border-color: rgba(240, 112, 77, .85);
  box-shadow: 0 0 0 1px rgba(215, 69, 31, .5), 0 0 38px rgba(215, 69, 31, .5), 0 50px 70px rgb(var(--c-shadow-rgb) / calc(.5 * var(--c-shadow-k)));
}
.os-stack--core .os-stack__base-label { color: var(--c-accent-soft); }
html[data-theme="light"] .os-stack--core .os-stack__base {
  background: linear-gradient(135deg, #fde6dd, #f5cbbb); border-color: rgba(215, 69, 31, .75);
  box-shadow: 0 0 0 1px rgba(215, 69, 31, .35), 0 0 34px rgba(215, 69, 31, .32), 0 50px 70px rgb(var(--c-shadow-rgb) / calc(.5 * var(--c-shadow-k)));
}
html[data-theme="light"] .os-stack--core .os-stack__base-label { color: var(--c-accent-soft); }
/* Legend and panels share one area. From 992 px every child sits in the same cell, so the area is as tall as the
   tallest panel in the current language and nothing around it moves when the choice changes. Hidden children keep
   their box but stay invisible, unfocusable and out of the accessibility tree (Bootstrap's [hidden] is !important). */
.os-filter-area { display: grid; width: min(100%, 540px); justify-self: center; }
@media (min-width: 992px) {
  .os-filter-area > * { grid-area: 1 / 1; align-self: start; }
  .os-filter-area > [hidden] { display: grid !important; visibility: hidden; }
}
[data-stack-filter].is-filter-used .os-filter-area > :not([hidden]) { animation: os-filter-in .3s var(--ease-out); }
@keyframes os-filter-in { from { opacity: 0; transform: translateY(4px); } }
.os-filter-panel { display: grid; gap: .6rem; align-content: start; }
.os-filter-panel__head { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .6rem; margin: 0; font-size: var(--fs-base); font-weight: 700; line-height: 1.3; color: var(--c-text); }
/* Rows reuse .os-stack-legend (markers in the module colour); icon | name | text, the names in one column. */
.os-filter-panel__rows { gap: .4rem; }
.os-filter-panel__rows li { display: grid; grid-template-columns: 1.4rem 11.5rem minmax(0, 1fr); column-gap: .65rem; align-items: start; }
@supports (grid-template-columns: subgrid) {
  .os-filter-panel__rows { grid-template-columns: 1.4rem max-content minmax(0, 1fr); column-gap: .65rem; }
  .os-filter-panel__rows li { grid-column: 1 / -1; grid-template-columns: subgrid; }
}
.os-filter-panel__rows li > a, .os-filter-panel__rows li > strong { font-weight: 600; line-height: 1.4rem; color: var(--c-text); white-space: nowrap; }
.os-filter-panel__rows li > a:hover { color: var(--c-brand-text); }
.os-filter-panel__text { color: var(--c-text-dim); font-size: .8125rem; line-height: 1.35; padding-top: .15rem; }
.os-filter-panel__text .status { margin: 0 .4rem 0 0; vertical-align: 1px; } /* chip before or after the text: a space separates it, never indented */
.os-filter-panel__rows i.os-filter-panel__base { background: linear-gradient(135deg, #2a2a30, #17171b); border: 1px solid rgb(var(--c-tint-rgb) / .35); color: #fff; }
html[data-theme="light"] .os-filter-panel__rows i.os-filter-panel__base { background: linear-gradient(135deg, #ffffff, #e9e4de); color: var(--c-text); }
.os-filter-panel__actions { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem 1.25rem; margin: .1rem 0 0; font-size: var(--fs-sm); }
.os-page .os-filter-panel__cta { display: inline-flex; align-items: center; gap: .35rem; color: var(--c-brand-text); font-weight: 600; }
.os-page .os-filter-panel__cta:hover { color: var(--c-accent-soft); }
.os-filter-panel__cta .bi { transition: transform .2s ease; }
.os-filter-panel__cta:hover .bi { transform: translateX(3px); }
.os-page .os-filter-panel__reset { color: var(--c-text-muted); text-decoration: underline; text-decoration-color: rgb(var(--c-tint-rgb) / .3); text-underline-offset: 3px; }
.os-page .os-filter-panel__reset:hover { color: var(--c-text); }
@media (max-width: 991.98px) {
  /* The legend/panel area comes right after the choices, the stack after it: a tap opens the panel where the
     reader is looking. :where() keeps this at class weight, so a page may still lift the stack above the choices
     (order: -1); the panel stays directly under the choices either way. Focus order follows the markup. */
  :where([data-stack-filter]) > .os-filter-area { order: 1; }
  :where([data-stack-filter]) > .os-stack-scene { order: 2; }
  /* One column, no reserved height; the two action links each on their own row with a 44 px target
     (the module names are inline links spaced about 28 px apart, which meets WCAG 2.5.8). */
  .os-filter-panel__actions { display: grid; justify-items: start; gap: 0; }
  .os-page .os-filter-panel__cta, .os-page .os-filter-panel__reset { min-height: 44px; display: inline-flex; align-items: center; }
}
@media (max-width: 575.98px) {
  .os-filter__pills { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .os-page .os-filter__pill { min-height: 44px; }
  /* Name above its text. */
  .os-filter-panel__rows, .os-filter-panel__rows li { grid-template-columns: 1.4rem minmax(0, 1fr); }
  .os-filter-panel__text { grid-column: 2; padding-top: 0; }
  .os-filter-panel__rows li > a, .os-filter-panel__rows li > strong { white-space: normal; }
}

/* ── Light theme (contract A, 2026-11-03): surfaces that stand on the page ──
   Tints read as inset panels inside white cards; panels that stand on the page itself are white cards instead, and the
   ghost button is a white pill, so nothing looks like a grey hole on the warm background. Dark values are unchanged. */
html[data-theme="light"] .stat { background: var(--c-surface); }
html[data-theme="light"] .os-page .btn--ghost { background: rgb(255 255 255 / .72); border-color: var(--c-line); }
html[data-theme="light"] .os-page .btn--ghost:hover { background: var(--c-surface); border-color: rgb(var(--c-tint-rgb) / .22); }
