/* /services — the page that has to earn the enquiry.

   Shape of the page: the hero and the services are one scroll story. It
   starts on the spreadsheet every small business already keeps and climbs,
   one service per step, to AI; a single stage beside the steps grows into a
   workspace as they pass. After it: the reassurance that every step stands
   alone, the web offer, how a project runs, prices, questions, the CTA. */

/* ---------- The journey ------------------------------------
   Two layouts share one piece of markup.

   Static, the default, is what renders with JavaScript off and under
   reduced motion: the hero, the stage showing the finished workspace, and
   the steps as a list. Nothing in it waits for a class to become visible.

   Live (.story-live, set by story.js) pins the stage beside the steps. The
   stage then carries reached-1 … reached-4 for every scene up to the
   current one, and each scene's rules below describe how the stage looks
   *before* that scene is reached. The default rules are the destination. */

.journey {
  --journey-top: 64px;
  position: relative;
  padding-block: calc(var(--header-height) + var(--journey-top)) 32px;
  /* Pieces of the stage leave and enter from the side (the note being
     flicked away). clip, unlike hidden, is not a scroll container, so the
     pinned stage inside still sticks. */
  overflow-x: clip;
}
/* A faint cell grid behind the hero: the page itself starts as a sheet. */
.journey::before {
  content: '';
  position: absolute; inset: 0 0 auto 0; height: min(100svh, 920px);
  background-image:
    linear-gradient(to right, var(--clr-text-a08) 1px, transparent 1px),
    linear-gradient(to bottom, var(--clr-text-a08) 1px, transparent 1px);
  background-size: 112px 34px;
  background-position: -1px -1px;
  mask-image: radial-gradient(ellipse 62% 70% at 22% 34%, var(--clr-bg), transparent 78%);
  pointer-events: none;
}

.journey-body {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, .86fr) minmax(0, 1.14fr);
  grid-template-rows: auto 1fr;
  column-gap: 72px;
}
.journey-hero { grid-column: 1; grid-row: 1; padding-bottom: 48px; }
.journey-steps { grid-column: 1; grid-row: 2; list-style: none; margin: 0; }
.journey-stage { grid-column: 2; grid-row: 1 / 3; align-self: start; container: stage / inline-size; }

.journey-hero h1 {
  font-size: clamp(2.3rem, 4.4vw, 3.7rem);
  line-height: 1.06; letter-spacing: -.035em; font-weight: 800;
}
.journey-hero .hero-lead { max-width: 500px; margin-bottom: 32px; font-size: 1.12rem; }
.journey-hero .hero-cta-group { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 14px; }

/* The steps. The service name sits inside the h2 as its label, so the
   heading says both what it is and the sentence the customer would say. */
.journey-step {
  padding-block: 36px;
  border-top: 1px solid var(--border-subtle);
  scroll-margin-top: var(--stage-h, 0px);
}
.journey-step h2 {
  font-size: clamp(1.5rem, 2.4vw, 2.05rem);
  line-height: 1.18; letter-spacing: -.025em;
  margin-bottom: 16px;
}
.step-label { display: block; }
.step-num { color: var(--clr-text-subtle); margin-right: 4px; }
.journey-step > p:not(.service-deliverables-label) {
  max-width: 470px; margin-bottom: 24px;
  color: var(--clr-text-muted); font-size: 1.06rem; line-height: 1.7;
}

.service-deliverables-label { color: var(--clr-text-subtle); margin-bottom: 14px; }
.service-deliverables { list-style: none; display: grid; gap: 12px; }
.service-deliverables li {
  position: relative; padding-left: 30px;
  color: var(--clr-text-muted); line-height: 1.55;
}
.service-deliverables li::before {
  content: '✓'; position: absolute; left: 0; top: 0;
  color: var(--clr-accent); font-size: .95rem;
}

/* ---------- The rail ---------------------------------------
   The page's service navigation, and while the story is live also its
   progress bar: the accent line grows with --scene. */

.journey-rail { margin-bottom: 18px; }
.journey-rail ol {
  position: relative;
  display: flex; flex-wrap: wrap; justify-content: space-between; column-gap: 12px;
  list-style: none; margin: 0; padding: 0 0 6px;
}
.journey-rail ol::before,
.journey-rail ol::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; border-radius: 2px;
}
.journey-rail ol::before { background: var(--border-subtle); }
.journey-rail ol::after { background: var(--clr-accent); transform-origin: left; scale: 0 1; }
.journey-rail a {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  font-family: var(--ff-label); font-size: .72rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--clr-text-muted);
}
.rail-num { color: var(--clr-text-subtle); }
.journey-rail a:hover, .journey-rail a.is-current { color: var(--clr-text); }
.journey-rail a.is-current .rail-num { color: var(--clr-accent-text); }
.journey-rail a:focus-visible { outline: 3px solid var(--clr-accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* ---------- The stage --------------------------------------
   Sized from its own width (container units), so the one composition
   scales from a phone to the desktop column. The workspace is, top to
   bottom: a label saying it is an example, the dock of connected apps, the
   wires, and the document. */

.journey-canvas {
  --row-h: 30px;
  --letters-h: 22px;
  --gutter: 30px;
  --rows: 8;
  --dock-h: 62px;
  --wires-h: 12cqi; /* the wire SVG is 300 x 36 at full width */
  --cols: 10% 16% minmax(0, 1fr) 13% 22%;
  position: relative;
}
.jr-tag {
  margin: 0 0 14px;
  font-family: var(--ff-label); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--clr-text-subtle);
}

/* The dock: the three programs the shop already uses. Cool blue is the
   palette's "already working" colour, so a connected app carries it. */
.jr-dock { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4cqi; height: var(--dock-h); }
.jr-app {
  position: relative;
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 10px;
  align-content: center; align-items: center;
  padding: 0 14px;
  background: var(--clr-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 28px rgba(0, 0, 0, .28);
}
.jr-app svg { grid-row: 1 / 3; width: 22px; height: 22px; color: var(--clr-text-muted); }
.jr-app-name { min-width: 0; font-size: .86rem; font-weight: 600; line-height: 1.25; color: var(--clr-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jr-app-state {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--ff-label); font-size: .6rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--clr-cool-text);
}
.jr-app-state::before {
  content: ''; flex-shrink: 0;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--clr-cool); box-shadow: 0 0 0 3px var(--clr-cool-a25);
}

.jr-wires { display: block; width: 100%; height: auto; overflow: visible; }
.jr-wires path { stroke: var(--clr-cool-a40); stroke-width: 1; stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 0; }
/* Data travelling down the wires. Extra content: only the live story has it. */
.jr-wires .jr-pulses path { stroke: var(--clr-cool-text); stroke-width: 1.8; stroke-dasharray: .1 .9; opacity: 0; }

/* The document is the shared paper window from experience.css (.story-doc,
   its title bar and its sheet tab). */
.jr-docwrap { position: relative; }
.jr-doc .report-mock-bar { gap: 7px; }
.jr-sync {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--ff-label); font-size: .64rem; font-weight: 500; letter-spacing: .03em;
  color: var(--clr-paper-muted); white-space: nowrap;
}
.jr-sync::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--clr-cool); }

