/* Opelli marketing page — black / white / one accent, light only. */

/* Lexend in two subsets, exactly the way Google serves it. The base file is
   latin only (U+0000–00FF): it has no č, ř, š, ž, ě, ů — so "Kč", "IČO" and
   the company's own address fell back to the system font, mid-word. The second
   file is latin-ext, and `unicode-range` on BOTH is what makes the split work:
   a face without one claims every codepoint, and the browser would keep
   choosing the base file for a glyph it does not have. Same family, same
   version (1.007), same 1000 upem — the two are interchangeable per glyph. */
@font-face {
  font-family: "Lexend";
  src: url("lexend.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Lexend";
  src: url("lexend-ext.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-display: swap;
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --ink: #101623;
  --ink-soft: #4b5565;
  --muted: #8a93a5;
  --line: #e8ebf1;
  --paper: #ffffff;
  --paper-soft: #f7f8fb;
  --accent: #2e5fe8;
  --accent-soft: #e9eefd;
  --radius: 14px;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html {
  scroll-behavior: smooth;
}

/* ---------------------------------------------------------------------------
   Cross-document view transitions. Navigating between pages used to be a white
   flash and a jump to the top; now the old page cross-fades into the new one
   and the chrome that is on both stays put.

   Two CSS lines do the whole thing — no router, no bundle, no client-side
   navigation. Browsers without it navigate exactly as before, so this is
   enhancement and nothing more.

   The named elements are the ones a reader tracks across the jump: the top bar
   and the agenda rail hold still while the content under them changes, which
   is what makes the move read as one page rather than two. A name must be
   UNIQUE per document, so it goes on chrome that appears once.
--------------------------------------------------------------------------- */
@view-transition {
  navigation: auto;
}
.nav {
  view-transition-name: site-nav;
}
.agenda-rail {
  view-transition-name: agenda-rail;
}
.footer {
  view-transition-name: site-footer;
}

::view-transition-old(root) {
  animation-duration: 0.22s;
}
::view-transition-new(root) {
  animation-duration: 0.28s;
}
/* The chrome does not cross-fade with the page; it simply stays. */
::view-transition-group(site-nav),
::view-transition-group(agenda-rail),
::view-transition-group(site-footer) {
  animation-duration: 0.2s;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
body {
  font-family:
    "Lexend",
    system-ui,
    -apple-system,
    sans-serif;
  color: var(--ink);
  background: var(--paper);
  line-height: 1.6;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}
img {
  max-width: 100%;
  height: auto;
  display: block;
}
.container {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 24px;
}

h1 {
  font-size: clamp(40px, 6vw, 68px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-weight: 650;
}
h2 {
  font-size: clamp(26px, 3.6vw, 38px);
  line-height: 1.15;
  letter-spacing: -0.015em;
  font-weight: 650;
}
h4 {
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 6px;
}
/* The reset zeroes every margin, so a paragraph following a heading sat on it.
   Restored on the ADJACENCY rather than on the heading, so it only lands where
   prose actually follows — and at one class of specificity, so every component
   that already spaces its own paragraph (`.section-sub`, `.agenda p`,
   `.tile-each`, `.faq p`) still wins and keeps the spacing it was designed
   with. */
h2 + p,
h3 + p {
  margin-top: 14px;
}

.kicker {
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
}
.section-sub {
  color: var(--ink-soft);
  max-width: 640px;
  margin-top: 16px;
}
.section-sub.center {
  margin-inline: auto;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 8px 18px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 550;
  text-decoration: none;
  cursor: pointer;
  transition:
    transform 0.15s ease,
    box-shadow 0.15s ease,
    background 0.15s ease;
}
.btn-lg {
  padding: 12px 26px;
  font-size: 15.5px;
}
.btn-primary {
  background: var(--accent);
  color: #fff;
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 24px rgba(46, 95, 232, 0.35);
}
.btn-ghost {
  border-color: var(--line);
  color: var(--ink);
  background: var(--paper);
}
.btn-ghost:hover {
  border-color: var(--ink);
}

/* Nav */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(12px);
  transition: box-shadow 0.2s ease;
}
/* A shadow rather than a border, because a border takes SPACE: adding one on
   scroll grew the bar by a pixel and pushed everything under it down by a
   pixel — visible as a twitch in the agenda rail, which is sticky against the
   bar's height. A shadow paints outside the box and moves nothing. */
.nav.scrolled {
  box-shadow: 0 1px 0 var(--line);
}
/* With the rail under it the two are one header, so the bar does not draw its
   own line: the rail's hairline is the boundary, and a second one appearing
   mid-scroll reads as the header coming apart. */
body:has(.agenda-rail) .nav.scrolled {
  box-shadow: none;
}
.nav-inner {
  display: flex;
  align-items: center;
  gap: 28px;
  height: 64px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-weight: 650;
  font-size: 18px;
  color: var(--ink);
  text-decoration: none;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 24px;
  margin-left: auto;
}
.nav-links > a,
.nav-trigger {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 14.5px;
}
.nav-links > a:hover,
.nav-trigger:hover {
  color: var(--ink);
}
.nav-links > a[aria-current="page"] {
  color: var(--ink);
}

/* The EN/CS switch. It sits inside .nav-links so the burger panel inherits it
   on a phone with no rule of its own. site.js (initLangSwitch) retargets both
   links at the CURRENT page's twin on every navigation; the hrefs in the
   markup are only the no-JS fallback (the two landing pages). The links carry
   data-native: crossing languages swaps the chrome and the document language,
   which the <main>-swap router must never try. */
.lang {
  display: flex;
  align-items: center;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px;
}
.lang a {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12.5px;
  color: var(--muted);
  text-decoration: none;
}
.lang a:hover {
  color: var(--ink);
}
.lang a[aria-current="true"] {
  background: var(--paper-soft);
  color: var(--ink);
  font-weight: 550;
}
@media (max-width: 860px) {
  .lang {
    align-self: flex-start;
    margin: 10px 8px 0;
  }
}

/* The Features dropdown. site.js opens it on click AND on hover (fine pointers
   only) so `aria-expanded` stays truthful either way — a CSS :hover rule would
   open a menu the attribute still calls closed. */
.nav-item {
  position: relative;
}
.nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14.5px;
  cursor: pointer;
  padding: 0;
}
.nav-trigger svg {
  width: 11px;
  height: 11px;
  transition: transform 0.18s ease;
}
.nav-item.open .nav-trigger {
  color: var(--ink);
}
.nav-item.open .nav-trigger svg {
  transform: rotate(180deg);
}
.nav-panel {
  position: absolute;
  top: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%) translateY(-6px);
  width: 340px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px -24px rgba(16, 22, 35, 0.3);
  padding: 8px;
  display: grid;
  gap: 2px;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.16s ease,
    transform 0.16s ease,
    visibility 0.16s;
}
.nav-item.open .nav-panel {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}
.nav-panel a {
  display: block;
  padding: 9px 12px;
  border-radius: 9px;
  text-decoration: none;
}
.nav-panel a:hover {
  background: var(--paper-soft);
}
.nav-panel b {
  display: block;
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 600;
}
.nav-panel span {
  display: block;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}
/* A hairline above the last entry: the overview link is a different kind of
   destination from the five agendas above it. */
.nav-panel .nav-panel-all {
  border-top: 1px solid var(--line);
  margin-top: 6px;
  padding-top: 12px;
  border-radius: 0 0 9px 9px;
}

/* The phone bar: a burger that drops the whole nav down as a panel. Hidden on
   desktop, and the only thing in the bar besides the mark and the CTA below
   860px. */
.nav-burger {
  display: none;
  margin-left: auto;
  border: 1px solid var(--line);
  background: var(--paper);
  border-radius: 10px;
  padding: 7px 9px;
  cursor: pointer;
  color: var(--ink);
}
.nav-burger svg {
  width: 18px;
  height: 18px;
  display: block;
}

/* Hero + the signal curves (site.js animates the three paths) */
/* `overflow-x: clip`, never `overflow: hidden`: the clipping is only ever meant
   to keep the signal curves and the hero shot's pointer tilt off the page's
   horizontal axis, and `hidden` on both axes sliced the hero screenshot's drop
   shadow flat along the section's bottom edge. `clip` is the one value that
   takes a single axis — `overflow-y: visible` beside `overflow-x: hidden` is
   silently promoted back to auto, which is why the pair cannot express this. */
.hero {
  position: relative;
  padding: 56px 0 40px;
  overflow-x: clip;
}
.hero-signals {
  position: absolute;
  inset: 0 0 auto;
  width: 100%;
  height: 520px;
  pointer-events: none;
}
.hero-signals path {
  fill: none;
  stroke: var(--accent);
}
.hero-signals path:nth-child(1) {
  stroke-width: 1.4;
  opacity: 0.07;
}
.hero-signals path:nth-child(2) {
  stroke-width: 2;
  opacity: 0.1;
}
.hero-signals path:nth-child(3) {
  stroke-width: 2.6;
  opacity: 0.13;
}
.hero-inner {
  position: relative;
  text-align: center;
}
/* The headline used to open on a rotating word (Company · Solopreneur ·
   Startup), anchored so only that word moved. The constellation below took
   over that job in v2 — it says the same thing with the agendas themselves,
   and two independent word animations in one viewport compete. The headline is
   one fixed sentence now. */
/* Wider than the 620 px it used to be: with the scene now standing between the
   headline and this, a narrow column of seven short lines read as a second
   caption. Fewer, longer lines read as a paragraph. */
/* The headline now stands above the picture rather than under it, so it needs
   its own air below — otherwise it sits on the window's top edge. */
.hero h1 {
  margin-bottom: 1.5em;
}
.hero-sub {
  color: var(--ink-soft);
  max-width: 860px;
  margin: 5em auto 0;
  font-size: 18px;
}
.hero-cta {
  display: flex;
  gap: 12px;
  justify-content: center;
  margin-top: 32px;
}

/* ---------------------------------------------------------------------------
   The hero constellation — the Opelli mark, with the agendas it covers
   surfacing around it at a steady beat. site.js owns the words and the
   flashes; the look is here.

   Nothing in the mark moves on its own. A background chip that turned behind
   the rings was tried and removed: the only motion on this stage now belongs
   to the words, and the mark answers them — one ring and the arriving chip
   flash together, which is what ties the two halves of the picture into one
   thing rather than a logo with labels floating near it.
--------------------------------------------------------------------------- */
.cst {
  position: relative;
  width: 100%;
  max-width: 900px;
  height: clamp(280px, 36vw, 400px);
  margin: 8px auto 0;
}
.cst-core {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(168px, 24vw, 262px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
}
.cst-logo {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}
/* The narrow view's stand-in for the orbiting words — see the block at the end
   of this file. Hidden while the stage animates. */
.cst-wordmark {
  display: none;
  font-size: clamp(30px, 9vw, 40px);
  font-weight: 650;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
}
/* An optical correction, not a redraw: the mark's own 9-unit stroke is tuned
   for a 26px favicon-sized mark and reads as a blot at 260px, where three
   overlapping rings merge into one shape. Thinner strokes at large scale keep
   the three circles legible as three circles — the same mark, drawn for the
   size it is being drawn at.

   The rings are addressed one at a time by site.js, which flashes one of them
   to the accent as each word arrives — the mark reacting to what surfaces
   around it. That flash is a Web Animation rather than a CSS class: several
   words can land inside one flash's duration, and re-triggering a class
   animation means removing it, forcing a reflow and adding it back. `animate()`
   just starts another one. */
.cst-logo circle {
  stroke: var(--ink);
  stroke-width: 5.5;
  fill: none;
}

/* Chips are positioned entirely by transform on the OUTER div (left/top stay
   0), written once per frame by site.js. The arrival and departure live on the
   INNER span, so CSS can own them without the frame loop overwriting a
   transform it also wants to set: in on a slight zoom, out on a blur. */
.cst-word {
  position: absolute;
  left: 0;
  top: 0;
  will-change: transform;
}
.cst-word span {
  display: inline-block;
  white-space: nowrap;
  font-size: 14.5px;
  font-weight: 550;
  color: var(--ink-soft);
  /* Translucent rather than solid: the signal curves keep drifting behind the
     chips, and a solid pill would punch holes in them. */
  /* Nearly opaque, not translucent: the words sit close enough to the mark to
     land ON it, and a see-through pill over a 5.5px ring is unreadable. */
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 15px;
  box-shadow: 0 6px 18px -12px rgba(16, 22, 35, 0.4);
  opacity: 0;
  transform: scale(0.88);
  filter: blur(5px);
  transition:
    opacity 0.5s ease,
    transform 0.5s ease,
    filter 0.5s ease,
    color 0.3s ease,
    border-color 0.3s ease,
    box-shadow 0.3s ease;
}
.cst-word.on span {
  opacity: 1;
  transform: scale(1);
  filter: blur(0);
  /* Arrival is quicker than departure and overshoots a little, so a word
     lands; leaving it to the same curve made words fade in like a crossfade. */
  transition:
    opacity 0.34s ease-out,
    transform 0.42s cubic-bezier(0.18, 0.9, 0.28, 1.06),
    filter 0.34s ease-out;
}

/* Screenshot frames */
.shot {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper);
  box-shadow: 0 24px 60px -24px rgba(16, 22, 35, 0.25);
  transition:
    transform 0.25s ease,
    box-shadow 0.25s ease;
}
.shot:hover {
  transform: translateY(-4px);
  box-shadow: 0 32px 70px -24px rgba(16, 22, 35, 0.32);
}
.shot-bar {
  display: flex;
  gap: 6px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--paper-soft);
}
.shot-bar i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--line);
}
.hero-shot {
  margin: 64px auto 0;
  max-width: 980px;
  will-change: transform;
}
.shot-wide {
  max-width: 980px;
  margin: 40px auto;
}
/* A SECONDARY shot: narrower than the section's primary, so a second
   screenshot supports the first instead of competing with it (Tasks: the
   normal board leads, compact follows). */