.jr-sheet {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  font-size: .8rem; color: var(--clr-paper-ink);
}
/* Row numbers are their own column, so a deleted row closes up underneath
   them the way it does in a real spreadsheet instead of taking its number. */
.jr-gutter {
  display: grid; grid-template-rows: var(--letters-h); grid-auto-rows: var(--row-h);
  height: calc(var(--letters-h) + var(--rows) * var(--row-h)); overflow: hidden;
  background: var(--doc-chrome);
  border-right: 1px solid var(--clr-paper-line);
}
.jr-gutter span {
  display: flex; align-items: center; justify-content: center;
  border-bottom: 1px solid var(--clr-paper-line);
  font-family: var(--ff-label); font-size: .62rem; color: var(--clr-paper-muted);
}
.jr-rows { position: relative; height: calc(var(--rows) * var(--row-h)); overflow: hidden; }
.jr-row { display: grid; grid-template-columns: var(--cols); height: var(--row-h); overflow: hidden; }
.jr-row > span {
  min-width: 0; padding: 0 8px;
  line-height: calc(var(--row-h) - 1px);
  border-right: 1px solid var(--clr-paper-line);
  border-bottom: 1px solid var(--clr-paper-line);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.jr-row > span:last-child { border-right: 0; }
.jr-row.is-letters { height: var(--letters-h); background: var(--doc-chrome); }
.jr-row.is-letters > span {
  line-height: calc(var(--letters-h) - 1px); text-align: center;
  font-family: var(--ff-label); font-size: .62rem; color: var(--clr-paper-muted);
}
.jr-row.is-head { font-weight: 600; }
.jr-row .c-num { text-align: right; font-variant-numeric: tabular-nums; }

/* A cell that changes its value between scenes holds both, stacked. */
.jr-swap { display: grid; }
.jr-swap > span { grid-area: 1 / 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.jr-swap > .v-before { opacity: 0; visibility: hidden; }
.is-error { font-family: var(--ff-label); font-weight: 600; color: var(--clr-accent-deep); }
.jr-caret {
  display: inline-block; width: 1.5px; height: 1.05em; margin-left: 1px;
  vertical-align: -.15em; background: var(--clr-paper-ink);
}
/* The status column automation fills in: a cool chip for an order that went
   through on its own, an orange one for the order handed to a person. Ink
   on a tint, so the text never relies on the blue itself for contrast. */
.jr-chip {
  display: inline-flex; align-items: center; gap: 5px;
  max-width: 100%; padding: 1px 7px;
  border-radius: 999px;
  background: var(--clr-cool-a25);
  font-weight: 600; font-size: .92em; line-height: 1.5;
  overflow: hidden; text-overflow: ellipsis;
}
.jr-chip::before { content: '✓'; }
.jr-chip.is-flag { background: var(--clr-accent-a18); color: var(--clr-accent-deep); }
.jr-chip.is-flag::before { content: none; }
/* The robot's cursor. Decoration that exists only while scene 2 plays. */
.jr-scan { display: none; }
.jr-row.is-dupe { height: 0; }
.jr-row.is-dupe > span { background: var(--clr-accent-a12); }
.jr-doc .sheet-tabs { padding-left: var(--gutter); }

/* The sheet as a table. Once the three sources feed it, it is no longer a
   spreadsheet someone types into but a table in the database sense: the
   row numbers and column letters fall away, the grid keeps only its row
   lines, the header becomes field names, and the tab takes a database
   mark. This is the destination, so it is the default; the live story
   restores the spreadsheet chrome until scene 1. The rows area grows by
   the letters row it absorbs, so the document keeps its height through
   the change. */
.jr-gutter { width: 0; min-width: 0; opacity: 0; }
.jr-row.is-letters { height: 0; }
.jr-rows { height: calc(var(--letters-h) + var(--rows) * var(--row-h)); }
.jr-row > span { border-right-color: transparent; }
.jr-row.is-empty > span { border-bottom-color: transparent; }
.jr-row.is-head { background: var(--doc-chrome); }
.jr-row.is-head > span {
  font-family: var(--ff-label); font-size: .6rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--clr-paper-muted);
}
.jr-doc .sheet-tabs { padding-left: 0; }
.jr-tab-db { flex-shrink: 0; width: 13px; height: 13px; margin-right: 6px; color: var(--clr-cool); }
.jr-meta {
  display: flex; align-items: center; gap: 7px;
  min-width: 0; margin-left: auto; padding: 0 14px;
  font-family: var(--ff-label); font-size: .6rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--clr-paper-muted); white-space: nowrap; overflow: hidden;
}
.jr-meta::before { content: ''; flex-shrink: 0; width: 6px; height: 6px; border-radius: 50%; background: var(--clr-cool); }
/* The raw sheets Tabelle1 is copied together from, one per program. They
   exist only in scene 0, like the note: connecting the programs replaces
   them with the streams, so they are live-only extras. */
.sheet-tabs .jr-tab-raw { display: none; } /* outranks .sheet-tab in experience.css */

/* The note a person leaves themselves: the only thing on the stage in the
   accent fill, because it is the work this page offers to take away. */
.jr-note {
  display: none;
  position: absolute; top: -16px; right: -10px; z-index: 2;
  width: min(46%, 210px); margin: 0; padding: 12px 14px 16px;
  background: var(--clr-accent); color: var(--clr-bg);
  font-size: .78rem; font-weight: 600; line-height: 1.35;
  border-radius: 2px 2px 16px 2px;
  box-shadow: 0 14px 26px rgba(0, 0, 0, .32);
  rotate: 4deg;
}

/* The title and the tab that change when the sheet becomes a report, the
   report itself and the assistant are live-only extras, like the note: the
   static stage is the connected, automated sheet and nothing more. */
.jr-title { display: grid; }
.jr-title b { grid-area: 1 / 1; font-weight: inherit; }
.jr-title .t-report,
.sheet-tabs .jr-tab-report, /* outranks .sheet-tab in experience.css, which loads later */
.jr-report,
.jr-assist { display: none; }