.shot-secondary {
  max-width: 720px;
  margin: 18px auto 0;
}

/* Wiki showcase: big shot + overlapping editor shot + floating callouts */
.wiki {
  padding: 110px 0 40px;
}
.wiki-stage {
  position: relative;
  margin-top: 48px;
  padding-bottom: 130px;
}
.wiki-shot-main {
  max-width: 880px;
}
.wiki-shot-editor {
  position: absolute;
  right: 24px;
  bottom: 0;
  width: min(46%, 470px);
  transform: rotate(1.2deg);
}
.wiki-shot-editor:hover {
  transform: rotate(0deg) translateY(-4px);
}
.callout {
  position: absolute;
  max-width: 230px;
  background: var(--ink);
  color: #fff;
  font-size: 13px;
  line-height: 1.45;
  padding: 10px 14px;
  border-radius: 12px;
  box-shadow: 0 12px 30px -12px rgba(16, 22, 35, 0.5);
}
.callout::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 22px;
  width: 10px;
  height: 10px;
  background: var(--ink);
  transform: rotate(45deg);
}
.co-1 {
  top: 42px;
  right: 6%;
}
.co-2 {
  top: 46%;
  left: -8px;
}
.co-2::before {
  left: auto;
  right: 26px;
}
.co-3 {
  bottom: 46px;
  right: 40%;
}
.wiki .mini-grid {
  margin-top: 8px;
}

/* Chapter dividers — the spine between runs of module sections. Deliberately
   quieter than a section head (h3, hairline, no shot) so it reads as a marker,
   not a competing section. The following .mod pulls its top padding in. */
.chapter {
  text-align: center;
  padding: 76px 24px 0;
}
.chapter-rule {
  display: flex;
  align-items: center;
  gap: 18px;
}
.chapter-rule::before,
.chapter-rule::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.chapter-num {
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: 0.14em;
  color: var(--accent);
}
.chapter h3 {
  font-size: clamp(23px, 3vw, 31px);
  line-height: 1.2;
  letter-spacing: -0.015em;
  font-weight: 650;
  margin-top: 24px;
}
.chapter-sub {
  color: var(--ink-soft);
  font-size: 15.5px;
  max-width: 540px;
  margin: 10px auto 0;
}
.chapter-mods {
  margin-top: 16px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Module sections */
.mod {
  padding: 84px 0;
}
.chapter + .mod {
  padding-top: 44px;
}
.split {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: 56px;
  align-items: center;
}
.split .split-text {
  order: 0;
}
.split.flip > .split-text {
  order: 1;
}
.split-text > p {
  color: var(--ink-soft);
}
.aside {
  font-size: 14px;
  color: var(--muted);
  margin-top: 18px;
}

/* Feature ticks (checklist) */
.ticks {
  list-style: none;
  margin-top: 18px;
  display: grid;
  gap: 9px;
}
.ticks li {
  padding-left: 26px;
  position: relative;
  color: var(--ink-soft);
  font-size: 15px;
}
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 14px;
  height: 8px;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

/* Feature chips (compact tag row) */
.chips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 18px;
}
.chips li {
  border: 1px solid var(--line);
  background: var(--paper-soft);
  border-radius: 999px;
  padding: 4px 13px;
  font-size: 13.5px;
  color: var(--ink-soft);
}

/* Feature mini-grid (bold lead + one line) */
.mini-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px 28px;
  margin-top: 36px;
}
.mini-grid li {
  font-size: 14px;
  color: var(--ink-soft);
  border-top: 1px solid var(--line);
  padding-top: 12px;
}
.mini-grid b {
  display: block;
  color: var(--ink);
  font-weight: 600;
  margin-bottom: 3px;
  font-size: 15px;
}
/* Two of them, centred: Volume and Sovereign are a footnote to the table
   above, not a second set of tiers. */
.mini-grid-2 {
  grid-template-columns: repeat(2, 1fr);
  max-width: 820px;
  margin-inline: auto;
}

/* Full-width band sections */
.band {
  background: var(--paper-soft);
}
.band-plain {
  background: var(--paper);
}
.band-head {
  text-align: center;
}

/* Time collage: calendar behind, list in front */
.duo {
  position: relative;
  padding: 0 0 56px 0;
}
.duo-back {
  max-width: 86%;
}
.duo-front {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 72%;
  transform: rotate(-1.2deg);
}
.duo-front:hover {
  transform: rotate(0deg) translateY(-4px);
}

/* Band collage: the wide band shot with a smaller detail floating over its
   lower-right corner (Tasks, People) */
.band-duo {
  position: relative;
  padding-bottom: 96px;
}
.band-duo .shot-wide {
  max-width: 92%;
}
.band-duo-front {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 46%;
  transform: rotate(1.1deg);
}
.band-duo-front:hover {
  transform: rotate(0deg) translateY(-4px);
}
/* Tall variant: a portrait public-form page floating beside the wide shot
   (Forms) — narrower so its height stays inside the collage */
.band-duo-front-tall {
  width: 34%;
}
/* …and a taller-still variant, because the front shot hangs from `bottom: 0`
   and grows UPWARD: past a certain aspect it climbs out of the top of the
   collage and lands on the heading. It is arithmetic, not taste — the wide
   shot is 92% wide at 2000×1194, so the box is 0.92 × 0.597 = 0.549 of the
   container plus the padding, and the front is its own width × its aspect.
   Orders' public page is 1280 × 3146 (2.46), where Forms' is 2290 (1.79):
   at 34% it needed 895px of a 685px box. 29% and 200px of padding fit it with
   room to spare. A NEW PORTRAIT SHOT MUST BE CHECKED AGAINST THIS, not eyeballed. */
.band-duo-xtall {
  padding-bottom: 200px;
}
.band-duo-xtall .band-duo-front-tall {
  width: 29%;
}

/* Compliance & Audit — the governance pair: two stacked split blocks inside
   one soft band, so the modules read as halves of a single story rather than
   two more entries in the module run. */
.gov .band-head {
  margin-bottom: 8px;
}
.gov-block {
  padding-top: 48px;
}
.gov-caption {
  text-align: center;
  color: var(--ink-soft);
  font-size: 15px;
  margin-top: 56px;
}
.gov .shot-wide {
  margin-top: 18px;
}
.split-text h3 {
  font-size: clamp(21px, 2.7vw, 28px);
  line-height: 1.2;
  letter-spacing: -0.015em;
  font-weight: 650;
}

/* Security — a spec sheet, no screenshot: there is nothing to photograph about
   encryption at rest. Three columns of short facts, hairline-ruled like the
   mini-grid so it reads as the same family, and a plain note at the end for
   the one thing a security page must be careful about — what it does NOT
   claim. (Access was the page's only dark strip until 2026-07-31; it is a
   light band now, and the trust story is two light sections in a row.) */
.spec {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px 40px;
  margin-top: 48px;
}
/* Two columns instead of three, centred and capped: across the full container
   a two-column spec sheet runs to ~560px a line, which is longer than anything
   else on the page reads. */
.spec-2 {
  grid-template-columns: repeat(2, 1fr);
  max-width: 880px;
  margin-inline: auto;
}
.spec-col h3 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-weight: 650;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.spec-col ul {
  list-style: none;
  display: grid;
  gap: 12px;
  margin-top: 14px;
}
.spec-col li {
  font-size: 14.5px;
  color: var(--ink-soft);
  padding-left: 16px;
  position: relative;
}
.spec-col li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 2px;
  background: var(--accent);
  opacity: 0.5;
}
.note-plain {
  max-width: 760px;
  margin: 48px auto 0;
  text-align: center;
  color: var(--ink-soft);
  font-size: 15px;
}
.note-plain b {
  color: var(--ink);
  font-weight: 650;
}

/* Agents (MCP): chat transcripts — user bubble, tool chips, assistant reply */
.agents {
  padding: 96px 0;
}
.agent-lead {
  max-width: 620px;
  margin: 56px auto 26px;
  text-align: center;
}
.agent-lead h3 {
  font-size: clamp(21px, 2.6vw, 27px);
  font-weight: 650;
  letter-spacing: -0.012em;
  line-height: 1.25;
}
.agent-lead p {
  color: var(--ink-soft);
  font-size: 15px;
  margin-top: 12px;
}

.chat {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
  display: grid;
  gap: 12px;
  align-content: start;
  box-shadow: 0 18px 44px -30px rgba(16, 22, 35, 0.35);
  transition:
    transform 0.2s ease,
    border-color 0.2s ease;
}
.chat:hover {
  transform: translateY(-3px);
  border-color: #cdd6e6;
}
.chat-wide {
  max-width: 820px;
  margin: 0 auto;
}
.chat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
  margin-top: 26px;
}
/* Three cards in a row: the scenario trio on the agents page. Collapses with
   .chat-grid in the mobile query below. */
.chat-grid-3 {
  grid-template-columns: repeat(3, 1fr);
}
.chat-wall {
  margin-top: 26px;
}

.msg {
  font-size: 14.5px;
  line-height: 1.55;
}
.msg b {
  display: block;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.msg-you {
  background: var(--accent-soft);
  border-radius: 12px;
  padding: 12px 15px;
}
.msg-you b {
  color: var(--accent);
}
.msg-ai {
  color: var(--ink-soft);
  padding: 0 3px;
  display: grid;
  gap: 12px;
}
.msg-ai strong {
  color: var(--ink);
  font-weight: 600;
}
.msg-foot {
  font-size: 13px;
  color: var(--muted);
}
/* A filename inside a reply — plain inline code, deliberately NOT the chip
   treatment `.tools code` gets: those are tool calls, these are file paths. */
.msg-ai code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.88em;
  background: var(--paper-soft);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
}

.tools {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 3px;
}
.tools code {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  border: 1px solid var(--line);
  background: var(--paper-soft);
  color: var(--ink-soft);
  border-radius: 999px;
  padding: 3px 11px 3px 9px;
}
.tools code::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
.tools code.refused {
  border-color: #f0cdb8;
  background: #fdf2ec;
  color: #9a4a19;
}
.tools code.refused::before {
  background: #c2410c;
}

.md {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  line-height: 1.7;
  background: var(--ink);
  color: #c3cee4;
  border-radius: 12px;
  padding: 16px 18px;
  overflow-x: auto;
  white-space: pre;
}

/* A request sample standing on its own in a section — the Datasets section
   on the work agenda. The `.md` block is the agents page's dark markdown
   block; capped to the `.mini-grid-2` width beneath it so the two read as one
   column, and pushed apart from the grid the way a wide shot is. */
.md-sample {
  max-width: 820px;
  margin: 0 auto;
}
.md-sample + .mini-grid {
  margin-top: 40px;
}

.agent-notes {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px 36px;
  margin-top: 48px;
}
.agent-notes li {
  border-left: 2px solid var(--accent);
  padding-left: 16px;
  color: var(--ink-soft);
  font-size: 14.5px;
}
.agent-notes span {
  display: block;
  color: var(--ink);
  font-weight: 600;
  font-size: 15px;
  margin-bottom: 2px;
}

/* ---------------------------------------------------------------------------
   THE SCENE — one thing you can ask for on this agenda, played back.

   Two thirds is the product and one third is the conversation driving it, and
   the split is the whole idea: you read what was asked on the right and watch
   what it did on the left, at the same time.

   **The product side is drawn, not photographed.** Every shape here is a div —
   a title, a paragraph, a task card, an avatar — and none of it says anything.
   That is deliberate three times over: nothing in it can go stale when the app
   changes (the screenshots below it carry the real screens, and they are the
   claim); a shape reads at a glance where a screenshot of dense UI at half a
   column reads as grey; and nobody can mistake a diagram for a recording of
   something we do not do. The chat side is the opposite — REAL prompt text and
   REAL tool names out of the app's `server/mcp/tools.js`, exactly like the
   transcripts on the agents page, because those are the part a reader is meant
   to take literally.

   **The animation is data, not code.** `SCENES` in site.js is a beat sheet of
   `[ms, ...ops]`; an op flips a class or switches the stage's view. A second
   agenda's scene is another list in that table and no new JavaScript. Every
   state below is a class the beat sheet turns on, so the whole thing can be
   read in one place instead of chased through two files.

   It runs only while it is on screen, it restarts from the top when it comes
   back, and `prefers-reduced-motion` gets the last frame and nothing else.
--------------------------------------------------------------------------- */
.scene {
  display: grid;
  /* The hero centres everything inside it. A transcript reading down the middle
     is not a transcript, so a scene always sets its own alignment. */
  text-align: left;
  grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr);
  gap: 16px;
  align-items: stretch;
  margin-top: 36px;
}

/* THE HERO — the same scenes, opened out around the mark: product left,
   assistant right, the three rings standing between them as the thing both
   halves are talking through. The two windows are equal here, not 1.85 : 1,
   because the landing is selling the pair and neither may read as the main one.

   **All six scenes rotate through it**, and they are the SAME partials the
   agenda pages include — `partials/scene-*.html`, one copy, two homes. They are
   stacked on top of each other in one grid cell and only the current one is
   visible, which is also why the mark is the ROTATOR's rather than any scene's:
   six copies of it would be six things to keep in register.

   Every scene leaves an empty middle column of exactly the mark's width, and
   the mark is centred over it. That is the whole trick — the scenes never touch
   the mark, and the mark never has to know which scene is playing.
--------------------------------------------------------------------------- */
.hero-rotator {
  --mark: clamp(168px, 24vw, 262px);
  position: relative;
  display: grid;
  margin: 26px 0 6px;
}
.hero-rotator > .scene {
  grid-area: 1 / 1;
}
/* Three classes, so this beats `.reveal.in` without an `!important`: the scenes
   still scroll-reveal like everything else, and the rotator decides which one
   of them is on top of that. */
/* Out, THEN in — not a cross-dissolve. Two scenes at half opacity put one
   breadcrumb on top of another and one reply through another; the words are the
   half of a scene meant to be read, and two of them at once are neither. The
   incoming one waits out the outgoing one's fade, which is what the delay is.
   It plays from beat 0 the moment it is handed the clock, and beat 0 is the
   reset, so nothing is missed behind the fade. */
.hero-rotator > .scene.reveal {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.3s ease,
    visibility 0s linear 0.3s;
}
.hero-rotator > .scene.reveal.is-current {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.35s ease 0.3s,
    visibility 0s linear 0s;
}
/* With no JS nothing is current, and six hidden scenes are an empty hero. Show
   the first one — the same `:has()` the top bar already uses. It reads as a
   still screenshot, which is exactly what the agenda pages fall back to. */
.hero-rotator:not(:has(> .is-current)) > .scene.reveal:first-of-type {
  opacity: 1;
  visibility: visible;
}
/* The windows are drawn at the SAME internal size as an agenda page's — 447 CSS
   px — and then zoomed to 60%. Shrinking the track alone would have reflowed
   every shape inside into a different composition and clipped the board; `zoom`
   keeps the drawing identical and makes it smaller, which is what "smaller"
   should mean for a picture.

   The three numbers are ONE decision: 268 window / 0.6 zoom = 447 internal, and
   the track carries the window plus the 48 rendered px of margin beside the
   mark. Change any of them and change the others. */
.hero-rotator .scene {
  grid-template-columns:
    clamp(220px, 24vw, 316px) var(--mark)
    clamp(220px, 24vw, 316px);
  justify-content: center;
  gap: 26px;
  align-items: stretch;
  margin: 0;
}
/* Placed by hand, into columns 1 and 3. The middle one has to stay EMPTY —
   the mark is the rotator's child, not the scene's, so nothing occupies that
   track and auto-placement would happily drop the chat into it. It did, and the
   mark landed on top of the conversation. */
.hero-rotator .scene-app {
  grid-column: 1;
  zoom: 0.6;
}
.hero-rotator .scene-chat {
  grid-column: 3;
  zoom: 0.6;
}
/* Air on the side that faces the mark, so the windows flank it rather than
   close in on it. Inside the zoom, so 5em lands at 48 rendered px. */
.hero-rotator .scene-app {
  margin-right: 5em;
}
.hero-rotator .scene-chat {
  margin-left: 5em;
}
/* …and the chat's type comes back up, because it is the one part of a scene
   meant to be READ. At 0.6 the agenda pages' 13.5 px lands at 8 px, which is a
   picture of a conversation rather than a conversation. */
.hero-rotator .scene-thread .msg {
  font-size: 19px;
}
.hero-rotator .scene-thread .msg b,
.hero-rotator .scene-thread .tools code,
.hero-rotator .scene-crumb em {
  font-size: 15px;
}
.hero-rotator .scene-thread .tools code {
  padding: 3px 12px 3px 11px;
}

/* The mark is the size it was on the constellation, centred over the empty
   middle column every scene leaves for it. */
.scene-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: var(--mark);
  aspect-ratio: 1;
}
.scene-mark .cst-logo {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}
/* The constellation's own optical correction, unchanged: the mark's 9-unit
   stroke is tuned for a favicon and reads as a blot at 262 px. */
.scene-mark .cst-logo circle {
  stroke: var(--ink);
  stroke-width: 5.5;
  fill: none;
}
/* The ring flash, fired by the rotator — one ring per beat of whichever scene
   is playing, round-robin. Same colours and the same 720 ms ease-out the
   constellation used, because it is the same gesture: the mark answering what
   just happened. At rest there is no animation at all; the driver removes
   `.flash`, forces a reflow and adds it back, which is what makes it fire again
   on a beat that has already fired it once. */
.scene-mark .cst-logo circle.flash {
  animation: ringFlash 0.72s ease-out;
}
@keyframes ringFlash {
  0% {
    stroke: var(--ink);
    stroke-width: 5.5;
  }
  22% {
    stroke: var(--accent);
    stroke-width: 6.6;
  }
  100% {
    stroke: var(--ink);
    stroke-width: 5.5;
  }
}
/* Without `zoom` the windows would lay out at their full 447 px inside a 316 px
   track and push the mark off the page. Fall back to the full-size pair —
   bigger than intended, but a hero rather than a wreck. */