.jr-body { display: grid; }
.jr-body > * { grid-area: 1 / 1; min-width: 0; }

/* The report shares the sheet's grid cell, so the document keeps its size
   when one turns into the other. One series, one hue: the bars are the
   amount column summed per product, labelled in ink. */
.jr-report {
  flex-direction: column; justify-content: center; gap: 10px;
  padding: 12px 16px;
  font-size: .8rem; color: var(--clr-paper-ink);
}
.jr-kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.jr-kpi {
  display: grid; gap: 2px; margin: 0; padding: 8px 10px;
  background: var(--doc-chrome);
  border: 1px solid var(--clr-paper-line);
  border-radius: var(--radius-sm);
}
.jr-kpi span, .jr-chart-title {
  font-family: var(--ff-label); font-size: .6rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--clr-paper-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.jr-kpi b { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.jr-chart-title { margin: 4px 0 0; }
.jr-bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.jr-bars li { display: grid; grid-template-columns: 32% minmax(0, 1fr) 4.6em; align-items: center; gap: 10px; }
.jr-bar-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jr-bar i {
  display: block; height: 12px; width: calc(var(--v) * 100%);
  background: var(--clr-cool); border-radius: 0 4px 4px 0;
  transform-origin: left;
}
/* €89.00, €58.70 and €12.50, as a share of the largest. */
.jr-bars li:nth-child(1) { --v: 1; }
.jr-bars li:nth-child(2) { --v: .66; }
.jr-bars li:nth-child(3) { --v: .14; }
.jr-bars b { font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }

/* The assistant: a dark card laid over the document, because it is not
   part of the spreadsheet but works from it. The one accent fill is the
   button a person presses, which is the point of the scene. */
.jr-assist {
  position: absolute; right: -14px; bottom: -28px; z-index: 3;
  width: min(62%, 310px);
  gap: 8px; padding: 12px;
  background: var(--clr-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: 0 22px 44px rgba(0, 0, 0, .42);
  color: var(--clr-text); font-size: .76rem; line-height: 1.4;
}
.jr-assist-head {
  display: flex; align-items: center; gap: 7px; margin: 0;
  font-family: var(--ff-label); font-size: .62rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--clr-text-muted);
}
.jr-assist-head svg { width: 15px; height: 15px; color: var(--clr-cool-text); }
.jr-msg { display: grid; gap: 3px; padding: 8px 10px; border-radius: 10px; }
.jr-msg.is-in { margin-right: 12%; background: var(--clr-surface-1); border-top-left-radius: 2px; }
.jr-msg.is-draft { margin-left: 8%; background: var(--clr-surface-3); border: 1px solid var(--clr-cool-a40); border-top-right-radius: 2px; }
.jr-msg-from {
  font-family: var(--ff-label); font-size: .56rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--clr-text-subtle);
}
.jr-draft { display: grid; }
.jr-draft > * { grid-area: 1 / 1; }
.jr-typing { display: flex; align-items: center; gap: 4px; opacity: 0; }
.jr-typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--clr-text-muted); animation: jrDots 1s ease-in-out infinite; }
.jr-typing i:nth-child(2) { animation-delay: .15s; }
.jr-typing i:nth-child(3) { animation-delay: .3s; }
.jr-source { font-size: .64rem; color: var(--clr-cool-text); }
.jr-review {
  justify-self: end;
  padding: 6px 12px; border-radius: var(--radius-full);
  background: var(--clr-accent); color: var(--clr-bg);
  font-family: var(--ff-label); font-size: .6rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}

/* A narrow stage (phones) drops the date column, shortens the sheet and
   turns each app into an icon over its name. */
@container stage (max-width: 460px) {
  .journey-canvas {
    --row-h: 22px;
    --letters-h: 16px;
    --gutter: 22px;
    --rows: 6;
    --dock-h: 48px;
    --cols: 14% minmax(0, 1fr) 21% 30%;
  }
  .jr-tag { margin-bottom: 10px; font-size: .6rem; }
  .jr-app { grid-template-columns: minmax(0, 1fr); justify-items: center; row-gap: 3px; padding: 0 6px; }
  .jr-app svg { grid-row: auto; width: 17px; height: 17px; }
  .jr-app-name { font-size: .7rem; }
  .jr-app-state { position: absolute; top: 6px; right: 6px; font-size: 0; }
  .jr-sheet { font-size: .66rem; }
  .jr-row > span { padding: 0 5px; }
  .jr-row .c-date { display: none; }
  .jr-sync { font-size: .56rem; }
  .jr-doc .report-mock-bar { padding-block: 9px; }
  .jr-doc .sheet-tabs { height: 26px; }
  .jr-meta { padding: 0 8px; font-size: .5rem; }
  .jr-tab-db { width: 11px; height: 11px; margin-right: 4px; }
  .jr-doc :is(.sheet-tab, .sheet-tab-add) { padding: 0 8px; font-size: .64rem; }
  .jr-note { width: 48%; padding: 9px 10px 12px; font-size: .64rem; }
  .jr-report { gap: 5px; padding: 6px 10px; font-size: .62rem; }
  .jr-kpis { gap: 6px; }
  .jr-kpi { padding: 4px 6px; gap: 0; }
  .jr-kpi span, .jr-chart-title { font-size: .5rem; }
  .jr-kpi b { font-size: .78rem; }
  .jr-chart-title { margin: 0; }
  .jr-bars { gap: 4px; }
  .jr-bars li { gap: 6px; }
  .jr-bar i { height: 8px; }
  .jr-assist { right: -6px; bottom: -14px; width: 66%; gap: 5px; padding: 8px; font-size: .62rem; }
  .jr-assist-head { font-size: .52rem; }
  .jr-assist-head svg { width: 12px; height: 12px; }
  .jr-msg { padding: 5px 7px; }
  .jr-msg-from { font-size: .48rem; }
  .jr-source { font-size: .54rem; }
  .jr-review { padding: 4px 9px; font-size: .5rem; }
}

/* ---------- The journey, live ------------------------------
   Everything below is scoped to .story-live, which story.js adds only when
   JavaScript runs AND motion is welcome. Transitions are scoped further, to
   .story-ready, two frames later, so the switch into the pinned layout is
   instant and only the scene changes the visitor scrolls through animate. */

/* One screen per step. The section loses its top offset so every page,
   the hero included, is exactly the part of the viewport under the fixed
   header, and the page snaps from one to the next. The snap is mandatory
   and stops at every page, so one flick of the wheel is one page. Anything
   after the journey is a single snap area taller than the screen (see
   .journey-after in the template), which a browser scrolls through freely,
   and the footer's end is the last snap position. */