@supports not (zoom: 0.6) {
  .hero-rotator .scene {
    grid-template-columns: minmax(0, 1fr) var(--mark) minmax(0, 1fr);
  }
  .hero-rotator .scene-app,
  .hero-rotator .scene-chat {
    zoom: normal;
    margin: 0;
  }
  .hero-rotator .scene-thread .msg,
  .hero-rotator .scene-thread .msg b,
  .hero-rotator .scene-thread .tools code,
  .hero-rotator .scene-crumb em {
    font-size: inherit;
  }
}

/* The product side, as a window. */
.scene-app {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--paper);
  border: 1px solid #e3e7ef;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 26px 60px -42px rgba(16, 22, 35, 0.5);
}
/* Same three dots as `.shot-bar` on a real screenshot, so the drawn window and
   the photographed ones belong to one family. */
.scene-chrome {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 14px;
  background: var(--paper-soft);
  border-bottom: 1px solid var(--line);
}
.scene-chrome > i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #d7dce6;
}
/* The one piece of text in the drawn window: which project, and which module
   you are looking at. The two labels are stacked on top of each other so the
   swap is a cross-fade and never a reflow. */
.scene-crumb {
  position: relative;
  margin-left: 10px;
  height: 14px;
  flex: 1;
}
.scene-crumb em {
  position: absolute;
  inset: 0;
  font-style: normal;
  font-size: 11px;
  line-height: 14px;
  letter-spacing: 0.02em;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.35s ease;
}
.scene-crumb em.is-on {
  opacity: 1;
}

.scene-body {
  display: flex;
  min-height: 0;
  flex: 1;
}
/* The app's own left rail, purely as a silhouette. */
.scene-rail {
  flex: 0 0 40px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 14px 0;
  align-items: center;
  border-right: 1px solid var(--line);
  background: var(--paper-soft);
}
.scene-rail i {
  width: 16px;
  height: 16px;
  border-radius: 5px;
  background: #dfe4ee;
  transition: background 0.35s ease;
}
/* The rail follows the stage: each view declares which rail item it belongs to
   with `data-rail`, and site.js lights that one. Nothing here knows the names
   of any scene's views — that is what lets a new scene be markup and a beat
   sheet, with no CSS of its own. */
.scene-rail i.is-on {
  background: var(--accent);
}

/* Tall enough for the fullest frame the beat sheet ever reaches — the Todo
   column with all four new cards on it. The two views share the height, so the
   window does not resize under the reader when the stage changes. */
.scene-stage {
  position: relative;
  flex: 1;
  min-width: 0;
  min-height: 360px;
}
.scene-view {
  position: absolute;
  inset: 0;
  overflow: hidden; /* a scene is never allowed to spill out of its window */
  padding: 18px 20px;
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity 0.4s ease,
    transform 0.4s ease;
}
.scene-view.is-live {
  opacity: 1;
  transform: none;
}
/* With no JS there is no stage, so the window shows its opening frame and
   stands still — a blank pane would be worse than a still one. site.js takes
   over the moment it sets `data-stage`. */
.scene:not([data-stage]) .scene-view:first-of-type,
.scene:not([data-stage]) .scene-crumb em:first-of-type {
  opacity: 1;
  transform: none;
}

/* ONE arrival, for everything. Anything a beat sheet brings on screen — a chat
   bubble, a tool chip, a task card, a row of a version list, a block of a page
   being built — carries `data-in` and arrives the same way. Rising and fading
   is the product's own "this just happened", and having exactly one of them is
   what keeps five scenes looking like one product rather than five demos. */
.scene [data-in] {
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity 0.3s ease,
    transform 0.32s cubic-bezier(0.22, 1.2, 0.4, 1);
}
.scene [data-in].is-on {
  opacity: 1;
  transform: none;
}

/* The skeleton vocabulary: `--w` on the element is its width, so a paragraph
   ragged like real prose is written in the markup where it can be seen, not
   spread over a dozen one-off classes here. */
.sk-line,
.sk-title,
.sk-key {
  display: block;
  width: var(--w, 100%);
  border-radius: 3px;
  background: #e6e9f1;
}
.sk-title {
  height: 13px;
  border-radius: 4px;
  background: #cfd5e1;
}
.sk-line {
  height: 7px;
}
/* Prose: a stack of lines with one gap between them. Every container in this
   vocabulary sets its own gap and NO shape carries a margin — a margin on
   `.sk-line` meant every new container had to remember to cancel it, and the
   one that forgot (the microsite page) silently grew half again as tall and
   pushed its own footer out of the window. */
.sk-flow {
  display: grid;
  align-content: start;
  gap: 9px;
}

/* The four decisions in the note. Each lights as the assistant reads it, and
   the same four land on the board a beat later — that pairing is the point of
   the whole scene, so the two use the same accent and the same rhythm. */
.sk-picks {
  list-style: none;
  margin: 16px 0 14px;
  display: grid;
  gap: 5px;
}
.sk-picks li {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 8px;
  margin: 0 -8px;
  border-radius: 7px;
  background: transparent;
  transition: background 0.3s ease;
}
.sk-picks li > i {
  flex: 0 0 6px;
  height: 6px;
  border-radius: 50%;
  background: #cfd5e1;
  transition: background 0.3s ease;
}
.sk-picks li > span {
  height: 7px;
  width: var(--w, 70%);
  border-radius: 3px;
  background: #e6e9f1;
  transition: background 0.3s ease;
}
.sk-picks li.is-on {
  background: var(--accent-soft);
}
.sk-picks li.is-on > i {
  background: var(--accent);
}
.sk-picks li.is-on > span {
  background: #b9caf6;
}

/* The board: three status columns, and the fourth column of the real board is
   off the right edge exactly as it is on a screen this wide. */
.sk-board {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  height: 100%;
}
.sk-col {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 0;
}
.sk-colhead {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 3px;
}
.sk-colhead > i {
  height: 7px;
  width: var(--w, 44%);
  border-radius: 3px;
  background: #cfd5e1;
}
.sk-card {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  padding: 8px 9px;
  display: grid;
  gap: 5px;
}
.sk-card .sk-line {
  height: 6px;
}
.sk-cardtop {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1px;
}
.sk-key {
  height: 7px;
  width: 30px;
  border-radius: 3px;
  background: #cdd8f4;
}
.sk-av {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #dfe4ee;
}
/* A card that was already on the board before the ask — one step quieter, and
   with a grey key instead of the accent one, so the four that arrive are
   visibly the new ones rather than four more of the same. */
.sk-card.is-idle {
  opacity: 0.65;
}
.sk-card.is-idle .sk-key {
  background: #dfe4ee;
}
/* A card the assistant files gets the shared arrival plus one thing of its own:
   it flashes its border once. That flash is what makes a card read as
   *landing* rather than as having been there all along. */
.sk-card[data-in].is-on {
  animation: cardLand 1.1s ease-out;
}
@keyframes cardLand {
  0%,
  35% {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  100% {
    border-color: var(--line);
    box-shadow: 0 0 0 0 rgba(46, 95, 232, 0);
  }
}

/* --- Four more primitives, each earning its keep in more than one scene ------
   The rule that keeps this from becoming five bespoke drawings: a new scene
   composes what is already here, and only adds a shape no other agenda can
   use. Everything below takes its size and colour from the markup (`--w`,
   `--h`, `--c`), so a variant is a style attribute, not a class in this file. */

/* A chart: columns of the timesheet week, and the same shape wherever else a
   quantity per period is the answer. The bar grows out of its baseline, which
   is how a number arriving reads as a number arriving. */
.sk-bars {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  height: 250px;
  margin: 2px 0 4px;
}
.sk-bars > div {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 8px;
  height: 100%;
}
/* Capped, and centred in its column. A bar as wide as the space it is given
   stops reading as a quantity and starts reading as a panel — five of those
   filled the window with blue and said nothing. */
.sk-bars i {
  display: block;
  width: min(100%, 54px);
  margin: 0 auto;
  height: var(--h, 40%);
  border-radius: 5px 5px 0 0;
  background: var(--c, #dfe4ee);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.45s cubic-bezier(0.22, 1.2, 0.4, 1);
}
.sk-bars i.is-on {
  transform: none;
}
/* The day under the column. */
.sk-bars b {
  display: block;
  height: 6px;
  border-radius: 3px;
  background: #e6e9f1;
  margin: 0 auto;
  width: 60%;
}

/* A list of rows — a version history, an activity stream, a directory. Every
   scene that answers with a LIST answers in these. */
.sk-rows {
  list-style: none;
  display: grid;
  gap: 8px;
}
.sk-rows > * {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
}
/* Takes the slack so the trailing pill stays right. On a row whose line has a
   width of its own (a timeline, where ragged lengths are the whole texture) put
   it on an empty span instead — `flex: 1` beats `width`, and every row set the
   same way came out identical. */
.sk-rows .sk-grow {
  flex: 1;
}
/* A status pill: grey until something makes it true. Two truths, and they are
   the two a reader needs — this one is live (accent), or this one was refused
   (the same orange as a refused tool chip, so the window and the chat agree). */
.sk-pill {
  flex: 0 0 auto;
  width: var(--w, 34px);
  height: 12px;
  border-radius: 999px;
  background: #e6e9f1;
  transition: background 0.35s ease;
}
.sk-pill.is-on {
  background: var(--accent);
}
.sk-pill.warn.is-on {
  background: #c2410c;
}
/* The line a list totals up on — a week's hours, a page's footer. */
.sk-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--line);
}

/* A page being built: the microsite preview, block by block. It is the one
   primitive with a frame of its own, because what it draws is a *page* and a
   page has an edge. */