.journey.story-live { --journey-top: 0px; padding-bottom: 0; }
html:has(.journey.story-live) { scroll-snap-type: y mandatory; }
/* The root's scroll-padding (experience.css) already clears the header plus
   24px of air; a page takes the air back so it lands flush under the header,
   or under the pinned stage on phones. */
.story-live :is(.journey-hero, .journey-step) {
  scroll-snap-align: start; scroll-snap-stop: always;
  scroll-margin-top: calc(var(--stage-h, 0px) - 24px);
}
.journey.story-live ~ .journey-after { scroll-snap-align: start; }
html:has(.journey.story-live) .site-footer { scroll-snap-align: end; }

/* Pinned for as long as the steps beside it are on screen, filling the
   part of the viewport the fixed header leaves free. */
.story-live .journey-stage {
  position: sticky; top: var(--header-height);
  height: calc(100svh - var(--header-height));
  margin-top: calc(var(--journey-top) * -1);
  display: flex; flex-direction: column; justify-content: center;
}
.story-live .journey-hero {
  min-height: calc(100svh - var(--header-height) - var(--journey-top));
  display: flex; flex-direction: column; justify-content: center;
  padding-bottom: var(--journey-top);
}
.story-live .journey-step {
  min-height: calc(100svh - var(--header-height));
  display: flex; flex-direction: column; justify-content: center;
  padding-block: 40px;
  border: 0;
}
/* The page change. Only the current page's text is shown: the page being
   left slides out the way it was scrolled, and the new one follows it in,
   line by line. Off the current page nothing is on screen to read, so
   fading it out costs no contrast. */
.story-live :is(.journey-hero, .journey-step):not(.is-current) > * { opacity: 0; translate: 0 36px; }
.story-live :is(.journey-hero, .journey-step).is-past > * { translate: 0 -36px; }
.story-ready :is(.journey-hero, .journey-step):not(.is-current) > * {
  transition: opacity .25s ease, translate .4s var(--ease-out);
}
.story-ready :is(.journey-hero, .journey-step).is-current > * {
  transition: opacity .5s ease var(--td), translate .75s cubic-bezier(.22, 1, .36, 1) var(--td);
}
:is(.journey-hero, .journey-step) > :nth-child(1) { --td: .12s; }
:is(.journey-hero, .journey-step) > :nth-child(2) { --td: .2s; }
:is(.journey-hero, .journey-step) > :nth-child(3) { --td: .28s; }
:is(.journey-hero, .journey-step) > :nth-child(4) { --td: .36s; }
:is(.journey-hero, .journey-step) > :nth-child(5) { --td: .44s; }

/* The camera. Each page frames the stage differently: the hero shows the
   sheet at an angle like an object on a desk, today looks at it head on,
   and later pages turn it towards whatever changed. Moving between framings,
   the stage dips back and returns, which is what makes a scroll read as a
   cut to the next scene rather than a slide. The dip alternates between
   two identical animations so that every page change restarts it. */
.story-live .journey-stage { isolation: isolate; }
.story-live .journey-canvas { transform: perspective(1600px) rotateX(0deg) rotateY(0deg) scale(1); }
.story-live .journey-stage[data-page="0"] .journey-canvas { transform: perspective(1600px) rotateX(10deg) rotateY(-18deg) scale(.9); }
.story-live .journey-stage[data-page="1"] .journey-canvas { transform: perspective(1600px) rotateX(0deg) rotateY(0deg) scale(1.03); }
.story-live .journey-stage[data-page="3"] .journey-canvas { transform: perspective(1600px) rotateX(3deg) rotateY(7deg) scale(1); }
.story-live .journey-stage[data-page="4"] .journey-canvas { transform: perspective(1600px) rotateX(3deg) rotateY(-7deg) scale(1); }
.story-live .journey-stage[data-page="5"] .journey-canvas { transform: perspective(1600px) rotateX(6deg) rotateY(0deg) scale(.97); }
.story-ready .journey-canvas { transition: transform 1.1s cubic-bezier(.22, 1, .36, 1); }
.story-ready .journey-stage:is([data-page="0"], [data-page="2"], [data-page="4"]) .journey-canvas { animation: jrDipA .8s var(--ease-out); }
.story-ready .journey-stage:is([data-page="1"], [data-page="3"], [data-page="5"]) .journey-canvas { animation: jrDipB .8s var(--ease-out); }

/* A soft light behind the stage that drifts to a new spot on every page.
   Cool blue, never the accent: see the note on tinting in base.css. */
.story-live .journey-stage::before {
  content: '';
  position: absolute; z-index: -1; inset: 12% 0;
  background: radial-gradient(closest-side, var(--clr-cool-a12), transparent);
  pointer-events: none;
}
.story-live .journey-stage[data-page="0"]::before { translate: -12% 4%; }
.story-live .journey-stage[data-page="1"]::before { translate: 6% 10%; }
.story-live .journey-stage[data-page="2"]::before { translate: 0 -10%; }
.story-live .journey-stage[data-page="3"]::before { translate: 12% 4%; }
.story-live .journey-stage[data-page="4"]::before { translate: -8% 10%; }
.story-live .journey-stage[data-page="5"]::before { translate: 10% 14%; }
.story-ready .journey-stage::before { transition: translate 1.4s cubic-bezier(.22, 1, .36, 1); }

@keyframes jrDipA { 30% { opacity: .45; scale: .95; } }
@keyframes jrDipB { 30% { opacity: .45; scale: .95; } }

.story-live .journey-rail ol::after { scale: calc(var(--scene, 0) / 4) 1; }
.story-ready .journey-rail ol::after { transition: scale .7s var(--ease-out); }

/* Scene 0, today. The sheet is filled in by hand: a note on it, one value
   still being typed, a broken reference and a duplicated row. Nothing is
   connected yet, so the document sits alone in the middle of the stage. */
.story-live .jr-note { display: block; }
.story-live .journey-stage:not(.reached-1) .jr-docwrap { translate: 0 calc((var(--dock-h) + var(--wires-h)) / -2); }
.story-live .journey-stage:not(.reached-1) .jr-app { opacity: 0; translate: 0 -16px; scale: .94; }
.story-live .journey-stage:not(.reached-1) .jr-wires > path { stroke-dashoffset: 1; }
.story-live .journey-stage:not(.reached-1) .jr-sync { opacity: 0; }
.story-live .journey-stage:not(.reached-1) .jr-swap > .v-before { opacity: 1; visibility: visible; }
.story-live .journey-stage:not(.reached-1) .jr-swap > .v-after { opacity: 0; visibility: hidden; }
.story-live .journey-stage:not(.reached-1) .jr-row.is-dupe { height: var(--row-h); }
/* Only the text waits; the empty cells keep their grid lines. */
.story-live .journey-stage:not(.reached-1) .jr-row.is-new > span { color: transparent; }
.story-live .journey-stage:not(.reached-1) .jr-caret { animation: jrBlink 1.05s steps(1) infinite; }

/* Scene 1, connected. In order: the note comes off, the sheet makes room,
   the apps dock one after another, the wires draw, and then the sheet
   corrects itself — the broken value arrives, the duplicate closes up, the
   typed value completes and a new order flows in on its own. The delays
   travel in --d because a transition shorthand resets transition-delay. */
.story-live .journey-stage.reached-1 .jr-note { opacity: 0; visibility: hidden; translate: 44px -34px; rotate: 18deg; }
.story-live .journey-stage.reached-1 .jr-pulses path { opacity: 1; animation: jrPulse 1.7s linear infinite; }
.jr-pulses path:nth-child(2) { animation-delay: -.6s; }
.jr-pulses path:nth-child(3) { animation-delay: -1.1s; }

.jr-app:nth-child(1) { --d: .3s; }
.jr-app:nth-child(2) { --d: .42s; }
.jr-app:nth-child(3) { --d: .54s; }
.jr-wires > path:nth-child(1) { --d: .62s; }
.jr-wires > path:nth-child(2) { --d: .72s; }
.jr-wires > path:nth-child(3) { --d: .82s; }
.jr-row.is-new > span:nth-child(1) { --d: 1.9s; }
.jr-row.is-new > span:nth-child(2) { --d: 1.96s; }
.jr-row.is-new > span:nth-child(3) { --d: 2.02s; }
.jr-row.is-new > span:nth-child(4) { --d: 2.08s; }

.story-ready .journey-stage.reached-1 .jr-note {
  transition: opacity .35s ease .1s, translate .55s var(--ease-out), rotate .55s var(--ease-out), visibility 0s linear .55s;
}
.story-ready .journey-stage.reached-1 .jr-docwrap { transition: translate .7s var(--ease-out) .12s; }
.story-ready .journey-stage.reached-1 .jr-app {
  transition: opacity .45s var(--ease-out) var(--d), translate .6s var(--ease-out) var(--d), scale .6s var(--ease-out) var(--d);
}
.story-ready .journey-stage.reached-1 .jr-wires > path { transition: stroke-dashoffset .6s var(--ease-out) var(--d); }
.story-ready .journey-stage.reached-1 .jr-pulses path { transition: opacity .4s ease 1.3s; }
.story-ready .journey-stage.reached-1 .jr-swap > .v-before { transition: opacity .25s ease 1.2s, visibility 0s linear 1.45s; }
.story-ready .journey-stage.reached-1 .jr-swap > .v-after { transition: opacity .4s ease 1.3s, visibility 0s linear 1.3s; }
.story-ready .journey-stage.reached-1 .jr-row.is-fixed .c-num { animation: jrFlash 1.6s ease-out 1.3s; }
.story-ready .journey-stage.reached-1 .jr-row.is-dupe { transition: height .5s var(--ease-out) 1.55s; }
.story-ready .journey-stage.reached-1 .jr-row.is-new > span {
  transition: color .4s ease var(--d), border-color .4s ease var(--m);
  animation: jrFlash 1.8s ease-out var(--d);
}
.story-ready .journey-stage.reached-1 .jr-sync { transition: opacity .5s ease 2.2s; }

/* Back to today: everything returns at once, quickly. */
.story-ready .journey-stage:not(.reached-1) :is(.jr-note, .jr-docwrap, .jr-app, .jr-sync, .jr-swap > span, .jr-row.is-dupe, .jr-row.is-new > span) {
  transition: opacity .3s ease, color .3s ease, translate .45s var(--ease-out), scale .45s var(--ease-out), rotate .45s var(--ease-out), height .35s var(--ease-out), visibility 0s;
}
.story-ready .journey-stage:not(.reached-1) .jr-wires > path { transition: stroke-dashoffset .3s ease; }

/* Scene 1 also turns the sheet into a table (see "The sheet as a table"
   above). The raw sheets rise out of the tab bar as the programs dock, so
   each one becomes a stream; once the wires land, the spreadsheet chrome
   recedes, and the corrections that follow happen in the table. The tab
   bar reports the sources last, with the sync time. */
.story-live .sheet-tabs .jr-tab-raw {
  display: flex; max-width: 9em; overflow: hidden; white-space: nowrap;
}
.story-live .journey-stage:not(.reached-1) .jr-gutter { width: var(--gutter); opacity: 1; }
.story-live .journey-stage:not(.reached-1) .jr-row.is-letters { height: var(--letters-h); }
.story-live .journey-stage:not(.reached-1) .jr-rows { height: calc(var(--rows) * var(--row-h)); }
.story-live .journey-stage:not(.reached-1) .jr-row > span { border-right-color: var(--clr-paper-line); }
.story-live .journey-stage:not(.reached-1) .jr-row.is-empty > span { border-bottom-color: var(--clr-paper-line); }
.story-live .journey-stage:not(.reached-1) .jr-row.is-head { background: transparent; }
.story-live .journey-stage:not(.reached-1) .jr-row.is-head > span {
  font-family: inherit; font-size: inherit; letter-spacing: normal; text-transform: none;
  color: var(--clr-paper-ink);
}
.story-live .journey-stage:not(.reached-1) .sheet-tabs { padding-left: var(--gutter); }
.story-live .journey-stage:not(.reached-1) .jr-tab-db { width: 0; margin-right: 0; opacity: 0; }
.story-live .journey-stage:not(.reached-1) .jr-meta { opacity: 0; translate: 8px 0; }
.story-live .journey-stage.reached-1 .jr-tab-raw { max-width: 0; padding-inline: 0; opacity: 0; translate: 0 -12px; }