.sk-page {
  height: 100%;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  display: grid;
  align-content: start;
  gap: 9px;
  padding: 14px 16px;
}
/* A header strip: a page's nav, a deal's title row. */
.sk-band {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.sk-page .sk-hero {
  display: grid;
  gap: 8px;
  justify-items: center;
  text-align: center;
  padding: 6px 0 2px;
}
.sk-page .sk-btn {
  height: 20px;
  width: 96px;
  border-radius: 999px;
  background: var(--accent);
  margin-top: 6px;
}
.sk-page .sk-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.sk-page .sk-cards > div {
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 8px;
  display: grid;
  gap: 6px;
}

/* A picture. It is the one shape whose whole job is to say "you are looking at
   a page a visitor sees", not at another panel of the app — a microsite preview
   made only of grey bars reads as a settings screen. Drawn like everything else
   here: a sky, a sun and two hills, so it cannot be mistaken for a real
   photograph either. */
.sk-img {
  position: relative;
  height: var(--h, 96px);
  border-radius: 8px;
  background: linear-gradient(#eef1f6, #e4e9f2);
  overflow: hidden;
}
.sk-img::before {
  content: "";
  position: absolute;
  top: 16%;
  left: 20%;
  width: 14%;
  aspect-ratio: 1;
  max-width: 18px;
  border-radius: 50%;
  background: #cfd7e5;
}
.sk-img::after {
  content: "";
  position: absolute;
  inset: 38% 0 0 0;
  background: #c5cfe1;
  clip-path: polygon(
    0% 100%,
    26% 30%,
    47% 100%,
    39% 100%,
    67% 8%,
    100% 100%
  );
}

/* A stage bar — a deal stepping through the pipeline. It is a row of the same
   cells the permission matrix uses: filled up to where the deal is, and the
   next one turns when it moves. */
.sk-steps {
  display: flex;
  gap: 7px;
}
.sk-steps > .sk-cell {
  flex: 1;
  max-width: none;
  width: auto;
  height: 14px;
}
.sk-cell.is-on {
  background: var(--accent);
}

/* The permission matrix: modules down the side, roles across the top, a cell
   for each pair. `.granted` is a fact the grid already showed; `.is-on` is the
   beat sheet reading a row out loud, and `.deny` is the one it cannot. */
.sk-matrix {
  display: grid;
  gap: 9px;
}
.sk-mrow {
  display: grid;
  grid-template-columns: 1.5fr repeat(5, 1fr);
  gap: 7px;
  align-items: center;
  padding: 3px 6px;
  margin: 0 -6px;
  border-radius: 7px;
  transition: background 0.3s ease;
}
.sk-mrow.is-on {
  background: var(--accent-soft);
}
/* The row the key's scope does not carry: read out in the refusal's colour, not
   the answer's, so the eye reaches the same verdict as the orange chip. */
.sk-mrow.warn.is-on {
  background: #fdf2ec;
}
/* Capped and centred for the same reason the chart's bars are: a cell as wide
   as its track reads as a bar, and a matrix of bars is not a matrix. */
.sk-cell {
  height: 22px;
  width: min(100%, 46px);
  margin: 0 auto;
  border-radius: 6px;
  background: #eef1f6;
  border: 1px solid transparent;
  transition:
    background 0.3s ease,
    border-color 0.3s ease;
}
.sk-cell.granted {
  background: #cdd8f4;
}
.sk-mrow.is-on .sk-cell.granted {
  background: var(--accent);
}
.sk-cell.deny.is-on {
  background: #fdf2ec;
  border-color: #f0cdb8;
}
/* The bar across a denied cell — a shape, because a padlock would be a glyph
   and this window has no glyphs. */
.sk-cell.deny.is-on::after {
  content: "";
  display: block;
  width: 13px;
  height: 3px;
  border-radius: 2px;
  background: #c2410c;
  margin: 9px auto 0;
}

/* A markdown document assembling itself. Dark, and the same dark as the `.md`
   transcript block on the agents page: the report is the one artefact that
   leaves Opelli as a file, and it should not look like another panel of UI. */
.sk-md {
  height: 100%;
  background: var(--ink);
  border-radius: 10px;
  padding: 16px 18px;
  display: grid;
  align-content: start;
  gap: 15px;
  overflow: hidden;
}
/* One section of the document — a heading and its lines, arriving together. */
.sk-md > div {
  display: grid;
  gap: 7px;
}
.sk-md .sk-line {
  height: 6px;
  background: #2f3d59;
}
.sk-md .sk-title {
  height: 9px;
  background: #4d6ea8;
}

/* The conversation side. It reuses `.msg` and `.tools` from the MCP transcripts
   on the agents page — same bubbles, same chips — and adds only the arrival. */
.scene-chat {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--paper-soft);
  border: 1px solid #e3e7ef;
  border-radius: var(--radius);
  padding: 14px;
}
.scene-chat-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
}
.scene-chat-head .sk-av {
  width: 18px;
  height: 18px;
  background: #cdd8f4;
}
.scene-thread {
  display: grid;
  align-content: start;
  gap: 10px;
  flex: 1;
}
.scene-thread .msg {
  font-size: 13.5px;
}
.scene-thread .msg p {
  line-height: 1.5;
}
.scene-thread .msg-you {
  padding: 10px 12px;
  border-radius: 10px;
}
.scene-thread .tools {
  gap: 5px;
  padding: 0;
}
.scene-thread .tools code {
  font-size: 11px;
  padding: 2px 9px 2px 8px;
  background: var(--paper);
}
.scene-thread .tools code b {
  font-weight: 600;
  color: var(--muted);
}
/* The composer never does anything — it is the floor of the panel, and without
   it the thread floats in a box with no bottom. */
.scene-composer {
  margin-top: 12px;
  height: 30px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  display: flex;
  align-items: center;
  padding: 0 12px;
}
.scene-composer i {
  width: 1.5px;
  height: 13px;
  background: var(--accent);
  animation: sceneCaret 1.1s steps(1) infinite;
}
@keyframes sceneCaret {
  50% {
    opacity: 0;
  }
}

/* Details grid */
.details {
  padding: 84px 0 96px;
}
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-top: 40px;
}
.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px;
  transition:
    transform 0.2s ease,
    border-color 0.2s ease;
}
.card:hover {
  transform: translateY(-3px);
  border-color: #cdd6e6;
}
.card p {
  color: var(--ink-soft);
  font-size: 14.5px;
}
.card code {
  font-size: 13px;
  background: var(--paper-soft);
  padding: 1px 6px;
  border-radius: 6px;
}

/* Vision */
.vision {
  padding: 40px 0 96px;
}

/* Beta */
.beta {
  padding: 20px 0 110px;
}
.beta-inner {
  background: var(--accent-soft);
  border-radius: 24px;
  padding: 64px 32px;
  text-align: center;
}
.beta-inner > p {
  color: var(--ink-soft);
  margin: 12px auto 28px;
  max-width: 480px;
}
.beta-row {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
.beta-row input {
  font: inherit;
  font-size: 15.5px;
  padding: 12px 20px;
  border: 1px solid #c6d3f5;
  border-radius: 999px;
  width: min(360px, 100%);
  background: var(--paper);
  color: var(--ink);
}
.beta-row input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
}
.beta-note {
  font-size: 13px;
  color: var(--muted);
  margin-top: 14px;
}
/* It carries only submit errors now, so it takes no space until it has one. */
.beta-note:empty {
  margin: 0;
}
.beta-note.error {
  color: #c2410c;
}
.beta-done {
  font-size: 18px;
  font-weight: 600;
  color: var(--accent);
}
#companyField {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

/* ---------------------------------------------------------------------------
   v2 structure: five agendas, each with its own page.

   The landing names the five and says what is inside them; the detail — the
   module sections, screenshots and feature lists — lives one click down under
   /features. The rule that keeps it honest: the landing may not grow a sixth
   thing to scroll past. A new module joins an agenda's page, exactly as it
   used to join a chapter.
--------------------------------------------------------------------------- */
.agendas {
  padding: 96px 0 40px;
}
.agendas .band-head {
  text-align: center;
}
/* Six tracks, each card spanning two: three per row, and the last row's two
   cards span three each so a five-card set ends centred instead of orphaned. */
.agenda-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 18px;
  margin-top: 44px;
}
/* The cards sit on --paper-soft rather than on the page's own white: on a white
   band a white card is only its hairline, and five of them read as one ruled
   block instead of five things you can pick. The hairline goes a step darker at
   the same time, because a border tuned against white disappears against a fill
   two per cent away from it.

   Hovering lifts the card to white — the same "picked up off the page" move the
   shadow is already making, and it is what keeps the fill from reading as
   disabled. */
.agenda {
  position: relative;
  overflow: hidden; /* crops the mark below at the card's own radius */
  grid-column: span 2;
  display: flex;
  flex-direction: column;
  background: var(--paper-soft);
  border: 1px solid #e3e7ef;
  border-radius: var(--radius);
  padding: 26px 24px 22px;
  text-decoration: none;
  color: inherit;
  transition:
    transform 0.2s ease,
    border-color 0.2s ease,
    background 0.2s ease,
    box-shadow 0.2s ease;
}
/* The mark, washed out and hung off the top-right corner so only its lower-left
   quadrant is on the card — the same trick as a logo that runs past the edge of
   a page. It is drawn through a MASK rather than as an <img>: logo.svg carries
   its own #363636 stroke, and a mask takes the shape and lets the colour be
   ours, so it can be tinted with the accent on hover and can never fight the
   text. Purely decorative, so it is inert to the pointer and unreachable by a
   screen reader (no markup at all). */
.agenda::before {
  content: "";
  position: absolute;
  /* Hung far enough past the corner that what is left on the card stops above
     the description: the mark is the card's texture, never something the eye
     has to read the copy through. */
  top: -108px;
  right: -86px;
  width: 210px;
  height: 212px; /* the mark is 177 × 178 — keep the ratio */
  background: #ccd3e0;
  -webkit-mask: url("img/logo.svg") no-repeat center / contain;
  mask: url("img/logo.svg") no-repeat center / contain;
  opacity: 0.45;
  pointer-events: none;
  transition:
    opacity 0.25s ease,
    background 0.25s ease;
}
/* Everything the card actually says stands in front of it. */
.agenda > * {
  position: relative;
}
.agenda:nth-child(4),
.agenda:nth-child(5) {
  grid-column: span 3;
}
.agenda:hover {
  transform: translateY(-3px);
  background: var(--paper);
  border-color: #cdd6e6;
  box-shadow: 0 24px 50px -34px rgba(16, 22, 35, 0.45);
}
.agenda:hover::before {
  background: var(--accent);
  opacity: 0.14;
}
.agenda-num {
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: 0.14em;
  color: var(--accent);
}
.agenda h3 {
  font-size: 21px;
  font-weight: 650;
  letter-spacing: -0.01em;
  line-height: 1.25;
  margin-top: 10px;
}
.agenda p {
  color: var(--ink-soft);
  font-size: 14.5px;
  margin-top: 8px;
}
/* The modules inside an agenda, as chips. They were a line of uppercase
   letter-spaced text, which read as a label for the card; as chips they read
   as the things you get, which is what they are. */
.agenda-mods {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 16px;
}
/* The chip and the card swap fills: white chips on the soft card, and on hover
   — when the card itself goes white — the chips take the soft fill instead, so
   there is always exactly one step of contrast between them. */
.agenda-mods li {
  border: 1px solid #e3e7ef;
  background: var(--paper);
  border-radius: 999px;
  padding: 3px 11px;
  font-size: 12.5px;
  color: var(--ink-soft);
  transition:
    border-color 0.2s ease,
    background 0.2s ease;
}
.agenda:hover .agenda-mods li {
  border-color: #dfe5ef;
  background: var(--paper-soft);
}
/* `margin-top: auto` on the last row pins the call to the card's bottom edge,
   so a short card and a long one still line their arrows up. */
.agenda-go {
  margin-top: auto;
  padding-top: 18px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
}
.agenda:hover .agenda-go {
  text-decoration: underline;
}

/* Sub-page header — the counterpart of the landing's hero on every page that
   is not the landing. No screenshot: the page's own first section carries one. */
.page-head {
  padding: 64px 0 8px;
}
.page-head h1 {
  font-size: clamp(34px, 4.6vw, 52px);
}

/* The agenda pages put that header on a panel: the site's own soft grey with a
   hairline of the mark's #D9D9D9 around it. Breadcrumbs stay OUTSIDE it — they
   are chrome for the page, not part of the header.

   The lighter fill is what lets the brand accent back into the kicker: on
   --paper-soft it measures 5.05:1, where the earlier solid #D9D9D9 put it at
   3.80:1 and forced a darkened blue. --ink is 17:1 here and --ink-soft 7.1:1. */
.page-head-panel {
  background: var(--paper-soft);
  border: 1px solid #d9d9d9;
  border-radius: 26px;
  padding: 44px 44px 40px;
}
.page-head-panel .section-sub {
  color: var(--ink-soft);
  max-width: 720px;
}
/* White chips on the grey, so the row reads as controls sitting on the panel. */
.page-head-panel .toc a {
  background: var(--paper);
  border-color: transparent;
}
.page-head-panel .toc a:hover {
  border-color: var(--ink);
}

/* ---------------------------------------------------------------------------
   The agenda rail — five segments under the top bar, one per agenda, filled up
   to the one you are on. It is the quick jump between agendas; the prev/next
   at the foot of each page is the same sequence read one step at a time.
   Sticky, because its whole job is being reachable without scrolling back.
--------------------------------------------------------------------------- */
.agenda-rail {
  position: sticky;
  top: 64px;
  z-index: 40;
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
/* The row scrolls in its own container rather than wrapping: five segments on
   one line is the whole idea, and the page must never scroll sideways. */
.agenda-rail-inner {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-top: 10px;
  padding-bottom: 10px;
}
.agenda-rail-inner::-webkit-scrollbar {
  display: none;
}
.rail-seg {
  flex: 1 0 auto;
  min-width: 96px;
  text-decoration: none;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 550;
  white-space: nowrap;
  display: grid;
  gap: 6px;
}
.rail-seg::before {
  content: "";
  display: block;
  height: 3px;
  border-radius: 2px;
  background: var(--line);
  transition: background 0.2s ease;
}
/* Filled up to here: everything at or before the current agenda. site.js does
   not touch this — the pages carry the class, so it is right before any script
   runs and right with none at all. */
.rail-seg.done::before {
  background: color-mix(in srgb, var(--accent) 45%, var(--line));
}
.rail-seg[aria-current="page"]::before {
  background: var(--accent);
}
.rail-seg[aria-current="page"] {
  color: var(--ink);
}
.rail-seg:hover {
  color: var(--ink);
}
.rail-seg:hover::before {
  background: color-mix(in srgb, var(--accent) 70%, var(--line));
}
.rail-num {
  color: var(--muted);
  font-weight: 650;
  letter-spacing: 0.08em;
  margin-right: 6px;
}
.rail-seg[aria-current="page"] .rail-num {
  color: var(--accent);
}
.page-head .section-sub {
  font-size: 18px;
  margin-top: 18px;
}
.crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 18px;
}
.crumbs a {
  color: var(--muted);
  text-decoration: none;
}
.crumbs a:hover {
  color: var(--ink);
}

/* What's on this page — the drill-down's table of contents, so a long agenda
   page announces its own length instead of making the reader scroll to find
   out what it holds. */
.toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 26px;
}
.toc a {
  border: 1px solid var(--line);
  background: var(--paper-soft);
  border-radius: 999px;
  padding: 5px 14px;
  font-size: 13.5px;
  color: var(--ink-soft);
  text-decoration: none;
}
.toc a:hover {
  border-color: var(--ink);
  color: var(--ink);
}

/* Between agenda pages: the next one in the reading order, so the five read as
   a sequence rather than five leaves off a menu. */
.pagenav {
  border-top: 1px solid var(--line);
  padding: 28px 0 96px;
}
.pagenav-inner {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.pagenav a {
  text-decoration: none;
  color: var(--ink-soft);
  font-size: 14.5px;
}
.pagenav a:hover {
  color: var(--ink);
}
.pagenav b {
  display: block;
  font-size: 11px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 650;
  margin-bottom: 2px;
}

/* ---------------------------------------------------------------------------
   Pricing. One published price in euro — the first three seats free, every
   seat after that at a rate the visitor switches between annual and monthly.
   Two rules make that safe: the HTML ships the annual figures, so the page is
   right with no JS at all, and every switchable figure is a `data-price`
   element that site.js rewrites in place.

   The page is four tiles, one table and two comparisons — deliberately. The
   version before it explained the price in five sections, and a price nobody
   can see at a glance is not a published price.
--------------------------------------------------------------------------- */

/* The billing-period switch. Buttons rather than a select, because both
   states have to be readable without opening anything.
   `.controls` beside them is a MARKER and carries no layout — it tells the
   markdown build this is a thing you operate rather than a thing you read
   (tools/build-md.mjs). Putting a `display` on it once quietly turned the
   calculator into a wrapping flex row. */
.switches {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-bottom: 26px;
}
.seg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.seg button {
  border: 0;
  background: none;
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  color: var(--ink-soft);
  padding: 5px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition:
    background 0.15s ease,
    color 0.15s ease;
}
.seg button:hover {
  color: var(--ink);
}
.seg button[aria-pressed="true"] {
  background: var(--accent);
  color: #fff;
}

/* Three worked sizes and the calculator, as four of the same tile. The
   calculator being a sibling rather than a widget under the prices is the
   whole idea: it is the fourth answer, in the same shape as the three fixed
   ones. */
/* The four tiles line up ROW BY ROW — head, price, per-person, usage — through
   a subgrid, so the hairline above the usage list is at the same height in all
   four however the lines inside them wrap. Bottom-aligning the lower half with
   `margin-top: auto` looked right until a tile wrapped one line fewer than its
   neighbours (the free one, whose "200 campaign recipients" fits on one line),
   and then its rule sat lower than the rest. Below 860px the tiles stack into
   two columns and go back to plain flex — see the media query. */
.tiles {
  display: grid;
  /* minmax(0, 1fr), not bare 1fr: 1fr's implied minimum is the column's
     min-content, and an unbreakable price ("18 330 Kč", NBSP-joined) is wide
     enough to win it extra width — so the tiles resized as the calculator's
     figures changed. Zero minimum keeps the four columns rigid whatever the
     slider says; the content wraps inside instead. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: repeat(5, auto);
  column-gap: 18px;
  row-gap: 0;
}
.tile {
  position: relative;
  background: var(--paper);
  border: 1px solid #e3e7ef;
  border-radius: 18px;
  padding: 26px 24px 28px;
  display: grid;
  grid-row: span 5;
  grid-template-rows: subgrid;
  transition:
    transform 0.2s ease,
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}
.tile:hover {
  transform: translateY(-3px);
  border-color: #cdd6e6;
  box-shadow: 0 26px 55px -38px rgba(16, 22, 35, 0.5);
}
/* The size of the team, as a chip. It used to be a large accent number, which
   put the loudest colour on the page above the figure it was supposed to
   introduce — the price is the answer, the team size is the question. */
.tile-seats {
  justify-self: start;
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-size: 13px;
  font-weight: 550;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 999px;
  padding: 6px 14px;
}
.tile-seats b {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.tile-total {
  display: flex;
  /* wrap, so the term suffix ("/ month, billed annually") drops to its own
     line when the price fills the tile, instead of breaking mid-phrase
     beside it. nowrap on the small keeps the phrase in one piece. */
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 7px;
  align-self: end;
  padding-top: 20px;
}
.tile-total b {
  font-size: clamp(34px, 3.8vw, 46px);
  font-weight: 650;
  letter-spacing: -0.035em;
  line-height: 1;
}
/* A koruna total is two to three times the euro's width at the same size —
   "8 580 Kč" against "€312" — so the Czech pages take the price down a step
   or the figure crowds the tile it is supposed to headline. :lang, not a
   class: the page already declares what it is. */
.tile-total b:lang(cs) {
  font-size: clamp(27px, 2.6vw, 34px);
}
.tile-total small {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}
.tile-each {
  font-size: 13.5px;
  color: var(--ink-soft);
  margin-top: 9px;
  align-self: start;
}
/* What that many people are allowed. Every line of it scales with the PAID
   seats, which is the reason it is on the tile rather than only in the table:
   "1 per paid seat" is a formula, "9 published sites" is an answer.

   Each line carries a tick. Ten rows of plain text read as a spec sheet — the
   marker is what turns the same ten rows into a list of things you get, and it
   costs no space because it sits in the indent the text needed anyway. */
.tile-usage {
  list-style: none;
  display: grid;
  gap: 9px;
  align-content: start;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.45;
  color: var(--muted);
}
.tile-usage li {
  position: relative;
  padding-left: 22px;
}
/* THE TICK, defined once and worn in two places — the price tiles and the
   "Yes" cells of the tables. A soft disc and two borders rotated into a check.
   Both earlier attempts tried to draw it out of background gradients and both
   came out wrong (a square in the tiles, a dash in the table): a check is two
   strokes at an angle, so two borders and a rotation is the honest way to say
   it. `--tick-x` / `--tick-y` place it; everything else is fixed. */
.tick::before,
.tile-usage li::before {
  content: "";
  position: absolute;
  left: var(--tick-x, 0px);
  top: var(--tick-y, 4px);
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--accent-soft);
}
/* The check is placed at the CIRCLE'S CENTRE and then pulled back by half its
   own size, rather than by two hand-tuned offsets — which is why it was never
   quite centred: the offsets were measured against the unrotated box, and
   rotating a box whose ink sits on two edges moves what you actually see. The
   -54% is optical, not arithmetic: a tick reads centred when it sits a hair
   high, because the long stroke carries the eye down. */
.tick::after,
.tile-usage li::after {
  content: "";
  position: absolute;
  left: calc(var(--tick-x, 0px) + 7.5px);
  top: calc(var(--tick-y, 4px) + 7.5px);
  width: 5px;
  height: 2.5px;
  border-left: 1.7px solid var(--accent);
  border-bottom: 1.7px solid var(--accent);
  transform: translate(-50%, -54%) rotate(-45deg);
}
.tile-usage b {
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* The call, on its own subgrid row, so four buttons sit on one line however
   many usage lines wrap above them — which is the whole reason the tile is a
   subgrid rather than a flex column. Ghost on all four: a filled button on one
   of them would mark it as the recommended plan, and there are no plans.
   The three with a figure ask for the demo the rest of the site asks for; the
   quoted one asks for the conversation it actually needs. */
.tile-cta {
  align-self: end;
  margin-top: 22px;
  width: 100%;
  justify-content: center;
  font-size: 14.5px;
  padding: 11px 18px;
}
/* The border tune-up belongs to the GHOST call only — the quoted tile's
   "Get in touch". The three order buttons went btn-primary (2026-08-06,
   owner's call): the fill marks the ACTION, not a recommended plan — all
   three sell the same product — and an accent fill with this light border
   would wear a gray fringe. */
.tile-calc .tile-cta.btn-ghost,
.tile-cta.btn-ghost:hover {
  border-color: #cdd6e6;
}

/* The uncapped half. It is identical in every tile, so it is the FLOOR rather
   than the thing you are comparing — a hairline and a quieter marker put it
   after the figures that actually differ, without hiding it. */
.tile-inc {
  color: var(--ink-soft);
}
.tile-inc::before {
  background: var(--paper-soft);
}
.tile-inc::after {
  border-color: #b9c4d8;
}
/* The FIRST of the included lines carries the rule. `:first-of-type` was the
   obvious way to say that and is wrong: every item here is an `li`, so it
   matched the top of the whole list instead. "The `.tile-inc` that follows
   something which is not one" is the thing actually meant. */
.tile-usage li:not(.tile-inc) + .tile-inc {
  margin-top: 8px;
  padding-top: 17px;
  border-top: 1px solid var(--line);
}
.tile-usage li:not(.tile-inc) + .tile-inc {
  --tick-y: 21px;
}
/* The quoted tile has no figures to lead its lines with — it lists what the
   quote covers instead, so the rows read as sentences rather than as values. */
.tile-usage-plain li {
  color: var(--ink-soft);
}
/* The calculator tile is the one you can touch, and it used to say so with a
   full accent border — which reads as a validation state, not an invitation:
   the same ring a field gets when it is focused, or wrong. It is a WASH now,
   accent at the top where the chip and the slider are, gone by the time the
   price starts, over a border only a shade bluer than its neighbours'. The
   slider is the affordance; the tile only has to look warmer than the ones
   either side of it. */
.tile-calc {
  border-color: #dbe4fb;
  background: linear-gradient(
    180deg,
    var(--accent-soft) 0%,
    rgba(233, 238, 253, 0.35) 26%,
    var(--paper) 46%
  );
  box-shadow: 0 26px 60px -38px rgba(46, 95, 232, 0.45);
}
.tile-calc:hover {
  border-color: #c6d3f7;
  box-shadow: 0 30px 65px -36px rgba(46, 95, 232, 0.55);
}
/* The chip is accent-soft on white everywhere else; on the wash that is the
   same colour twice and it disappears. White here, which also makes it read as
   the thing the slider under it is setting. */
.tile-calc .tile-seats {
  background: var(--paper);
  box-shadow: 0 1px 3px rgba(46, 95, 232, 0.12);
}
.tile-calc label {
  display: block;
  cursor: pointer;
}
/* THE SLIDER, shared by the pricing calculator and the deck's account stack.
   `accent-color` on a native range is one line and looks like a native range;
   this is the same control drawn in the site's own hand — a 6px track, a white
   thumb ringed in accent, and the filled part of the track painted in.

   WebKit has no `::-moz-range-progress`, so the fill is a gradient stop driven
   by `--range-pct`, which the script that owns each slider sets on input. With
   no JS the stop sits where the markup's `value` puts it and never moves, which
   is correct: with no JS the slider does not move either. */
.range {
  width: 100%;
  height: 22px;
  margin-top: 12px;
  cursor: pointer;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
}
.range:focus {
  outline: none;
}
.range::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 999px;
  background: linear-gradient(
    to right,
    var(--accent) 0 var(--range-pct, 50%),
    var(--line) var(--range-pct, 50%) 100%
  );
}
.range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  margin-top: -7px;
  border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--accent);
  box-shadow: 0 2px 8px -1px rgba(46, 95, 232, 0.45);
  transition:
    transform 0.15s ease,
    box-shadow 0.15s ease;
}
.range:hover::-webkit-slider-thumb {
  transform: scale(1.08);
}
.range:active::-webkit-slider-thumb {
  transform: scale(1.16);
}
.range:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 5px var(--accent-soft);
}
/* Firefox paints the filled half itself, so it needs no percentage. */
.range::-moz-range-track {
  height: 6px;
  border-radius: 999px;
  background: var(--line);
}
.range::-moz-range-progress {
  height: 6px;
  border-radius: 999px;
  background: var(--accent);
}
.range::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--accent);
  box-shadow: 0 2px 8px -1px rgba(46, 95, 232, 0.45);
}
.range:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 5px var(--accent-soft);
}
.price-fine {
  margin-top: 22px;
  text-align: center;
  font-size: 13.5px;
  color: var(--muted);
}