.journey-canvas { --m: .9s; }
.jr-tab-raw:nth-child(2) { --d: .1s; }
.jr-tab-raw:nth-child(3) { --d: .22s; }
.jr-tab-raw:nth-child(4) { --d: .34s; }
.story-ready .journey-stage.reached-1 .jr-tab-raw {
  transition: opacity .3s ease var(--d), translate .4s var(--ease-out) var(--d), max-width .45s var(--ease-out) calc(var(--d) + .25s), padding .45s var(--ease-out) calc(var(--d) + .25s);
}
.story-ready .journey-stage.reached-1 .jr-gutter { transition: opacity .3s ease var(--m), width .5s var(--ease-out) var(--m); }
.story-ready .journey-stage.reached-1 .jr-rows { transition: height .5s var(--ease-out) var(--m); }
.story-ready .journey-stage.reached-1 .jr-row.is-letters { transition: height .5s var(--ease-out) var(--m); }
.story-ready .journey-stage.reached-1 .jr-row.is-head { transition: background-color .4s ease var(--m), grid-template-columns .5s var(--ease-out); }
.story-ready .journey-stage.reached-1 .jr-row > span { transition: border-color .4s ease var(--m); }
.story-ready .journey-stage.reached-1 .jr-row.is-head > span { transition: border-color .4s ease var(--m), color .4s ease var(--m); }
.story-ready .journey-stage.reached-1 .sheet-tabs { transition: padding-left .5s var(--ease-out) var(--m); }
.story-ready .journey-stage.reached-1 .jr-tab-db { transition: width .4s var(--ease-out) var(--m), margin-right .4s var(--ease-out) var(--m), opacity .3s ease calc(var(--m) + .2s); }
.story-ready .journey-stage.reached-1 .jr-meta { transition: opacity .4s ease 2.3s, translate .5s var(--ease-out) 2.3s; }
.story-ready .journey-stage:not(.reached-1) :is(.jr-gutter, .jr-rows, .jr-row.is-letters, .jr-row.is-head, .jr-row > span, .sheet-tabs, .jr-tab-db, .jr-meta, .jr-tab-raw) {
  transition: opacity .25s ease, width .3s ease, height .3s ease, max-width .3s ease, padding .3s ease, margin-right .3s ease, translate .3s ease, border-color .25s ease, background-color .25s ease, color .25s ease, grid-template-columns .5s var(--ease-out);
}

/* Scene 2, automated. The status column widens, and a cursor works down
   the orders the way a person would click through them, leaving a status
   on each one as it passes. The one order that doesn't fit is handed on. */
.story-live .journey-stage:not(.reached-2) .journey-canvas { --cols: 11% 16% minmax(0, 1fr) 14% 13%; }
.story-live .journey-stage:not(.reached-2) .jr-row.is-head .c-status { color: transparent; }
.story-live .journey-stage:not(.reached-2) .jr-chip { opacity: 0; scale: .6; }

.jr-rows > .jr-row:nth-child(2) .jr-chip { --d: .65s; }
.jr-rows > .jr-row:nth-child(3) .jr-chip { --d: 1.1s; }
.jr-rows > .jr-row:nth-child(4) .jr-chip { --d: 1.55s; }
.jr-rows > .jr-row:nth-child(6) .jr-chip { --d: 2s; }
.jr-rows > .jr-row:nth-child(7) .jr-chip { --d: 2.45s; }

.story-ready .jr-row { transition: grid-template-columns .5s var(--ease-out); }
.story-ready .journey-stage.reached-2 .jr-row.is-head .c-status { transition: color .3s ease .3s; }
.story-ready .journey-stage.reached-2 .jr-chip {
  transition: opacity .25s ease var(--d), scale .45s cubic-bezier(.34, 1.56, .64, 1) var(--d);
}
.story-ready .journey-stage.reached-2:not(.reached-3) .jr-scan {
  display: block;
  position: absolute; left: 0; right: 0; top: 0; height: var(--row-h);
  border: 2px solid var(--clr-cool);
  border-radius: 3px;
  pointer-events: none;
  opacity: 0;
  animation: jrScan 2.5s linear .5s both;
}
.story-ready .journey-stage:not(.reached-2) :is(.jr-chip, .jr-row.is-head .c-status) {
  transition: opacity .2s ease, scale .2s ease, color .2s ease;
}
@container stage (max-width: 460px) {
  .story-live .journey-stage:not(.reached-2) .journey-canvas { --cols: 16% minmax(0, 1fr) 23% 12%; }
}

/* Scene 3, analysed. The amount column lights up, the sheet steps back and
   the same figures come forward as a report: the key figures first, then
   the bars, then their values. A Report tab opens beside Tabelle1. */
.story-live .jr-title .t-report { display: block; }
.story-live .jr-tab-report { display: flex; }
.story-live .jr-report { display: flex; }
.story-live .journey-stage:not(.reached-3) .jr-title .t-report { opacity: 0; }
.story-live .journey-stage:not(.reached-3) .jr-tab-report { opacity: 0; translate: -10px 0; }
.story-live .journey-stage:not(.reached-3) .jr-report { opacity: 0; visibility: hidden; }
.story-live .journey-stage:not(.reached-3) .jr-kpi { opacity: 0; translate: 0 12px; }
.story-live .journey-stage:not(.reached-3) .jr-bar i { scale: 0 1; }
.story-live .journey-stage:not(.reached-3) .jr-bars b { opacity: 0; }
.story-live .journey-stage.reached-3 .jr-title .t-sheet { opacity: 0; }
.story-live .journey-stage.reached-3 .jr-sheet { opacity: 0; visibility: hidden; scale: .97; }
.story-live .journey-stage.reached-3 .sheet-tab.is-active { background: transparent; box-shadow: none; color: var(--clr-paper-muted); font-weight: 400; }
.story-live .journey-stage.reached-3 .jr-tab-report { background: var(--doc-bg); color: var(--clr-paper-ink); font-weight: 600; box-shadow: inset 0 -2px 0 var(--clr-accent); }

.jr-kpi:nth-child(1) { --d: .95s; }
.jr-kpi:nth-child(2) { --d: 1.05s; }
.jr-kpi:nth-child(3) { --d: 1.15s; }
.jr-bars li:nth-child(1) { --d: 1.25s; }
.jr-bars li:nth-child(2) { --d: 1.35s; }
.jr-bars li:nth-child(3) { --d: 1.45s; }

.story-ready .journey-stage.reached-3 .jr-rows > .jr-row:not(.is-head) .c-num { animation: jrFlash 1s ease-out; }
.story-ready .journey-stage.reached-3 .jr-sheet {
  transition: opacity .35s ease .6s, scale .5s var(--ease-out) .6s, visibility 0s linear .95s;
}
.story-ready .journey-stage.reached-3 .jr-report { transition: opacity .4s ease .8s, visibility 0s linear .8s; }
.story-ready .journey-stage.reached-3 :is(.jr-title b, .jr-tab-report, .sheet-tab.is-active) {
  transition: opacity .3s ease .8s, translate .4s var(--ease-out) .8s, background-color .3s ease .8s, color .3s ease .8s, box-shadow .3s ease .8s;
}
.story-ready .journey-stage.reached-3 .jr-kpi { transition: opacity .35s ease var(--d), translate .5s var(--ease-out) var(--d); }
.story-ready .journey-stage.reached-3 .jr-bar i { transition: scale .8s cubic-bezier(.22, 1, .36, 1) var(--d); }
.story-ready .journey-stage.reached-3 .jr-bars b { transition: opacity .3s ease calc(var(--d) + .45s); }
.story-ready .journey-stage:not(.reached-3) :is(.jr-sheet, .jr-report, .jr-title b, .jr-tab-report, .sheet-tab.is-active, .jr-kpi, .jr-bar i, .jr-bars b) {
  transition: opacity .25s ease, scale .3s ease, translate .3s ease, visibility 0s, background-color .25s ease, color .25s ease, box-shadow .25s ease;
}