/* The two tables. Its own scroll container, so three columns never widen the
   document on a phone — and `overflow: hidden` on the wrap is what lets the
   header row and the last row sit inside the rounded corner instead of square
   against it. */
.table-wrap {
  overflow: hidden auto;
  overflow-x: auto;
  border: 1px solid #e3e7ef;
  border-radius: 18px;
  background: var(--paper);
  margin-top: 44px;
}
.ptable {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
}
.ptable th,
.ptable td {
  padding: 14px 22px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  color: var(--ink-soft);
  vertical-align: top;
}
/* The first column is the question every row answers; it gets the room to say
   it on one line, and the value columns split what is left. */
.ptable th:first-child {
  width: 32%;
}
/* A hairline between columns. Long rows in a wide table lose the eye somewhere
   in the middle, and a vertical rule costs nothing to read past — it is the one
   piece of structure a table of prose actually needs. */
.ptable td + td,
.ptable th + th,
.ptable th + td {
  border-left: 1px solid var(--line);
}
/* THREE LEVELS, and they have to be told apart at a glance:
     the column heads   — darkest fill, ink type
     a section heading  — the soft grey, ink-soft type
     a row              — white
   Both were on `--paper-soft` with grey type, which in the metered table put
   the column heads and the three section headings at the same weight; you
   could not tell which row was naming the columns and which was naming a
   group. Same treatment on both tables, so the guest table's head and this
   one's are the same object. */
.ptable thead th {
  background: #e8ecf4;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink);
  font-weight: 650;
  padding-top: 14px;
  padding-bottom: 14px;
  border-bottom-color: #dbe1ec;
}
.ptable tbody th {
  color: var(--ink);
  font-weight: 600;
}
/* The row under the cursor, so the eye keeps its place across the width. */
.ptable tbody tr:not(.ptable-sec):hover th,
.ptable tbody tr:not(.ptable-sec):hover td {
  background: #fafbfd;
}
/* Only the very last row of the very last group loses its rule — with three
   tbodies, `tbody tr:last-child` is three rows, and two of them were opening a
   gap in the middle of the table. */
.ptable tbody:last-of-type tr:last-child th,
.ptable tbody:last-of-type tr:last-child td {
  border-bottom: 0;
}
/* A group heading: what KIND of fact the rows under it are. Light fill, dark
   grey type, and it spans the table — it is a label for the group, not a value
   in any column, so it must never look like a row you could read across. */
.ptable-sec th {
  background: var(--paper-soft);
  color: var(--ink-soft);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding-top: 13px;
  padding-bottom: 13px;
  border-top: 1px solid #e3e7ef;
  border-bottom-color: #e3e7ef;
}
/* A section heading spans the table, so the column rules must not run through
   it — they would draw it as three cells and undo the spanning. */
.ptable-sec th + th {
  border-left: 0;
}
/* …except the first, which sits directly under the column heads. */
.ptable tbody:first-of-type .ptable-sec th {
  border-top: 0;
}
.ptable-3 td:last-child {
  color: var(--muted);
}
/* An answer worth catching from across the table. */
.ptable .hl {
  color: var(--accent);
  font-weight: 600;
}
/* "Yes" earns the same tick the price tiles wear, so a reader who has just
   scrolled past them already knows what it means. */
.ptable .yes {
  position: relative;
  padding-left: 45px;
  --tick-x: 22px;
  --tick-y: 16px;
  color: var(--ink);
  font-weight: 600;
  white-space: nowrap;
}
/* And "nothing here" recedes to a mark rather than a word-sized dash sitting
   at the same weight as the answers around it. */
.ptable .na {
  color: #c9d0dd;
}

/* `hidden` means hidden, whatever a component's own `display` says — the
   demo form and its thank-you swap on that attribute. */
[hidden] {
  display: none !important;
}

/* Pricing questions — a plain two-column definition list, no accordion: five
   short answers are quicker to read than to click open. */
.faq {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px 40px;
  margin-top: 48px;
}
.faq h3 {
  font-size: 16px;
  font-weight: 650;
}
.faq p {
  color: var(--ink-soft);
  font-size: 14.5px;
  margin-top: 6px;
}

/* Company page — the facts block beside the story. */
.factsheet {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
  background: var(--paper-soft);
}
.factsheet h3 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-weight: 650;
}
.factsheet dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px 18px;
  margin-top: 16px;
  font-size: 14.5px;
}
.factsheet dt {
  color: var(--muted);
}
.factsheet dd {
  color: var(--ink);
}
.factsheet a {
  color: var(--accent);
  text-decoration: none;
}
.factsheet a:hover {
  text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Footer. Four link columns over a legal line, and the legal line is the point:
   a company selling a business platform states who it is, where it is
   registered and under what number, on every page.

   NB the whole footer is duplicated in every page (this site has no build
   step — HTML is the artifact). Change it in one page and change it in all of
   them; README names the files.
--------------------------------------------------------------------------- */
.footer {
  border-top: 1px solid var(--line);
  padding: 56px 0 32px;
  background: var(--paper-soft);
}
.footer-top {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 40px;
}
.footer-brand .brand {
  font-size: 17px;
}
.footer-tag {
  color: var(--ink-soft);
  font-size: 14px;
  margin-top: 12px;
  max-width: 260px;
}
.footer-col h3 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
  font-weight: 650;
  margin-bottom: 14px;
}
.footer-col ul {
  list-style: none;
  display: grid;
  gap: 9px;
}
.footer-link {
  color: var(--ink-soft);
  font-size: 14px;
  text-decoration: none;
}
.footer-link:hover {
  color: var(--ink);
}
.footer-legal {
  border-top: 1px solid var(--line);
  margin-top: 44px;
  padding-top: 24px;
  display: flex;
  gap: 10px 28px;
  flex-wrap: wrap;
  align-items: baseline;
  font-size: 13px;
  color: var(--muted);
}
.footer-legal b {
  color: var(--ink-soft);
  font-weight: 600;
}
.footer-legal .footer-copy {
  margin-left: auto;
}

/* ---------------------------------------------------------------------------
   Screenshot lightbox. The shots ship at 2000px and are drawn at roughly half
   that, so there is real detail in them a reader cannot otherwise reach —
   clicking one opens it at full size.

   `[hidden]` needs saying out loud: the attribute's `display: none` comes from
   the UA stylesheet and LOSES to the explicit `display: grid` below, so
   without this the lightbox would sit open over every page. (The app's admin
   console learned the same lesson.)
--------------------------------------------------------------------------- */
.shot img {
  cursor: zoom-in;
}
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: clamp(12px, 3vw, 40px);
  background: rgba(16, 22, 35, 0.82);
  backdrop-filter: blur(6px);
  cursor: zoom-out;
  animation: lb-in 0.16s ease-out;
}
.lightbox[hidden] {
  display: none;
}
@keyframes lb-in {
  from {
    opacity: 0;
  }
}
.lightbox img {
  max-width: 100%;
  max-height: 92vh;
  width: auto;
  border-radius: 10px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.6);
  cursor: default;
}
.lightbox-close {
  position: absolute;
  top: clamp(10px, 2vw, 22px);
  right: clamp(10px, 2vw, 22px);
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s ease;
}
.lightbox-close:hover {
  background: rgba(255, 255, 255, 0.26);
}
.lightbox-close svg {
  width: 18px;
  height: 18px;
}
/* The page must not scroll behind an open lightbox. */
body.lightbox-open {
  overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .lightbox {
    animation: none;
  }
}

/* Scroll reveal */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.6s ease,
    transform 0.6s ease;
}
.reveal.in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .shot,
  .card,
  .btn {
    transition: none;
  }
  /* A scene keeps its last frame — site.js applies the whole beat sheet at once
     and never starts a clock — so everything in one that would move is off.
     Written as one rule over the scene's own subtree rather than a list of
     selectors: the list went stale the first time an attribute was renamed,
     and `cardLand` was still firing here for a while because of it. */
  .scene *,
  .scene *::before,
  .scene *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* Mobile / narrow */
@media (max-width: 860px) {
  /* The bar becomes mark + burger + CTA; the links drop down as a panel. The
     Features submenu renders flat inside it — a dropdown inside a dropdown is
     not a thing worth building for six links. */
  .nav-burger {
    display: block;
  }
  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 20px 40px -28px rgba(16, 22, 35, 0.5);
    padding: 8px 16px 16px;
    margin-left: 0;
  }
  .nav.nav-open .nav-links {
    display: flex;
  }
  .nav-links > a,
  .nav-trigger {
    padding: 11px 8px;
    font-size: 15.5px;
  }
  .nav-item {
    position: static;
  }
  .nav-trigger {
    display: none;
  }
  .nav-panel {
    position: static;
    transform: none;
    width: auto;
    opacity: 1;
    visibility: visible;
    box-shadow: none;
    border: 0;
    border-radius: 0;
    padding: 0;
    transition: none;
  }
  .nav-item.open .nav-panel {
    transform: none;
  }
  .nav-panel a {
    padding: 9px 8px;
  }
  .nav-panel span {
    display: none;
  }
  .nav-panel .nav-panel-all {
    margin-top: 4px;
    padding-top: 10px;
  }

  .hero {
    padding-top: 64px;
  }
  /* The stage keeps its aspect on a phone by growing taller as it narrows;
     site.js switches to the stacked anchor set below 620px and clamps every
     chip inside the box, so nothing here can push the page sideways. */
  .cst {
    height: clamp(360px, 96vw, 430px);
    margin-top: 14px;
  }
  .cst-word span {
    font-size: 13px;
    padding: 4px 12px;
  }
  .agendas {
    padding: 64px 0 24px;
  }
  .agenda-grid {
    grid-template-columns: 1fr;
  }
  /* One card per row is nearly the container's full width, so the mark can hang
     further off the corner without ever getting near the text. */
  .agenda::before {
    top: -120px;
    right: -96px;
  }
  .agenda,
  .agenda:nth-child(4),
  .agenda:nth-child(5) {
    grid-column: span 1;
  }
  /* EVERY SCENE STANDS DOWN THE MOMENT THE NAV BECOMES A BURGER. Stacked, a
     scene is a window and a transcript one under the other, and a visitor who
     is scrolling a phone reads past a set-piece rather than watching it — so it
     is two screens of motion in the way of the sentence it was meant to
     illustrate. site.js stands the machinery down at the SAME threshold: no
     controllers, no clock, no observers. Keep the two in step.

     What survives on the landing is what the constellation left at its own
     narrow breakpoint: the mark, still, with the wordmark under it. */
  .scene {
    display: none;
  }
  .hero-rotator {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    margin: 8px auto 26px;
  }
  .scene-mark {
    position: static;
    transform: none;
    width: clamp(132px, 42vw, 180px);
  }
  .hero-rotator .cst-wordmark {
    display: block;
  }
  /* The two gaps the headline and the paragraph keep around the picture are
     measured against a picture; around a wordmark they are a hole. */
  .hero h1 {
    margin-bottom: 0.7em;
  }
  /* Adjacent margins collapse, so this is the whole gap under the wordmark —
     not the rotator's 26 px plus it. */
  .hero-sub {
    margin-top: 3.4em;
  }
  .page-head {
    padding-top: 32px;
  }
  .page-head-panel {
    padding: 30px 22px 26px;
    border-radius: 20px;
  }
  /* The rail drops to its NUMBERS. Five captions do not fit a phone, and the
     version that scrolled sideways to hold them hid the segments you had not
     reached — which is the one thing a progress bar exists to show. Numbers
     always fit, so the five go back to equal shares of the row and it reads as
     a progress bar again rather than a scrolling list. The page's own <h1> two
     lines below says which agenda this is; the rail only has to say where in
     the five you are.

     The caption stays in the accessibility tree — clipped, not removed. A link
     whose whole name is "03" is no use to a screen reader. */
  .agenda-rail {
    top: 64px;
  }
  .rail-seg {
    min-width: 0;
    flex: 1 1 0;
    font-size: 12px;
    position: relative;
  }
  .rail-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .rail-num {
    margin-right: 0;
  }
  .agenda-rail-inner {
    gap: 10px;
    overflow-x: hidden;
  }
  .faq {
    grid-template-columns: 1fr;
  }
  /* Pricing: four tiles become two rows of two, then one column on a phone —
     the calculator stays the last of them either way. Two per row is few
     enough that the subgrid stops earning its keep, and a plain flex column
     brings back a row gap the subgrid has to spend on its own rows. */
  .tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: none;
    row-gap: 16px;
  }
  .tile {
    display: flex;
    flex-direction: column;
    grid-row: auto;
    grid-template-rows: none;
  }
  .tile-total {
    margin-top: auto;
  }
  .pagenav {
    padding-bottom: 64px;
  }
  .footer-top {
    grid-template-columns: 1fr 1fr;
    gap: 28px;
  }
  .footer-brand {
    grid-column: 1 / -1;
  }
  .footer-legal .footer-copy {
    margin-left: 0;
  }
  .mod {
    padding: 56px 0;
  }
  .chapter {
    padding-top: 52px;
  }
  .chapter + .mod {
    padding-top: 32px;
  }
  .split {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .split.flip > .split-text {
    order: 0;
  }
  .mini-grid,
  .grid {
    grid-template-columns: 1fr;
  }
  .spec {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .hero-cta {
    flex-wrap: wrap;
  }
  /* Wiki stage stacks; callouts become plain chips in the flow */
  .wiki-stage {
    padding-bottom: 0;
  }
  .wiki-shot-main {
    max-width: none;
  }
  .wiki-shot-editor {
    position: static;
    width: 100%;
    margin-top: 20px;
    transform: none;
  }
  .callout {
    position: static;
    display: block;
    max-width: none;
    margin-top: 12px;
  }
  .callout::before {
    display: none;
  }
  .duo {
    padding-bottom: 0;
  }
  .duo-back {
    max-width: none;
  }
  .duo-front {
    position: static;
    width: 100%;
    margin-top: 20px;
    transform: none;
  }
  .band-duo {
    padding-bottom: 0;
  }
  .band-duo .shot-wide {
    max-width: none;
  }
  .band-duo-front {
    position: static;
    width: 100%;
    margin-top: 20px;
    transform: none;
  }
  .band-duo-front-tall {
    max-width: 340px;
    margin-left: auto;
    margin-right: auto;
  }
  /* Agents: one column of chats, the report block stays scrollable in place */
  .agents {
    padding: 56px 0;
  }
  .chat-grid,
  .agent-notes {
    grid-template-columns: 1fr;
  }
  .chat {
    padding: 16px;
  }
  .md {
    font-size: 11.5px;
    padding: 14px;
  }
}

/* ---------------------------------------------------------------------------
   Narrow: no animation at all.

   Below 620px the constellation stands down — the mark goes static and the
   wordmark takes the place of the orbiting words. Six chips will not fit
   around a mark that already spans most of the screen, and the version that
   stacked them down the middle read as a list of features rather than as one
   object. site.js uses the SAME 620px threshold and never starts its clock
   below it, so this is not just visual: on a phone there is no rAF loop, no
   interval and no Web Animations running at all.

   Last in the file on purpose: it has to win the same-specificity ties against
   the 860px block above.
--------------------------------------------------------------------------- */
@media (max-width: 620px) {
  .tiles {
    grid-template-columns: 1fr;
  }

  .cst {
    height: auto;
    /* The animated stage carried its own air inside a fixed height; standing
       in flow, the wordmark needs that space put back or it sits on the kicker. */
    margin: 4px auto 30px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
  }
  /* Out of the absolute positioning the animated stage needs, into plain flow. */
  .cst-core {
    position: static;
    transform: none;
    width: clamp(132px, 42vw, 180px);
  }
  .cst-word {
    display: none;
  }
  .cst-wordmark {
    display: block;
  }
}

/* A second band-head inside a module section — the "Ask for it" scene that
   follows a section's own copy (datasets on the work page, signing on trust):
   the room a fresh section would have had above it. */
.scene-head {
  margin-top: 56px;
}