/* Scene 4, asked. A customer writes about #1042, the order handed to the
   team in scene 2; the report steps back, the assistant comes forward,
   thinks, drafts a reply from the sheet, and leaves the sending to a person. */
.story-live .jr-assist { display: grid; }
.story-live .journey-stage:not(.reached-4) .jr-assist { opacity: 0; visibility: hidden; translate: 0 40px; scale: .92; }
.story-live .journey-stage:not(.reached-4) :is(.jr-msg, .jr-draft-text, .jr-source) { opacity: 0; translate: 0 8px; }
.story-live .journey-stage:not(.reached-4) .jr-review { opacity: 0; scale: .8; }
.story-live .journey-stage.reached-4 .jr-doc { scale: .95; translate: -4% -3%; }

.jr-msg.is-in { --d: .6s; }
.jr-msg.is-draft { --d: 1.05s; }
.jr-draft-text { --d: 2.25s; }
.jr-source { --d: 2.5s; }
.jr-review { --d: 2.8s; }

.story-ready .jr-doc { transition: scale .8s cubic-bezier(.22, 1, .36, 1), translate .8s cubic-bezier(.22, 1, .36, 1); }
.story-ready .journey-stage.reached-4 .jr-assist {
  transition: opacity .4s ease .2s, translate .8s cubic-bezier(.22, 1, .36, 1) .2s, scale .8s cubic-bezier(.22, 1, .36, 1) .2s, visibility 0s linear .2s;
}
.story-ready .journey-stage.reached-4 :is(.jr-msg, .jr-draft-text, .jr-source) {
  transition: opacity .35s ease var(--d), translate .5s var(--ease-out) var(--d);
}
.story-ready .journey-stage.reached-4 .jr-typing { animation: jrTyping 1.1s linear 1.15s both; }
.story-ready .journey-stage.reached-4 .jr-review {
  transition: opacity .3s ease var(--d), scale .5s cubic-bezier(.34, 1.56, .64, 1) var(--d);
}
.story-ready .journey-stage:not(.reached-4) :is(.jr-assist, .jr-msg, .jr-draft-text, .jr-source, .jr-review) {
  transition: opacity .25s ease, translate .3s ease, scale .3s ease, visibility 0s linear .3s;
}

@keyframes jrTyping { 0%, 100% { opacity: 0; } 12%, 88% { opacity: 1; } }
@keyframes jrDots { 50% { translate: 0 -3px; opacity: .5; } }
@keyframes jrScan {
  0% { translate: 0 var(--row-h); opacity: 0; }
  4% { opacity: 1; }
  12% { translate: 0 var(--row-h); }
  18%, 30% { translate: 0 calc(var(--row-h) * 2); }
  36%, 48% { translate: 0 calc(var(--row-h) * 3); }
  54%, 66% { translate: 0 calc(var(--row-h) * 4); }
  72%, 88% { translate: 0 calc(var(--row-h) * 5); opacity: 1; }
  100% { translate: 0 calc(var(--row-h) * 5); opacity: 0; }
}
@keyframes jrBlink { 50% { opacity: 0; } }
@keyframes jrPulse { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes jrFlash { from { background-color: var(--clr-cool-a25); } }

/* Phones and narrow tablets: the stage pins to the top of the screen on
   an opaque band, and the steps scroll up underneath it. story.js moves
   its trigger line into the strip below the stage and publishes the stage
   height, which keeps an anchor jump from landing under it. */
@media (max-width: 899px) {
  .journey { --journey-top: 36px; }
  .journey-body { display: block; }
  .journey-hero { padding-bottom: 36px; }
  .journey-stage { margin-bottom: 12px; }
  .journey-hero .hero-cta-group .btn { width: 100%; }

  .story-live .journey-hero { min-height: 0; display: block; padding-bottom: 32px; }
  .story-live .journey-stage {
    z-index: 2;
    height: auto; margin-top: 0; padding-block: 4px 14px;
    display: block;
    background: var(--clr-bg);
    /* Full-bleed band without leaving the grid: the shadow paints the
       ground out to the screen edges and the clip keeps it to this height. */
    box-shadow: 0 0 0 100vmax var(--clr-bg);
    clip-path: inset(0 -100vmax);
  }
  .story-live .journey-step {
    min-height: calc(100svh - var(--header-height) - var(--stage-h, 0px));
    padding-block: 28px;
  }
  /* Only the current label is spelled out; the others keep their number
     on screen and their name for assistive technology. */
  .story-live .journey-rail a:not(.is-current) .rail-label {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
}

/* ---------- Every step works alone ------------------------ */

.ladder-note { background: var(--clr-surface-1); border-block: 1px solid var(--border-subtle); padding-block: 72px; }
.ladder-note-inner { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 56px; align-items: center; }
.ladder-note h2 { font-size: clamp(1.6rem, 2.6vw, 2.2rem); line-height: 1.15; letter-spacing: -.025em; margin: 0; }
.ladder-note p { max-width: 560px; margin: 14px 0 0; color: var(--clr-text-muted); font-size: 1.06rem; line-height: 1.65; }
.ladder-note-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }

/* ---------- Web development ------------------------------- */

.web-service { border-bottom: 1px solid var(--border-subtle); }
.web-service-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: center; padding-block: 80px; }
.web-service h2 { font-size: clamp(1.6rem, 2.6vw, 2.15rem); line-height: 1.15; letter-spacing: -.025em; }
.web-service p:not(.eyebrow) { color: var(--clr-text-muted); line-height: 1.7; margin: 18px 0 24px; }
.web-offer-list { list-style: none; display: grid; gap: 0; }
.web-offer-list li {
  padding: 20px 0; border-top: 1px solid var(--border-color);
  color: var(--clr-text-muted); line-height: 1.55;
}
.web-offer-list li:last-child { border-bottom: 1px solid var(--border-color); }
.anchor-alias { display: block; scroll-margin-top: calc(var(--header-height) + 24px); }

/* ---------- Process --------------------------------------- */

.process-band { background: var(--clr-surface-1); border-bottom: 1px solid var(--border-subtle); padding-block: 80px; }
.process-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; counter-reset: step; list-style: none; }
.process-list li {
  counter-increment: step;
  padding: 28px 26px;
  background: var(--clr-bg);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}
.process-list li::before {
  content: '0' counter(step);
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: 20px;
  border-radius: 50%;
  background: var(--clr-accent-a15); color: var(--clr-accent-text);
  font-family: var(--ff-label); font-size: .82rem; font-weight: 700;
}
.process-list h3 { font-size: 1.2rem; letter-spacing: -.015em; }
.process-list p { color: var(--clr-text-muted); line-height: 1.6; margin: 8px 0 0; }

/* ---------- FAQ ------------------------------------------- */

.service-faq { display: grid; grid-template-columns: 1fr 1.25fr; gap: 64px; padding-block: 80px; }
.service-faq .section-intro { margin-bottom: 0; }
.faq-list { display: grid; gap: 12px; align-content: start; }
.faq-item {
  background: var(--clr-surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  transition: border-color .2s ease, background .2s ease;
}
.faq-item:hover { border-color: var(--clr-accent-a30); }
.faq-item[open] { background: var(--clr-surface-2); border-color: var(--clr-accent-a30); }
.faq-item > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 20px 22px;
  font-size: 1.02rem; font-weight: 600; cursor: pointer;
  list-style: none;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary::after {
  content: '+';
  flex-shrink: 0;
  font-family: var(--ff-label); font-size: 1.3rem; font-weight: 400; line-height: 1;
  color: var(--clr-accent);
  transition: transform .2s ease;
}
.faq-item[open] > summary::after { transform: rotate(45deg); }
.faq-item > summary:hover { color: var(--clr-accent-text); }
.faq-item > p { color: var(--clr-text-muted); padding: 0 22px 20px; line-height: 1.7; margin: 0; }

/* ---------- Responsive ------------------------------------ */

/* Grid and flex children size to min-content by default, so a single long
   German compound ("Unternehmenswissen") can push a column wider than its
   track and scroll the whole page sideways on a narrow phone. */
.journey-body > *,
.ladder-note-inner > *,
.web-service-inner > *,
.service-faq > *,
.process-list > * { min-width: 0; }
.journey-hero h1,
.journey-step h2,
.journey-step p,
.service-deliverables li { overflow-wrap: break-word; }

@media (max-width: 768px) {
  .ladder-note { padding-block: 52px; }
  .ladder-note-inner { grid-template-columns: 1fr; gap: 24px; }
  .ladder-note-actions .btn { width: 100%; }

  .web-service-inner { grid-template-columns: 1fr; gap: 28px; padding-block: 56px; }
  .process-band { padding-block: 56px; }
  .process-list { grid-template-columns: 1fr; gap: 14px; }
  .process-list li { padding: 22px 20px; }
  .process-list li::before { width: 34px; height: 34px; margin-bottom: 14px; }
  .service-faq { grid-template-columns: 1fr; gap: 28px; padding-block: 56px; }
  .faq-item > summary { padding: 16px 18px; font-size: .98rem; }
  .faq-item > p { padding: 0 18px 16px; }
}

/* ---------- Pricing --------------------------------------
   Floors and ranges, not quotes. The figures use --ff-label because they are
   numbers, which is what the monospace is reserved for. The band sits on the
   page ground between two surface-1 bands, so depth comes from the cards. */

.pricing-band { padding-block: 80px; border-bottom: 1px solid var(--border-subtle); }
.pricing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.price-card {
  background: var(--clr-surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 28px 26px;
  display: flex; flex-direction: column;
  transition: border-color .2s ease, background .2s ease;
}
.price-card:hover { border-color: var(--clr-accent-a30); background: var(--clr-surface-2); }
.price-kind {
  font-family: var(--ff-label);
  font-size: .74rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  line-height: 1.45;
  color: var(--clr-text-subtle);
  /* Two lines' worth, so the figures line up across the three cards even
     though only some labels wrap. */
  min-height: 2.9em;
  margin-bottom: 18px;
}
.price-figure {
  font-family: var(--ff-label);
  font-size: clamp(1.3rem, 2.4vw, 1.65rem); font-weight: 600; letter-spacing: -.01em;
  color: var(--clr-accent-text);
}
.price-desc { color: var(--clr-text-muted); font-size: .95rem; line-height: 1.6; margin-top: 14px; }

.price-notes {
  list-style: none; display: grid; gap: 12px;
  max-width: 760px; margin-top: 36px;
  padding-left: 20px; border-left: 2px solid var(--clr-accent-a40);
}
.price-notes li { color: var(--clr-text-muted); line-height: 1.6; }

/* ---------- Low risk -------------------------------------
   The promises that were spread across /about, the process steps and the FAQ,
   collected immediately before the closing CTA. */

.risk-band { background: var(--clr-surface-1); border-block: 1px solid var(--border-subtle); padding-block: 72px; }
.risk-band-inner { display: grid; grid-template-columns: 1fr 1.15fr; gap: 56px; align-items: start; }
.risk-band-inner .section-intro { margin-bottom: 0; }
.risk-list li { color: var(--clr-text); font-size: 1.02rem; }

/* ---------- Case study (slot) ----------------------------
   Styled ahead of the first documented client story so publishing it is a
   copy edit. The markup is commented out in the template until then. */

.case-band { background: var(--clr-surface-1); border-block: 1px solid var(--border-subtle); padding-block: 80px; }
.case-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.case-step {
  background: var(--clr-bg);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 26px 24px;
}
.case-step p:not(.eyebrow) { color: var(--clr-text-muted); line-height: 1.65; margin: 0; }

/* Same narrow-screen guard as the sections above. */
.pricing-grid > *, .risk-band-inner > *, .case-grid > * { min-width: 0; }
.price-kind, .price-figure, .risk-list li { overflow-wrap: break-word; }

@media (max-width: 1000px) {
  .pricing-grid, .case-grid { grid-template-columns: 1fr 1fr; }
  .risk-band-inner { gap: 36px; }
}

@media (max-width: 768px) {
  .pricing-band { padding-block: 56px; }
  .pricing-grid, .case-grid { grid-template-columns: 1fr; gap: 14px; }
  .price-card { padding: 24px 20px; }
  .price-notes { margin-top: 28px; }
  .risk-band { padding-block: 48px; }
  .risk-band-inner { grid-template-columns: 1fr; gap: 26px; }
  .case-band { padding-block: 56px; }
}
