/*
 * The site's design, expressed entirely through the tokens the document carries.
 *
 * NO COLOUR, TYPE OR MEASURE IN THIS FILE IS A LITERAL. Every one of them is a
 * `var(--ccg-*)` whose value is emitted into the served page from ccg.yaml. That
 * is not tidiness: it is what lets a check read the design out of the response
 * instead of out of this file. A check that greps a stylesheet for `#181818`
 * confirms that a file contains what the same change wrote into it.
 *
 * The rule is not "no number appears here", and an earlier wording of it said so
 * and was false: `z-index: 10`, `min-width: 220px` and a handful of `gap` and
 * `flex` values are literals in this file. They are structural rather than
 * design - a dropdown that must not collapse below a usable width, a stacking
 * order - and none of them is a value anyone would look for in ccg.yaml. What
 * would break the property is a colour, a font or the content measure written
 * here, and none is.
 *
 * The fallbacks in the `var()` calls are deliberately absent for the same
 * reason. A missing token should render wrong and be noticed, not be quietly
 * papered over by a default nobody chose.
 *
 * RESPONSIVE BEHAVIOUR IS AT THE END OF THIS FILE, in four `@media` blocks -
 * three under the heading `the narrow bands`, two of those by width and one by
 * `(hover: none)` which carries no width at all, and then `the wide band` after
 * them - and it is the one part of this stylesheet that is not a reading of
 * anything. Everything above those blocks was measured at 1920 on the live site
 * and reproduced; the narrow view was decided for this site instead - "neu,
 * nicht uebernehmen" - so the original's own breakpoints were not looked at. The
 * decisions, the measurements they came from and the list of what the design
 * decides rather than measures are in
 * openspec/changes/design-the-mobile-view/design.md.
 *
 * The three band edges are the only three design numbers written into this file,
 * and each block says why the language leaves its own nowhere else to be. The
 * `(hover: none)` block carries no number: it asks what the pointer can do.
 *
 * THIS SAID `two` UNTIL 15.09.2026. `give-the-layout-a-wide-band` added the
 * third, `min-width: 1921px`, which is the only `min-width` query here and the
 * only declared departure from the transfer in this file: the live site strands
 * the same 1200 px column in a 3840 px window and this one does not. That
 * change's design.md carries the reason for the number and for the ceiling the
 * block ramps to.
 */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  padding: 0;
  background: var(--ccg-page);
  color: var(--ccg-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-size-body);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-body);
}

p { margin: 0 0 var(--ccg-p-margin-bottom); }

a { color: var(--ccg-link); text-decoration: none; }
a:hover, a:focus-visible { text-decoration: underline; }

/* The content column. `--ccg-measure` is its width and `--ccg-measure-gutter`
   the space it keeps on each side at narrower viewports, which is how the live
   site's `max-width: calc(100% - 60px)` behaves. */
.ccg-measure {
  width: var(--ccg-measure);
  max-width: calc(100% - (2 * var(--ccg-measure-gutter)));
  margin-left: auto;
  margin-right: auto;
}

/* --- headings ---------------------------------------------------------------
 *
 * h1 is Spartan; h2 and h3 are Inter. Three families for one site is the
 * original's, reproduced rather than normalised.
 *
 * h1's colour is the one recorded as unmeasured. The only h1 on the live site
 * sits in the hero and is white, and white applied to the element would render
 * an invisible heading on every light section - so the white belongs to the hero
 * rule below and this one carries a colour nobody measured, marked as such where
 * it is defined.
 */
h1 {
  margin: 0;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-h1-size);
  line-height: var(--ccg-h1-line);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-h1-tracking);
  color: var(--ccg-h1-color);
}

h2 {
  margin: 0 0 var(--ccg-h2-margin-bottom);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-h2-size);
  line-height: var(--ccg-h2-line);
  font-weight: var(--ccg-h2-weight);
  letter-spacing: var(--ccg-h2-tracking);
  color: var(--ccg-heading);
}

h3 {
  margin: 0 0 var(--ccg-h3-margin-bottom);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-h3-size);
  line-height: var(--ccg-h3-line);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-h3-tracking);
  color: var(--ccg-heading);
}

/* --- the header -------------------------------------------------------------
 *
 * TWO STATES, MEASURED SEPARATELY, AND THE SECOND ONE IS NOT A SMALLER VERSION
 * OF THE FIRST. At the top of the document the live header is `position:
 * absolute`, 70 px tall, transparent, white-lettered, with a white logo lying
 * over the hero photograph. Past scrollY 302 it is `position: fixed`, 72 px tall,
 * white, shadowed, navy-lettered, with a second logo file at 44 px. Both files
 * are in the markup and this shows one of them: swapping a `src` from a script
 * would leave a visitor whose browser runs none with whichever file the template
 * wrote, and that visitor is exactly the one the transparent state is right for.
 *
 * `data-ccg-sticky` is set by js/ccg.js and by nothing else, from the threshold
 * the header carries as an attribute. Without the script the header stays in its
 * first state, which is the state the top of the document is in anyway.
 */
/* The sentinel js/ccg.js inserts: an element as tall as the measured threshold,
   at the very top of the document, watched by an intersection observer. It paints
   nothing and takes no space in the flow. */
.ccg-sticky-sentinel {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  pointer-events: none;
  visibility: hidden;
}

.ccg-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10002;
  height: var(--ccg-header-height);
  background: transparent;
}

.ccg-header[data-ccg-sticky="on"] {
  position: fixed;
  height: var(--ccg-header-height-sticky);
  background: var(--ccg-header-sticky-bg);
  box-shadow: var(--ccg-header-sticky-shadow);
}

.ccg-header__inner {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  height: 100%;
}

.ccg-header__logo {
  display: flex;
  align-items: center;
  margin-right: auto;
  text-decoration: none;
}

.ccg-header__logo-image { display: block; width: auto; }
.ccg-header__logo-image--over { height: var(--ccg-header-logo-height); }
.ccg-header__logo-image--sticky { display: none; height: var(--ccg-header-logo-sticky-height); }

/* A PAGE WITH NO HERO GETS THE COLOURED MARK AT THE TOP OF THE DOCUMENT TOO, and
 * this rule exists because the article is the first page in this repository that
 * has no hero. The header is transparent and absolute over whatever is beneath
 * it; on the nine pages this site already served that is always a dark band, so
 * the white mark and the white lettering were the only state the top of a
 * document ever needed. An article opens on white, and without this the mark is
 * white on white - measured on the served page as `logo-weiss.png` at
 * `display: block` over a white ground.
 *
 * The navigation needed nothing: `.ccg--hero .ccg-nav__link--l1` already binds
 * the white to the hero case, so without the class the links paint their measured
 * navy. Only the mark is a file rather than a colour, and a file cannot be
 * recoloured by the rule that recolours the text beside it.
 *
 * DECLARED BEFORE THE STICKY PAIR, because both are 0-3-0 and the sticky state
 * has to win: once the bar is white and fixed, the coloured mark is what belongs
 * on it either way, and these two rules must not be able to put the white one
 * back. */
.ccg:not(.ccg--hero) .ccg-header__logo-image--over { display: none; }
.ccg:not(.ccg--hero) .ccg-header__logo-image--sticky { display: block; }

.ccg-header[data-ccg-sticky="on"] .ccg-header__logo-image--over { display: none; }
.ccg-header[data-ccg-sticky="on"] .ccg-header__logo-image--sticky { display: block; }

/* THE CONTROL THAT OPENS THE MENU AT A NARROW WIDTH, AND ITS BASE STATE, WHICH
 * IS THE HALF THAT WAS MISSING FOR AN HOUR. The narrow band's own block at the
 * end of this file shows it; without a rule here it kept a `button`'s own
 * `display: inline-block` at every width, which is not what the comment in
 * partials/header.html.twig said and not what design.md claimed either.
 *
 * WHAT IT COST, because it is a better argument than the rule: js/ccg.js asks
 * this element's computed `display` to decide which band the window is in, so a
 * button that is displayed at 1920 told the script the reference viewport was a
 * phone. The parallax handler returns at a narrow band, so it returned at 1920
 * as well and wrote nothing at all - three checks in test/motion.sh and
 * test/home.sh went red together, the plainest of them reading `not one of the 4
 * layers changed its background position at any scroll position`. The suite
 * found it; the two places that asserted it in prose did not.
 *
 * IT IS ALSO WHAT KEEPS THE BUTTON OUT OF THE FOCUS ORDER AT THE WIDE BAND. An
 * element that is not displayed is not tabbable, which is why this can be a
 * `display` and not a `visibility`. */
.ccg-header__toggle { display: none; }

.ccg-nav__list, .ccg-nav__sublist {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ccg-nav__item { position: relative; }

.ccg-nav__link {
  display: block;
  padding: var(--ccg-nav-padding);
  color: var(--ccg-nav-color);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-nav-size);
  font-weight: var(--ccg-nav-weight);
  line-height: var(--ccg-nav-line);
  text-decoration: none;
  white-space: nowrap;
}

a.ccg-nav__link:hover, a.ccg-nav__link:focus-visible {
  background: var(--ccg-nav-hover-bg);
  color: var(--ccg-nav-hover-color);
  text-decoration: none;
}

/* An entry whose page does not exist yet. It is not a link, it does not respond
   to a pointer, and it is dimmed - three signals rather than one, because the
   markup distinction (`data-nav-target`) is invisible to a person looking at the
   page.
 *
 * IT IS NOT WORN BY AN ENTRY THE ORIGINAL GIVES NO TARGET, and the class is on
 * the element only where partials/header.html.twig's third branch puts it. Both
 * of this menu's `label_only` entries wore it until 2026-09-02 and were grey on
 * a menu whose original greys neither: measured that day at 1920 on
 * https://www.consulting-concept.de/, `Unsere Consulting Leistungen` is
 * rgb(255,255,255) and `Karriere` is rgb(24,24,24), each the colour of the
 * linked entries beside it. Dropping the class is what gives them their
 * siblings' colour here, because every colour those siblings take -
 * `.ccg--hero .ccg-nav__link--l1`, the sticky pair, `.ccg-nav__link--l2` - is
 * written for the level and not for the element. */
.ccg-nav__link--missing {
  color: var(--ccg-nav-missing-color);
  cursor: default;
}

/* --- the sub-menus ----------------------------------------------------------
 *
 * THE PANEL, at the original's own measurements. Read on 2026-09-02 at 1920 from
 * `ul#menu-custom-menu` at https://www.consulting-concept.de/, each parent
 * hovered with a real pointer event before its panel was read, so these are used
 * values off a laid-out box:
 *
 *     nifty/style.css  .btMenuHorizontal .menuPort > nav > ul ul
 *                      position: absolute; width: 240px; padding: 10px 0;
 *
 * and both live panels measure 240 wide at y = 52.5. This one was `width: auto`
 * over `min-width: 220px`, which laid out at 220, and carried no padding, which
 * put its first entry 10 px above the original's.
 *
 * `top: 100%` IS KEPT AND IS NOT THE SAME THING AS THE ORIGINAL'S `52.5px`. The
 * live panel is at y = 52.5 because its parent `li` is 70 tall from y = 0; this
 * one is at y = 52.5 because its parent is 35 tall from y = 17.5. The used
 * values already agree to the pixel and writing 52.5 here would move the panel
 * 17.5 px down. */
.ccg-nav__sublist {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  flex-direction: column;
  /* STRETCH, SO AN ENTRY IS AS WIDE AS THE PANEL IT SITS IN. `flex-start` made
     each `li` as wide as its own text, which is two differences from the
     original at once: its entries are the panel's full 240 - measured
     2026-09-02, `a` at 240 x 37.5 with `padding: 7.5px 18.75px` - and the third
     level's `left: 100%` resolves against its parent `li`, so a shrunk `li` put
     the third panel 147 px inside the second one instead of flush beside it. */
  align-items: stretch;
  background: var(--ccg-page);
  width: 240px;
  padding: 10px 0;
}

.ccg-nav__item--has-children:hover > .ccg-nav__sublist,
.ccg-nav__item--has-children:focus-within > .ccg-nav__sublist { display: flex; }

/* THE PANEL OF THE SECOND-TO-LAST TOP-LEVEL ITEM SITS ONE PARENT WIDTH TO THE
 * LEFT, which is the original's rule and not a number read off it. Measured:
 * the live panel under `Über uns` computes `left: -111.031px` against a parent
 * `li` of exactly that width, and nifty/style.css line 2214 says why, under its
 * own heading `Last menu item (FIX)`:
 *
 *     .btMenuRight.btMenuHorizontal:not(.btMenuLeft) .menuPort > nav > ul >
 *       li:nth-last-child(2):not(.btMenuWideDropdown) > ul { right: 0; left: -100%; }
 *
 * Reproduced as the positional rule it is. Our own `Über uns` is 101 wide rather
 * than 111.031, so the pixel value would have been wrong by 10 px on a menu set
 * in a different face; `-100%` resolves against the same containing block on
 * both sides and is therefore the thing that carries over. */
.ccg-nav__list > .ccg-nav__item:nth-last-child(2) > .ccg-nav__sublist { left: -100%; }

/* THE THIRD LEVEL OPENS BESIDE ITS PARENT, NOT BELOW IT. The original's rules,
 * from the same sheet:
 *
 *     .btMenuHorizontal .menuPort > nav > ul ul ul { top: -10px; }
 *     .btMenuHorizontal .menuPort ul ul ul         { left: 240px; right: auto; }
 *
 * and the measurement they produce: `Karriere`'s `li` is at y = 100 and the
 * panel it opens is at y = 90, x = parent + 240, so its first entry lands on
 * exactly the baseline of the entry it belongs to. `-10px` is the panel's own
 * `padding: 10px 0` taken back off, which is why it is written that way here as
 * well rather than as a nought - the padding above is the same 10.
 *
 * `left: 100%` rather than the original's `240px`: the two are the same number
 * on a 240-wide panel, and written as a percentage the offset cannot drift away
 * from the width three rules up. */
.ccg-nav__sublist .ccg-nav__sublist {
  top: -10px;
  left: 100%;
}

/* AND BELOW 1465 IT OPENS THE OTHER WAY, WHICH IS A DEPARTURE AND NOT THE
 * ORIGINAL'S RULE. Measured 21.09.2026 with the panels forced visible so both
 * sides are read in the same state:
 *
 *     width   ours: document / window        original: third panel, right edge
 *     1025    1128 / 1025                    (its menu is already the mobile one)
 *     1180    1283 / 1180                    (the same)
 *     1280    1373 / 1280
 *     1360    1413 / 1360
 *     1440    1453 / 1440                    1193 + 240 = 1433, inside 1440
 *     1460    1463 / 1460
 *     1466    1466 / 1466  - fits
 *
 * So ours offers a horizontal scrollbar from 1025 to about 1464 and the original
 * never does. WHY THEY DIFFER IS NOT THIS RULE: at 1440 the original's
 * second-to-last top-level item sits at x 1064 and ours at x 1213, so our menu
 * as a whole stands about 149 px further right, and the third panel runs out of
 * window that much sooner. Reproducing the original's placement is a change to
 * the header at every width, and this change does not own it.
 *
 * AN EARLIER VERSION OF THIS RULE CARRIED NO MEDIA QUERY and was wrong. It was
 * written on a reading taken at 1180, where the original's third panel measures
 * at x -290 - which looked like the original flipping it leftwards and is in
 * fact its whole menu off-canvas, because at 1180 the original is already
 * serving its mobile menu and ours is not. Unconditional, the rule moved the
 * panel at 1920 as well, and `case_menu` failed on ten routes at once with the
 * measurement that says so: the original opens it flush past its parent at
 * x 1453.1 and ours opened it at 973.1. Two constructions compared in one
 * number, which is the mistake this file keeps a name for.
 *
 * 1465 IS MEASURED, NOT DERIVED. It is where the panel stops crossing the
 * window's right edge: 1463 in a 1460 window, 1466 in a 1466 window. Above it
 * nothing changes and the panel opens where the original opens it. */
@media (max-width: 1464px) {
  .ccg-nav__list > .ccg-nav__item:nth-last-child(2) .ccg-nav__sublist .ccg-nav__sublist {
    left: auto;
    right: 100%;
  }
}
/* EVERY LEVEL INSIDE A PANEL, NOT ONLY THE SECOND. The original's third level is
   rgb(24,24,24), the same as its second - measured 2026-09-02 - and a rule
   written for `--l2` alone left the two entries under `Karriere` painting the
   top-level navy on a white panel. */
.ccg-nav__link--l2, .ccg-nav__link--l3 { color: var(--ccg-nav-child-color); }
.ccg-nav__link--l2.ccg-nav__link--missing,
.ccg-nav__link--l3.ccg-nav__link--missing { color: var(--ccg-nav-missing-color); }

/* --- the bar that marks the current branch -----------------------------------
 *
 * THE ORIGINAL MARKS THE CURRENT PAGE AND THIS THEME DID NOT, and the reading
 * that said otherwise looked at the link's colour, weight, text-decoration and
 * border - all four of which really are identical to the siblings'. The mark is
 * on `a::after`, which that reading never asked for. Re-measured on five live
 * pages at 1920 on 26 August 2026 and recorded in ccg.yaml beside the values.
 *
 * EVERY TOP-LEVEL ITEM CARRIES THE BAR; THE OPACITY IS THE MARK. The unmarked
 * ones sit at 0, which is what makes the mark a change of one property rather
 * than the appearance of an element - and is why the transition below is on the
 * bar and not on the state.
 *
 * THE `--l1` SCOPE IS THE MEASUREMENT, not tidiness. The live sub-menu entries
 * carry `content: none` on their `::after`; only the top level has a bar, and
 * the item marked on /single-service/ is the top-level parent rather than the
 * sub-menu entry that is the page itself.
 *
 * IT IS ON THE LINK AND THE SPAN ALIKE. The live parent item "Unsere Consulting
 * Leistungen" is an anchor with no href and is the marked item on all five
 * service pages; here that entry has no page to point at and is rendered as the
 * `--missing` span. A rule scoped to `a` would leave five of eight pages
 * unmarked.
 */
.ccg-nav__link--l1 { position: relative; }

.ccg-nav__link--l1::after {
  content: "";
  display: block;
  position: absolute;
  left: var(--ccg-nav-mark-inset);
  right: var(--ccg-nav-mark-inset);
  bottom: 0;
  height: var(--ccg-nav-mark-height);
  pointer-events: none;
  opacity: 0;
  background: var(--ccg-nav-mark-color);
  transform: scale(var(--ccg-nav-mark-scale));
  transform-origin: 50% 50%;
  transition: var(--ccg-t-all);
}

.ccg-nav__item--current > .ccg-nav__link--l1::after {
  opacity: var(--ccg-nav-mark-opacity);
}

/* THE TWO STATES, AND THE SECOND IS THE ONE ASSUMING WOULD HAVE GOT WRONG. Over
 * the hero the marked bar is white; on the white sticky header it is the blue,
 * not white on white. The live rule reaches the first through
 * `background-color: currentColor` on a header whose links are white.
 *
 * THIS THEME WRITES THE COLOUR INSTEAD OF INHERITING IT, and that is a departure
 * from the mechanism taken to keep the measured result. `currentColor` here
 * would resolve to `--ccg-nav-missing-hero-color` on the five service pages,
 * because their marked item is the entry with no page behind it and this theme
 * dims those - a grey bar the original does not have. The measured value is
 * white, so white is what is written.
 *
 * The sticky rule wins by specificity (0-4-1 against 0-3-1), the same way the
 * link colours two blocks down do, rather than by where it sits in the file.
 */
.ccg--hero .ccg-nav__item--current > .ccg-nav__link--l1::after {
  background: var(--ccg-nav-mark-hero-color);
}

.ccg-header[data-ccg-sticky="on"] .ccg-nav__item--current > .ccg-nav__link--l1::after {
  background: var(--ccg-nav-mark-color);
}

/* The two glyphs at the right of the header: 15px apart, 16px tall, taking the
   navigation's colour. Drawn as SVG rather than set in an icon font - the
   departure is recorded in the readme and in the template that holds them. */
.ccg-header__social {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ccg-header__social-item { margin-left: var(--ccg-header-social-gap); }

.ccg-header__social-link {
  display: flex;
  align-items: center;
  height: var(--ccg-header-social-size);
  color: var(--ccg-nav-color);
  text-decoration: none;
}

.ccg-header__glyph { display: block; }
.ccg-header__glyph--facebook, .ccg-header__glyph--linkedin { height: var(--ccg-header-social-size); }

/* --- the hero ---------------------------------------------------------------
 *
 * Where the white h1 belongs. The dark ground and the white heading are what
 * belongs to the document rather than to one page; the photograph, the mesh and
 * the constellation belong to the home page's hero and are below.
 */
.ccg-hero {
  background: var(--ccg-hero-bg);
  color: var(--ccg-hero-heading);
  padding-top: var(--ccg-main-padding-top);
  padding-bottom: var(--ccg-main-padding-bottom);
}

.ccg-hero h1, .ccg-hero__title,
.ccg-home-hero h1, .ccg-home-hero__title { color: var(--ccg-hero-heading); }

/* THE NAVIGATION'S HALF OF THE SAME BINDING. The h1 had one from the start and
 * the navigation did not, which is how a repair that renamed the measured white
 * to `--ccg-nav-hero-color` left it consumed by nothing: over the hero the links
 * then painted navy on near-black. The header sits outside `.ccg-hero` - it
 * overlays it - so the page says which case it is on the body element.
 *
 * SCOPED TO THE TOP LEVEL, AND THAT IS NOT TIDINESS. Written as
 * `.ccg--hero .ccg-nav__link` this rule is 0-2-0 and declared last, so it beat
 * `.ccg-nav__link--l2` (0-1-0) and `.ccg-nav__link--missing` (0-1-0). Every
 * sub-menu entry then painted white on a white panel.
 *
 * AND IT STOPS APPLYING ONCE THE HEADER IS STICKY, which is the third appearance
 * of the same defect and the one this cycle would have shipped: the sticky header
 * is a white bar, and white lettering on it is the same white-on-white. The
 * attribute is on the header, so the sticky rules are written from it and win by
 * being more specific than the body-scoped ones.
 */
.ccg--hero .ccg-nav__link--l1,
.ccg--hero .ccg-header__logo,
.ccg--hero .ccg-header__social-link { color: var(--ccg-nav-hero-color); }
.ccg--hero .ccg-nav__link--l1.ccg-nav__link--missing { color: var(--ccg-nav-missing-hero-color); }

.ccg-header[data-ccg-sticky="on"] .ccg-nav__link--l1,
.ccg-header[data-ccg-sticky="on"] .ccg-header__social-link { color: var(--ccg-nav-color); }
.ccg-header[data-ccg-sticky="on"] .ccg-nav__link--l1.ccg-nav__link--missing { color: var(--ccg-nav-missing-color); }

.ccg-hero__subtitle { color: var(--ccg-hero-heading); }

/* --- the content ------------------------------------------------------------ */

.ccg-main { padding-bottom: var(--ccg-main-padding-bottom); }

/* Only when there is no hero: with one, the hero supplies the top spacing and
   the header overlays it. */
.ccg-main > .ccg-measure:first-child { padding-top: var(--ccg-main-padding-top); }

.ccg-article__title { margin-bottom: var(--ccg-h2-margin-bottom); }

.ccg-article ul, .ccg-article ol { margin: 0 0 var(--ccg-p-margin-bottom); padding-left: 20px; }

/* --- the footer -------------------------------------------------------------
 *
 * TWO SECTIONS, AND THE FIRST ONE IS 200 PX OF NOTHING. That is the live site's
 * composition, read from `div.bt-site-footer` at a window width of 1920 px: a
 * spacing section carrying no text, no link and no image, then a content section
 * of one row of four columns 310 px wide. Both stand on the same ground; the
 * wrapper around them is transparent, which is why the colour is on the parts
 * rather than on `.ccg-footer`.
 *
 * THE 310 IS NOT WRITTEN ANYWHERE. It is what the live mechanism produces and it
 * is reproduced rather than asserted: a 1200 px box, a holder pulled out to 1240
 * by a negative margin of one gutter on each side, four items at `1 1 25%` of
 * that, each giving back the same gutter as padding. 1240 / 4 = 310, and the
 * content inside it is 270. Writing `width: 310px` instead would be a number
 * that happens to agree at one viewport.
 *
 * Every measure, colour and type metric below is a `--ccg-footer-*` token from
 * ccg.yaml, with four deliberate exceptions: `--ccg-font-display` on the column
 * titles, `--ccg-nav-missing-color` on an entry whose route no page answers, and
 * `--ccg-t-all` and `--ccg-t-footersocial` on the two transitions. The first two
 * are site-wide values the footer shares rather than owns; the transitions are
 * kept out on purpose, for the reason design.md gives under the motion tokens.
 * The geometry - flex, floats-turned-flex, `border-radius: 50%`, `box-sizing` -
 * is written out, which is the same split the top of this file states.
 *
 * The live site produces every vertical gap with an empty `div.bt_bb_separator`
 * whose only effect is a margin. Those separators are not reproduced as
 * elements: the margins they carry are put on the elements they separated, and
 * the measured values are unchanged. The deviation is named in
 * openspec/changes/rebuild-the-footer/design.md.
 */

.ccg-footer {
  color: var(--ccg-footer-text);
  font-size: var(--ccg-footer-size);
  line-height: var(--ccg-footer-line);
}

/* 200 px of deliberate emptiness. It has no content and must not acquire any -
   the specification checks that it holds no text, no link and no image. */
.ccg-footer__spacer {
  height: var(--ccg-footer-spacer-height);
  background: var(--ccg-footer-bg);
}

.ccg-footer__content { background: var(--ccg-footer-bg); }

.ccg-footer__row {
  display: flex;
  flex-wrap: wrap;
  margin-left: calc(-1 * var(--ccg-footer-column-gutter));
  margin-right: calc(-1 * var(--ccg-footer-column-gutter));
}

/* AN EQUAL SHARE OF THE ROW, WHATEVER THE ROW HOLDS, AND IT WAS A QUARTER EACH.
   The live footer lays four columns out and a quarter was the right number for
   as long as this one did too. OP#4618 withdrew the `Weitere Leistungen` column
   with the services behind it, and a quarter each then left the three remaining
   columns on their old x positions with 310 px of empty rail on the right - the
   footer visibly off-centre, which is what a fixed share does when the count
   changes.

   `flex: 1 1 0` with no `max-width` is the same rule stated as a rule rather
   than as a number: the columns divide the row evenly however many there are,
   which is a quarter each at four and a third each at three. Measured at 1920
   after the change: three columns of 413.3 at x 340, 753.3 and 1166.7, filling
   the 1240 px rail the way four of 310 did.

   THE NARROW BANDS ARE UNAFFECTED. Both media queries below restate `flex` and
   `max-width` for their own counts - two across, then one - and they come later
   in this file at the same specificity, so they win where they apply exactly as
   they did before. */
.ccg-footer__column {
  flex: 1 1 0;
  padding-left: var(--ccg-footer-column-gutter);
  padding-right: var(--ccg-footer-column-gutter);
}

/* --- column 1: the logo and the three contact lines --- */

/* No height: 1488 / 566 is 2.629, so 270 px of width is 102.70 px of height on
   its own. A written height would have to be either that fraction or a 0.3 px
   squash, and the aspect does the work of both. */
.ccg-footer__logo {
  display: block;
  width: var(--ccg-footer-logo-width);
  height: auto;
  margin-bottom: var(--ccg-footer-logo-gap);
}

.ccg-footer__contact-line {
  display: flex;
  align-items: center;
  margin: 0 0 var(--ccg-footer-contact-row-gap);
  font-size: var(--ccg-footer-contact-size);
  line-height: var(--ccg-footer-contact-line);
}

.ccg-footer__contact-line:last-child { margin-bottom: 0; }

.ccg-footer__contact-icon {
  flex: none;
  width: var(--ccg-footer-contact-icon-width);
  height: var(--ccg-footer-contact-icon-height);
}

.ccg-footer__contact-text { padding-left: var(--ccg-footer-contact-icon-gap); }

/* --- the column headings and the vertical menus --- */

/* Spartan at text size, not the Inter that h3 carries - the live footer's
   headings are the display face. There is no element rule for `h4` in this
   file, so this rule is the whole of it. */
.ccg-footer__column-title {
  margin: 0 0 var(--ccg-footer-heading-gap);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-footer-heading-size);
  line-height: var(--ccg-footer-heading-line);
  font-weight: var(--ccg-footer-heading-weight);
  letter-spacing: var(--ccg-footer-heading-tracking);
  color: var(--ccg-footer-text);
}

.ccg-footer__list { margin: 0; padding: 0; list-style: none; }

/* One entry is a 16 px line box with 9.6 px under it: 25.6 px, and five of them
   are the 128 px the second column's menu measures. The last entry keeps its
   9.6 px - dropping it would make the menu 25.6 x 5 minus a bit. */
.ccg-footer__item {
  line-height: var(--ccg-footer-link-line);
  padding-bottom: var(--ccg-footer-link-gap);
}

/* The nested entry. Not indented on the live site - the sub-menu starts at the
   same x as its parent - so it reads flat while being spaced as a child: the
   parent's line box, then this list, then the parent's 9.6 px. */
.ccg-footer__list--sub { margin: 0; padding: 0; }

/* `display: inline` and not `inline-block`: the live link is inline, so its
   18.5 px box overhangs the 16 px line box it sits in, which is why a link's
   top reads a pixel above its item's. */
.ccg-footer__link {
  display: inline;
  color: var(--ccg-footer-text);
  font-size: var(--ccg-footer-link-size);
  line-height: var(--ccg-footer-link-size);
  text-decoration: none;
  /* Measured `all` / `0.2s` / `ease` on the live link, which is `t-all` exactly,
     so the footer gets no transition token of its own for this. */
  transition: var(--ccg-t-all);
}

/* The live hover fades the link and does not underline it, so the document-wide
   `a:hover { text-decoration: underline }` is turned off here rather than
   inherited. */
a.ccg-footer__link:hover, a.ccg-footer__link:focus-visible {
  opacity: var(--ccg-footer-link-hover-opacity);
  text-decoration: none;
}

.ccg-footer__link--missing { color: var(--ccg-nav-missing-color); cursor: default; }

/* --- column 4: the social marks, the claim, the legal menu --- */

/* 24 px under this heading rather than the 25 the other two carry. Both were
   measured; they differ because the live markup separates them with different
   separators. */
.ccg-footer__column[data-ccg-footer-column="4"] .ccg-footer__column-title {
  margin-bottom: var(--ccg-footer-social-lead);
}

/* The live marks are floated with 11.2 px of right padding, which puts their
   left edges 51.2 px apart. A flex row with a gap of the same value puts them in
   the same two places and sizes its own height, which a float row does not. */
.ccg-footer__social {
  display: flex;
  gap: var(--ccg-footer-social-gap);
  margin: 0 0 var(--ccg-footer-social-trail);
  padding: 0;
  list-style: none;
}

.ccg-footer__social-link {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--ccg-footer-social-size);
  height: var(--ccg-footer-social-size);
  border: var(--ccg-footer-social-border) solid var(--ccg-footer-social-color);
  border-radius: 50%;
  color: var(--ccg-footer-social-color);
  text-decoration: none;
  transition: var(--ccg-t-footersocial);
}

/* The live hover fills the circle by growing an INSET box-shadow to a spread
   larger than the circle's radius, and turns the glyph white. Nothing moves and
   nothing scales - `transform` is `none` in both states. The spread is the
   glyph's own size on the live site, so it is that token here too. */
.ccg-footer__social-link:hover, .ccg-footer__social-link:focus-visible {
  box-shadow: inset 0 0 0 var(--ccg-footer-social-glyph-size) var(--ccg-footer-social-color);
  color: var(--ccg-footer-social-hover-color);
  text-decoration: none;
}

.ccg-footer__social-glyph {
  display: block;
  height: var(--ccg-footer-social-glyph-size);
  width: auto;
}

/* Two metrics, and the outer one wins: the `small` sets 14/19.6, the paragraph
   around it sets a 27.2 px line box, and 27.2 is the larger - which is why the
   claim occupies 54.4 px over its two lines rather than 39.2. */
.ccg-footer__claim {
  margin: 0 0 var(--ccg-footer-block-gap);
  font-size: var(--ccg-footer-size);
  line-height: var(--ccg-footer-claim-box-line);
}

.ccg-footer__claim small {
  font-size: var(--ccg-footer-claim-size);
  line-height: var(--ccg-footer-claim-line);
}

/* Nothing separates two entries but the 4 px of padding on each side of them -
   no bullet, no rule, no pipe. It wraps onto four lines inside the 270 px
   column, and the wrap is arithmetic rather than a breakpoint: it wraps at 1920
   px too, because the column and not the window is what is 270 px wide. */
.ccg-footer__list--legal { display: flex; flex-wrap: wrap; }

.ccg-footer__legal-item {
  padding: 0 var(--ccg-footer-legal-pad);
  line-height: var(--ccg-footer-legal-line);
}

/* --- the home page ----------------------------------------------------------
 *
 * FIVE SECTIONS AND SIX ROWS. The live page's services section carries two rows -
 * the five cards and the industries network - and the network is not a section.
 * The team section between Zukunft and News computes to `display: none` at every
 * breakpoint with a height of nought, so it is not built at all.
 *
 * NO COLOUR, FONT, TYPE METRIC, GRADIENT, SHADOW OR FILTER IS A LITERAL BELOW.
 * Each is a `var(--ccg-*)` emitted into the served page from ccg.yaml, so a check
 * reads the design out of the response rather than out of this file. What IS
 * written out is geometry - a grid, an inset, a radius, a stacking order, the
 * size of a decorative bar - and that is the same split the top of this file
 * states: a value a reader would look for in ccg.yaml comes from a token, and
 * structure does not.
 *
 * Whole shadows and whole filters are single tokens for the reason the whole
 * transitions already are: reassembled from a colour token and three lengths,
 * they are values nobody measured.
 */

.ccg--modular .ccg-main { padding-bottom: 0; }

.ccg-home {
  position: relative;
  padding-top: var(--ccg-section-space);
  padding-bottom: var(--ccg-section-space);
  overflow: hidden;
}

/* The decorative shapes the live sections carry: waves at the edges, dot fields
   behind the content. They are content images rather than theme assets - they
   belong to the page and are replaced in the panel - so the template supplies
   the source and this only places them at the height each renders on the live
   page. The heights are the files' own at 1920: every one of the five is
   width-driven, and the token pins the measurement rather than trusting that. */
.ccg-home__wave {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  pointer-events: none;
  z-index: 1;
}

/* NOT FLIPPED, AND THAT WAS THE DEFECT. This carried `transform: scaleY(-1)`,
   which turned the bottom wave of another section into a top one. The live top
   wave is its own file - `top_wave_01.png`, 1945x430 - and this repository has
   held it all along. */
.ccg-home__wave--top { top: 0; bottom: auto; }

.ccg-home__wave--hero { height: var(--ccg-wave-hero); }
.ccg-home__wave--leistungen { height: var(--ccg-wave-loesungen); }
.ccg-home__wave--zukunft-top { height: var(--ccg-wave-zukunft-top); }
.ccg-home__wave--zukunft { height: var(--ccg-wave-zukunft); }
.ccg-home__wave--news { height: var(--ccg-wave-news); }

.ccg-home > .ccg-measure { position: relative; z-index: 2; }

/* --- THE HAIRLINE AT A FULL-BLEED BOUNDARY ---------------------------------
 *
 * WHAT IT IS. At a browser zoom that is not a whole multiple of the device's own
 * pixels - 80 % on a 2x display, which is `deviceScaleFactor: 1.6` - a grey line
 * one device pixel high is drawn across the whole window where one full-bleed
 * section meets the next. Measured on 14.09.2026 against the released demo
 * (2.0.23) at 1890 x 900:
 *
 *   /        css y 1027.5   row 152,151,152 between two rows of 247,247,245
 *   /        css y 3818.75  row 197,188,214 between two rows of 255,255,255
 *   /        css y 5132.5   row 200,194,213 between two rows of 255,255,255
 *   /about   css y 3032.5   row 174,177,183 under 247,247,245
 *   /about   css y 6303.75  row 219,220,222 between two rows of 247,247,245
 *
 * WHERE IT COMES FROM, AND IT IS NOT THE FRACTIONAL BOUNDARY THE TICKET NAMES.
 * Blink snaps a section's `overflow: hidden` clip to a whole CSS pixel and Skia
 * then maps that whole number into device space. The clip lands between two
 * device rows whenever `round(bottom in CSS px) x deviceScaleFactor` is not an
 * integer, and Skia draws a clip that lands between two rows ANTIALIASED. Every
 * layer inside the section - the wave, the darkened photograph under it, the mesh
 * - is then rasterised into that last row at partial coverage, while the section's
 * own background box and the next section's are snapped and land on whole rows.
 * What shows through the shortfall is the dark ground the wave is there to cover.
 *
 * The hero's bottom is at a WHOLE css pixel, 1028, and the line is there anyway.
 * Swept on the demo at 1890 x 900, one crop at that boundary per scale:
 *
 *   dsf   1028 x dsf   line
 *   1.0   1028.0       none        1.5   1542.0      none
 *   1.1   1130.8       151,151,153 1.6   1644.8      152,151,154
 *   1.25  1285.0       none        1.75  1799.0      none
 *   2.0   2056.0       none
 *
 * TWO CELLS OF THAT TABLE WERE WRONG HERE UNTIL 14.09.2026 and are corrected
 * above rather than left: 1028 x 1.0 is 1028.0 and not 1644.0, which is 1.6's
 * value written twice; and the two rows that carry a line read 151,151,153 at
 * 1.1 and 152,151,154 at 1.6, which is design.md S 2.1's crop table. The row
 * scan at the head of this comment reads the SAME row on `/` at 152,151,152 -
 * a whole-page row detector against a per-scale crop, two instruments, and the
 * two readings differ by 2 in the blue channel. Neither was re-measured for
 * this correction; what is fixed is that one table was printed twice with
 * different numbers.
 *
 * Seven for seven on "the device position is not whole", nought for seven on any
 * reading of the CSS position. A sweep of 1.0, 0.9, 0.8, 0.75, 0.67 and 2.0 -
 * which is what this change first drove - cannot see it: a fractional scale
 * ABOVE 1 was never asked for, and the leak is the section's own dark ground
 * rather than the page behind it, so a magenta page background never reaches the
 * viewer and a probe that looks for a GAP reports nothing.
 *
 * ISOLATED BY HIDING ONE ELEMENT AT A TIME AT dsf 1.6, same crop, same page:
 * hiding the wave removes the line; hiding the photograph, the particles or the
 * shade only lightens it; `overflow: visible` on the section removes it;
 * `overflow: clip` does not, and neither does `will-change: transform`,
 * `isolation: isolate` or `contain: paint` on their own. A plain DIV of
 * `#f7f7f5` put in the wave's place, with no image anywhere, draws the same line
 * at 209,209,210 - so it is the clip and not the image.
 *
 * THE REPAIR, IN TWO MOVES, AND NOTHING PAINTED MOVES.
 *
 * 1. The section below a boundary paints ONE PIXEL of the ground above it, with
 *    `box-shadow: 0 -1px 0`. An outer box-shadow is outside the border box, so no
 *    geometry changes at all, and it is painted by the element rather than by a
 *    descendant, so it is snapped rather than antialiased and covers the whole
 *    row. The colour is the colour of the row above the boundary, read off the
 *    page: 247,247,245 under the hero and under both of /about's quote sections,
 *    255,255,255 under the two waves that end in white, written as
 *    `var(--ccg-section-bg)` and `var(--ccg-page)` rather than as literals,
 *    because test/composition.sh asks every colour in this file to come from a
 *    token and the two tokens already hold exactly those values. At every whole device
 *    scale that pixel already holds exactly that colour, so the picture at
 *    1920 x 1080, scale 1, is unchanged.
 *
 * 2. `isolation: isolate` on the home sections, so that a section's absolutely
 *    positioned layers - the photograph at `z-index: 0` is one - cannot paint
 *    over the pixel the next section has just laid down. `.ccg-sv` has been a
 *    stacking context since it was written (`position: relative; z-index: 2`),
 *    which is why /about needs only the first move. A stacking context has a
 *    price of its own and it was measured rather than waved past: Blink
 *    composites the section as a group, and text over a backdrop it can no longer
 *    prove opaque gives up subpixel antialiasing for grayscale. 2,424 pixels of
 *    glyph and image edge at 1920 x 1080 at scale 1, worst 38 of the 765 a pixel
 *    can differ by, the biggest band being the archive button. The background
 *    colours below pull the other way - an opaque ground is what lets text keep
 *    its subpixel edges, and putting `.ccg-home-news` back to `transparent` grows
 *    that band from 1,719 pixels to 14,147. design.md S 2.3.
 *
 * AND ONE MORE WHERE THE WAVE IS AT THE TOP. `.ccg-home-zukunft` carries a top
 * wave, and there the leaking row is the section's OWN first device row: nothing
 * is painted after it that could cover it. What leaks is the mesh, the scrim and
 * the photograph BEHIND the wave - three layers, which is how many the rule
 * moves - because the wave's own edge sits on the clip edge and is antialiased
 * twice while they are antialiased once. They start one pixel
 * lower instead. The pixel they give up is one no visitor has ever seen,
 * because the wave covers it: read off the file on 14.09.2026, `welle-oben.png`
 * is 1945 x 430 and its row 0 is 255,255,255 at alpha 255 across 1,943 of its
 * 1,945 columns - the two exceptions being the outermost column on each side,
 * at alpha 245 - and the image stays fully opaque to that tolerance down to
 * row 46, the first interior pixel below full alpha being in row 47. This
 * comment read "opaque white over its first 424 px" until that measurement was
 * taken; it is opaque for 47 rows and pure white for 5.
 *
 * AND THAT MOVE IS NOT FREE, WHICH THIS COMMENT SAID NOTHING ABOUT UNTIL IT WAS
 * MEASURED. Those layers are `inset: 0`; a top of 1 px makes their box one pixel
 * shorter, and the photograph and the mesh are sized to COVER that box, so they
 * are resampled rather than merely moved. At 1920 x 1080 at device scale 1,
 * 517,309 pixels of the `.ccg-home-zukunft` band change - 3.7 % of the page -
 * none of them by more than 11 of the 765 a pixel can differ by, which is under
 * what a screen shows and over nought. The arithmetic and the instrument are in
 * this change's design.md, S 2.3.
 *
 * AND A THIRD FORM ON /blog, WHICH IS WRITTEN WHERE `.ccg-bl-head` IS. That
 * section has no full-bleed neighbour below it to lay a pixel down from, and it
 * needs no vertical clip of its own, so it takes `overflow-x: clip` with
 * `overflow-y: visible` instead. The reason is beside the rule.
 *
 * NOT REPAIRED HERE, and both are written into this change's design.md with the
 * measurement: /mergers-acquisitions at css y 945.625, where the row above the
 * boundary is the wave's own picture and not a flat colour, so a one-pixel cover
 * would replace a graphic; and /recruiting at css y 4521.875, where the two
 * grounds either side are the same and the line is 43,32,136 between 25,14,124
 * above and 22,11,124 below - the weakest of the eight and the only one whose
 * cover would change a pixel at scale 1. Those two figures read 945.8 and
 * 45,34,137 here until 14.09.2026 and neither was a measurement: 945.8 is the
 * `.ccg-lt-hero__port` band height further down this file, and 45,34,137 is a
 * row nothing recorded. design.md S 2.1's reproduction table is the reading
 * all three documents now carry.
 */
.ccg-home { isolation: isolate; }

.ccg-home-warum { box-shadow: 0 -1px 0 var(--ccg-section-bg); }
.ccg-home-zukunft { box-shadow: 0 -1px 0 var(--ccg-page); }
.ccg-home-news { box-shadow: 0 -1px 0 var(--ccg-page); }

.ccg-ab-mission-section,
.ccg-ab-close-section { box-shadow: 0 -1px 0 var(--ccg-section-bg); }

/* AND THE TWO SECTIONS WITH NO GROUND OF THEIR OWN GET THE ONE THEY ARE ALREADY
   STANDING ON, BECAUSE ONE PIXEL IS NOT ALWAYS ENOUGH.
   The antialiased row is the device row the boundary falls INSIDE, so where the
   boundary's device position is B, that row is floor(B). The one-pixel shadow
   above covers the rows from round(B - scale) to round(B), which reaches floor(B)
   only when the fraction is a half or more; where it is less than a half the row
   is reached by the next section's own background box instead, which starts at
   round(B). The two together cover it at every scale - but only if the section
   below HAS a background. Measured on this branch before this rule was added:
   `/` and /about are clean at 1.0, 1.5, 1.6, 1.75 and 2.0 and still carry a line
   at 1.1 and 1.25, at exactly the two boundaries whose lower section is
   transparent - `.ccg-home-news` at css y 5133.047 and `.ccg-ab-mission-section`
   at 3032.828. 1.1 x 5133.047 is 5646.35 and 1.6 x 5133.047 is 8212.88: .35
   against .88, which is the halves.
   THOSE TWO POSITIONS ARE THIS BRANCH'S AND NOT THE DEMO'S, which is why they
   are not the 5132.500 and 3032.500 design.md S 2.1 records for the same two
   boundaries: S 2.1 reads the released 2.0.23 demo and these were read on a
   container built from this branch. Half a pixel of content above the boundary
   is the whole of the difference and neither figure is a correction of the
   other; both are labelled here because an unlabelled pair reads as a
   contradiction.
   `--ccg-page` IS WHAT IS BEHIND THEM ALREADY: `body` computes
   `rgb(255, 255, 255)` and `html` and `.ccg-main` are both transparent, so this
   paints the colour that was showing through in the first place and the picture
   at scale 1 is the same picture. `.ccg-home-news` keeps its dot field, which is
   a background IMAGE; this is the colour under it. And the colour is right for
   the row as well as for the section: where the background reaches the row the
   boundary sits in its upper half, so most of the row belongs to the section
   below and the section below's ground is what should be in it.
   `.ccg-home-news` takes the same declaration, and it is written beside that
   section rather than here, because the rule that makes it transparent stands
   further down this file at the same specificity and would otherwise win. */
.ccg-ab-mission-section { background-color: var(--ccg-page); }

/* The eyebrow, in the three treatments the live page gives it. They are three
   and not one: WARUM has no rule at all (`content: none` on both pseudo-elements
   of the live element), LEISTUNGEN has a border under the text that is as long as
   the text, and BRANCHEN, KARRIERE and NEWS have no rule and a different colour.
   Reproducing one of them everywhere is the simplification this cycle exists to
   remove. */
.ccg-eyebrow {
  margin: 0 0 14.4px;
  color: var(--ccg-accent-2-text);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-warum-eyebrow-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-warum-eyebrow-tracking);
  text-transform: uppercase;
}

.ccg-eyebrow--violet {
  color: var(--ccg-accent);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-branchen-eyebrow-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-branchen-eyebrow-tracking);
  margin-bottom: 16px;
}

.ccg-eyebrow--on-dark {
  color: var(--ccg-accent-soft-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-branchen-eyebrow-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-branchen-eyebrow-tracking);
  margin-bottom: 19.2px;
}

/* The rule is the text's own width, because the element shrink-wraps: 188px on
   the live page for the words UNSERE LEISTUNGEN. An `::after` of a fixed length
   would be a different measurement. */
.ccg-eyebrow--underlined {
  display: inline-block;
  color: var(--ccg-accent);
  font-size: var(--ccg-svc-eyebrow-size);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-svc-eyebrow-tracking);
  padding-bottom: 7.2px;
  border-bottom: 2px solid var(--ccg-accent);
  margin-bottom: 17.6px;
}

.ccg-home__intro { margin-bottom: 48px; }

.ccg-home__intro--centred {
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

.ccg-home__title { margin: 0 0 16px; }

.ccg-home__title--warum {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-warum-head-size);
  line-height: var(--ccg-warum-head-line);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-warum-head-tracking);
}

/* ONE LINE, AND IT DOES NOT FIT IN THE 760px BLOCK THE HEADER MEASURES. The live
   headline is one line at 51.2px inside a 760px block; the Inter this theme
   self-hosts sets the same string wider than that and wraps it, which adds a line
   of 53.76px to the section. The block keeps its measured width and the headline
   is laid out at its own width and centred over it, so what is reproduced is the
   line count rather than the containment. The difference in the face's own metrics
   is a declared departure - the readme records it beside the fonts.

   `left` AND NOT `margin-left`. Written as `margin-left: 50%` this was undone by
   the `margin` shorthand each of the two headlines sets in its own rule further
   down, and the news headline rendered 415px left of centre - visible in a
   screenshot and in nothing else. */
.ccg-home__title--services,
.ccg-home-news__title {
  position: relative;
  left: 50%;
  width: max-content;
  max-width: none;
  transform: translateX(-50%);
}

.ccg-home__title--services {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-svc-head-size);
  line-height: var(--ccg-svc-head-line);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-svc-head-tracking);
}

.ccg-home__title-first { color: var(--ccg-heading); }
.ccg-home__title-second { color: var(--ccg-accent); }

/* The second half of the services headline is painted with a gradient clipped to
   the glyphs. `color` stays the gradient's own first stop: it is what a reader
   that cannot paint the gradient gets, and it is what the contrast measurement
   reads. The gradient's light end is 3.49:1 on white - the live site's own
   choice, recorded in the change's design notes, and not something a single
   threshold over one colour can see. */
.ccg-home__title-gradient {
  color: var(--ccg-accent);
  /* WHERE THE MEASURED GRADIENT END IS DRAWN, and it is written out of two colour
     tokens rather than held as one. `--ccg-button-end` is the measured end of the
     live call to action's gradient and no button paints it - white on it is
     3.49:1 - so until this cycle it ended a rule for a class no template emitted,
     which is the defect test/home.sh's class check was written for. It paints the
     second half of the services headline instead, which is the live element it was
     measured on: `linear-gradient(120deg, #5B2FD4 0%, #9B6DFF 100%)` is the accent
     and this value exactly. */
  background: linear-gradient(120deg, var(--ccg-accent) 0%, var(--ccg-button-end) 100%);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.ccg-home__subline {
  margin: 0;
  color: var(--ccg-body-quiet);
  font-size: var(--ccg-warum-sub-size);
  line-height: var(--ccg-warum-sub-line);
}

/* --- the hero, as the home page has it -------------------------------------- */

.ccg-home-hero {
  display: flex;
  align-items: flex-end;
  min-height: calc(100vh + var(--ccg-hero-bottom-space));
  padding-top: 0;
  padding-bottom: calc(2 * var(--ccg-hero-bottom-space));
  background-color: var(--ccg-hero-bg);
  color: var(--ccg-hero-heading);
}

/* The wrapper carries the filter and the oversize; the layer inside it carries
   the photograph and the parallax. Both are the live arrangement, and the split
   is what keeps `[data-ccg-parallax]` an element with no filter on it. */
.ccg-home-hero__media {
  position: absolute;
  top: -12px;
  bottom: -12px;
  left: -19px;
  right: -19px;
  overflow: hidden;
  filter: var(--ccg-hero-photo-filter);
  transform: scale(var(--ccg-hero-photo-scale));
  transform-origin: center;
  z-index: 0;
}

.ccg-home-hero__photo {
  position: absolute;
  inset: 0;
  background-position: 50% 50%;
  background-size: cover;
  background-repeat: no-repeat;
}

/* Five radial stops over the photograph, blurred and drifting. */
.ccg-home-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--ccg-hero-mesh);
  filter: var(--ccg-hero-mesh-blur);
  animation: var(--ccg-hero-mesh-motion);
}

@keyframes ccgMeshDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  25%      { transform: translate(-3%, 2%) scale(1.05); }
  50%      { transform: translate(2%, -2.5%) scale(0.97); }
  75%      { transform: translate(-1.5%, -1%) scale(1.03); }
}

/* THE CONSTELLATION IS THE FULL-SCREEN CELL, NOT THE SECTION, and the difference
   is visible rather than academic. The SVG is `preserveAspectRatio="slice", so
   its scale is whatever it takes to cover its own box: over `inset: 0` it covered
   the section's 1920 x 1208 and rendered 1900 x 1272, which magnified every orb
   to 177 px against the 158 the live page draws and pushed the third one off the
   right edge. The live element is 1920 x 1080 - solved back out of the rendered
   1891 x 1151 at (-62, -79) the measurement records, which is exactly what a
   1920 x 1080 box becomes under this transform. */
.ccg-home-hero__constellation {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 100vh;
  z-index: 2;
  pointer-events: none;
}

.ccg-home-hero__svg {
  display: block;
  width: 100%;
  height: 100%;
  transform: var(--ccg-hero-svg-transform);
  transform-origin: center center;
  filter: var(--ccg-hero-svg-filter);
}

/* TWO ARRANGEMENTS ARE RENDERED AND ONE OF THEM IS SHOWN, and `display` is the
   only lever there is. `preserveAspectRatio` is an attribute, not a property:
   measured on 07.09.2026 against this build by appending
   `.ccg-home-hero__svg { preserve-aspect-ratio: xMidYMid meet;
   -webkit-preserve-aspect-ratio: xMidYMid meet; preserveAspectRatio: xMidYMid
   meet }` as the last stylesheet on the running page and then reading the
   element - `getAttribute('preserveAspectRatio')` still `xMidYMid slice`,
   `preserveAspectRatio.baseVal` still align 6 / meetOrSlice 2, and
   `getComputedStyle(...).getPropertyValue('preserve-aspect-ratio')` empty. So the
   choice between covering and fitting cannot be made by a media query, and a
   second element is what makes it at all.

   The narrow one is hidden here rather than shown at the narrow band, so a
   window this file has no block for gets the arrangement the rest of the file
   was written against. */
.ccg-home-hero__constellation--narrow { display: none; }

/* No transform on the narrow arrangement, and it is the one place the two
   differ in more than coordinates. `--ccg-hero-svg-transform` is
   `translate(-4%, -4%) rotate(-4deg) scale(0.95)` - the live element's own - and
   a four-degree tilt of a horizontal chain of nodes reads as drift, while the
   same tilt of a badge with three discs around it reads as a mistake. The
   filter, which is the glow, is kept. */
.ccg-home-hero__svg--narrow { transform: none; }

/* No `width: 100%` here, and that is the point of the comment. `.ccg-measure` is
   also a class, so a width declared on this class and later in the file beats it
   - which is how the hero's content column measured 1860px at 1920 instead of
   1200px on its first run. */
.ccg-home-hero__inner {
  position: relative;
  z-index: 5;
  width: var(--ccg-measure);
  max-width: calc(100% - (2 * var(--ccg-measure-gutter)));
  margin-left: auto;
  margin-right: auto;
}

/* The text is not on the rail's left edge. An empty four-column block pushes it
   right on the live page; here it is the indent that block measures. */
.ccg-home-hero__text { padding-left: var(--ccg-hero-text-indent); }

.ccg-home-hero__title { margin: 0; }

/* Two steps of one headline, not two lines of one setting. */
.ccg-home-hero__setup {
  display: block;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-h1-setup-size);
  line-height: var(--ccg-h1-setup-line);
  font-weight: var(--ccg-h1-setup-weight);
  letter-spacing: var(--ccg-h1-setup-tracking);
  opacity: var(--ccg-h1-setup-opacity);
}

.ccg-home-hero__punch {
  position: relative;
  display: block;
  margin-top: var(--ccg-h1-punch-space);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-h1-punch-size);
  line-height: var(--ccg-h1-punch-line);
  font-weight: var(--ccg-h1-punch-weight);
  letter-spacing: var(--ccg-h1-punch-tracking);
}

.ccg-home-hero__punch::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -0.12em;
  height: 4px;
  width: 0;
  border-radius: 2px;
  background: var(--ccg-h1-underline);
  animation: var(--ccg-h1-underline-motion);
  transform-origin: left;
}

@keyframes ccgH1Underline {
  0%   { width: 0; opacity: 0; }
  20%  { opacity: 1; }
  100% { width: 100%; opacity: 1; }
}

.ccg-home-hero__subline {
  max-width: 1014px;
  margin: 36px 0 0;
  color: var(--ccg-hero-heading);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-hero-subline-size);
  line-height: var(--ccg-hero-subline-line);
}

/* The overlay the live wave carries: `rgba(0,0,0,0.85)` masked twice with the
   same file and composited `subtract`. Reproduced as declared. Two identical mask
   layers subtracted from one another leave nothing, so what this paints is
   nothing - which is measured in the change's design notes rather than reasoned
   about here, and is why the wave below it is what a visitor sees. */
.ccg-home-hero__wave-shade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--ccg-wave-hero);
  background: var(--ccg-hero-wave-overlay);
  -webkit-mask-size: 100% 100%, 100% 100%;
  mask-size: 100% 100%, 100% 100%;
  -webkit-mask-position: 0 0, 0 0;
  mask-position: 0 0, 0 0;
  -webkit-mask-repeat: no-repeat, no-repeat;
  mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: source-out;
  mask-composite: subtract;
  pointer-events: none;
  z-index: 3;
}

/* --- "Mehr als Buchhaltung": three cards ------------------------------------ */

.ccg-home-warum { background: var(--ccg-section-bg); }

.ccg-cards {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ccg-cards--3 { grid-template-columns: repeat(3, 1fr); gap: 25.6px; }

.ccg-card {
  position: relative;
  overflow: hidden;
  padding: 35.2px 28.8px 32px;
  border: 1px solid var(--ccg-card-hairline);
  border-radius: 18px;
  background: var(--ccg-page);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
}

.ccg-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--ccg-top-bar);
}

.ccg-card__number {
  position: absolute;
  top: 22.4px;
  right: 25.6px;
  color: var(--ccg-accent-2-text);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-warum-num-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-warum-num-tracking);
  opacity: 0.9;
}

.ccg-card__tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: 20.8px;
  border-radius: 14px;
  background: var(--ccg-card-tile-bg);
  color: var(--ccg-accent);
}

.ccg-card__icon { display: block; }

.ccg-card__title { margin: 0 0 11.2px; }

.ccg-card__text {
  margin: 0;
  color: var(--ccg-body-quiet);
  font-size: var(--ccg-warum-body-size);
  line-height: var(--ccg-warum-body-line);
}

/* --- "Fünf Funktionen": five cards in one row ------------------------------- */

/* The dot field is one large decoration rather than a tile - repeating it puts a
   second cluster wherever the section happens to end. */
.ccg-home-leistungen {
  background-color: var(--ccg-section-bg);
  background-repeat: no-repeat;
/* THE ANCHOR IS THE PHASE'S ZERO POINT, AND A PIXEL ANCHOR PUTS THE UNCOVERED
   EDGE IN SHOT. js/ccg.js reads this declared value once and adds the parallax
   shift to it, so whatever stands here is the offset at the moment the layer's
   top edge meets the top of the window. `cover` on a box of the image's own
   aspect ratio leaves nought vertical slack, and with nought slack every
   positive offset uncovers exactly that many pixels at the layer's top edge.
   Measured on 2026-09-02 at 1920 by sweeping the document in 40 px steps: this
   layer put 261 px of uncovered ground inside the window at scroll 2040.
   Written as `50%`, which is what all five layers of the original declare and
   which on nought slack is nought - so the offset is nought at the moment the
   edge arrives and the uncovered strip is never inside the window. */
  background-position: 50% 50%;
  background-size: cover;
}

.ccg-home-leistungen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--ccg-section-mesh-loesungen);
}

.ccg-home-leistungen .ccg-home__subline {
  max-width: 600px;
  margin-left: auto;
  margin-right: auto;
  color: var(--ccg-body-quiet-2);
  font-size: var(--ccg-svc-sub-size);
  line-height: var(--ccg-svc-sub-line);
}

.ccg-services {
  display: grid;
  /* `minmax(0, 1fr)` and not `1fr`: a bare `1fr` floors each track at the
     item's min-content width, so the card with the longest title took width from
     its neighbours and the five measured 216.9, 216.9, 216.9, 223 and 243.2
     rather than five equal tracks. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 20.8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ccg-services__item { display: flex; }

/* THE CARD IS A PORTRAIT WITH A PHOTOGRAPH HANGING IN ITS TOP-RIGHT CORNER, and
   the large top padding is what clears it. The watermark hangs out of the bottom
   right and is clipped by the card, which is why `overflow: hidden` is not
   decoration here. */
.ccg-service {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow: hidden;
  padding: var(--ccg-svc-card-space-top) 25.6px 28.8px;
  border: 1.5px solid var(--ccg-accent);
  border-radius: 18px;
  background: var(--ccg-page);
  box-shadow: var(--ccg-shadow-service);
  min-height: 360px;
  text-align: center;
  text-decoration: none;
}

a.ccg-service:hover, a.ccg-service:focus-visible { text-decoration: none; }

.ccg-service__photo {
  position: absolute;
  top: 20.8px;
  right: 20.8px;
  width: var(--ccg-svc-photo-size);
  height: var(--ccg-svc-photo-size);
  border-radius: 50%;
  border: var(--ccg-svc-photo-ring) solid var(--ccg-accent-2);
  background-position: 50% 50%;
  background-size: cover;
  box-shadow: var(--ccg-shadow-photo);
  z-index: 2;
}

.ccg-service__watermark {
  position: absolute;
  right: -8.8px;
  bottom: -70.4px;
  color: var(--ccg-accent);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-svc-watermark-size);
  line-height: 1;
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-svc-watermark-tracking);
  opacity: var(--ccg-svc-watermark-opacity);
  pointer-events: none;
  z-index: 0;
}

/* The pictogram is the one part of this card that does NOT follow the card's
   `text-align: center`. Measured on the live card: the 48 px glyph sits at
   dx 26.6 from the card's left edge - the content box's own left edge - while
   the number, the title, the rule, the sub-line and the call to action below it
   are all centred. The first pass of this change read it as centred and set
   `margin: 0 auto`, which put it at dx 87.7; a second measurement of both pages
   side by side is what found it. */
.ccg-service__icon {
  display: block;
  width: 48px;
  height: 48px;
  margin: 0 0 22.4px;
  color: var(--ccg-accent);
  position: relative;
  z-index: 1;
}

.ccg-service__number {
  position: relative;
  z-index: 1;
  display: block;
  margin-bottom: 8.8px;
  color: var(--ccg-accent-2-text);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-svc-num-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-svc-num-tracking);
}

.ccg-service__title {
  position: relative;
  z-index: 1;
  margin: 0 0 14px;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-svc-title-size);
  line-height: var(--ccg-svc-title-line);
  font-weight: var(--ccg-h1-weight);
  letter-spacing: var(--ccg-svc-title-tracking);
  color: var(--ccg-heading);
}

.ccg-service__accent {
  position: relative;
  z-index: 1;
  display: block;
  width: 32px;
  height: 3px;
  margin: 0 auto 14px;
  border-radius: 2px;
  background: var(--ccg-accent-2);
}

.ccg-service__kicker {
  position: relative;
  z-index: 1;
  display: block;
  margin-bottom: 25.6px;
  color: var(--ccg-body-quiet-3);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-svc-tag-size);
  line-height: var(--ccg-svc-tag-line);
  font-weight: var(--ccg-weight-semi);
}

.ccg-service__link {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4.992px;
  margin-top: auto;
  color: var(--ccg-accent);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-svc-cta-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-svc-cta-tracking);
  text-transform: uppercase;
}

/* A card whose page does not exist yet. It keeps every part of the live card and
   loses the pointer, because it is not a link: the marked-label rule this
   repository follows everywhere else. The card itself does NOT turn grey - the
   whole point of marking a label rather than the card is that the composition
   survives it - so THE LABEL IS WHERE THE DISTINCTION LIVES, and the template
   has to emit the modifier on the label as well as on the card. It did not: for
   one round these two rules sat here matching nothing, and the five "Mehr
   erfahren" labels painted `--ccg-accent`, byte-identical to a control that
   works, with `cursor: default` as the only difference a visitor could find and
   only once the pointer was already on it. test/composition.sh's `paint` case
   now reads both colours off the running page. */
.ccg-service--missing { cursor: default; }
.ccg-service__link--missing { color: var(--ccg-nav-missing-color); cursor: default; }

.ccg-arrow { display: inline-block; }

.ccg-home__cta {
  margin-top: 48px;
  text-align: center;
}

/* --- the call to action's button --------------------------------------------
 *
 * The gradient the live site paints ends at `--ccg-button-end`, and this ends at
 * `--ccg-button-end-applied` instead: white text on the measured end is 3.49:1,
 * and this button is the one element on the page whose whole job is to be read.
 * Both values and both numbers are in ccg.yaml.
 */
.ccg-button {
  display: inline-flex;
  align-items: center;
  gap: 9.408px;
  padding: 17.6px 35.2px;
  border-radius: var(--ccg-button-radius);
  background: linear-gradient(135deg, var(--ccg-button) 0%, var(--ccg-button-end-applied) 100%);
  color: var(--ccg-button-text);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-pill-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-pill-tracking);
  box-shadow: var(--ccg-shadow-pill);
  text-decoration: none;
}

a.ccg-button:hover, a.ccg-button:focus-visible { text-decoration: underline; }

/* A call to action whose page does not exist yet. It keeps the shape so the
   section still reads, and loses the gradient so it does not read as a button
   that works: the same distinction the navigation makes. */
.ccg-button--missing {
  background: var(--ccg-card-bg);
  color: var(--ccg-nav-missing-color);
  border: 1px solid var(--ccg-card-border);
  box-shadow: none;
  cursor: default;
}

.ccg-home__locality {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7.36px;
  max-width: 680px;
  margin: 25.6px auto 0;
  color: var(--ccg-accent);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-badge-size);
  line-height: var(--ccg-badge-line);
  font-weight: var(--ccg-weight-semi);
  text-align: center;
}

.ccg-home__locality strong { font-weight: var(--ccg-h3-weight); }

.ccg-home__pin {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--ccg-accent-2-text);
}

.ccg-home__pin svg { display: block; }

/* --- "Ihre Branche ist unser Zuhause": the second row of the same section ---- */

.ccg-home-branchen { margin-top: 112px; }

/* Left-aligned, unlike the centred header directly above it. */
.ccg-home-branchen__intro { padding-left: 2px; }

.ccg-home-branchen__title {
  max-width: 600px;
  margin: 0;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-branchen-head-size);
  line-height: var(--ccg-branchen-head-line);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-branchen-head-tracking);
  color: var(--ccg-heading);
}

/* THE STAGE IS 1200 x 600 EXACTLY WHERE THERE IS ROOM FOR IT, so one SVG unit is
   one CSS pixel and every coordinate in the markup is also a position on the
   page. The 60px shift is measured and is why the stage overhangs the rail's
   right edge.

   AND IT NO LONGER STANDS 1200 WIDE IN A RAIL THAT IS NOT. `.ccg-measure` is
   `width: 1200px; max-width: calc(100% - 60px)`, so below a 1260 px window the
   rail is narrower than 1200 while this stage was not, and the 60 px shift then
   carried it further out still: measured on the working tree on 11.09.2026, the
   stage stood at x 90..1290 at 1025, at 1100, at 1180 and at 1200 - the same box
   at all four, because nothing in it depended on the window - and put
   `.ccg-branche__label` "Handwerk" at a right edge of 1209.57 and "Gastronomie"
   at 1119.27. Photographed at 1025: Handwerk is not on the page at all and
   Gastronomie is cut down the middle by the edge of the window.

   `max-width: 100%` ALONE WOULD BE A SECOND DEFECT OF THE KIND THIS CHANGE
   EXISTS TO REMOVE. The stage carries `svg.ccg-network__lines`, `viewBox="0 0
   1200 600"` with `preserveAspectRatio="none"`, and the hub's dashed ring and
   halo are drawn in it. Holding the height at 600 while the width fell to 965
   measured a skew of 1.243 on that SVG at 1025 and painted the ring and the halo
   as ellipses. So the height follows the width: `aspect-ratio` keeps the stage's
   own 2:1 and the SVG's skew stays 1.000 at every width, measured 1.000 at 1025,
   1100, 1180, 1200, 1259, 1280, 1600 and 1920.

   FROM 1260 TO 1920 NOTHING MOVES. The rail is 1200 there, the cap does not
   bind, and `aspect-ratio` from a 1200 px width is 600 px exactly - re-measured
   at 1440, 1600 and 1920, every node, every label, the stage and the SVG
   identical to HEAD's.

   ABOVE 1920 IT DOES MOVE, and this paragraph read "at 1260 and above nothing
   moves" until `give-the-layout-a-wide-band` made that false. The wide block at
   the foot of this file puts `.ccg-network` on `--ccg-rail`, so at 2560 the
   stage is 1600 wide and `aspect-ratio` makes it 800 tall, and every node and
   label moves with it. What does NOT change is the shape: the stage keeps its
   own 2:1 at every width, so the SVG's skew is still 1.000 and the node
   diameters below stay the same share of the stage - see the rule at the foot
   of this file and the note on those diameters. */
.ccg-network {
  position: relative;
  width: var(--ccg-measure);
  max-width: 100%;
  height: auto;
  aspect-ratio: 1200 / 600;
  margin: 24px 0 32px;
  transform: translateX(var(--ccg-branchen-stage-offset));
}

.ccg-network__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}

.ccg-network__halo {
  transform-origin: 550px 320px;
  animation: var(--ccg-branchen-halo-motion);
}

@keyframes cnHubHaloPulse {
  0%, 100% { opacity: 0.85; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.08); }
}

.ccg-network__lead { animation: var(--ccg-branchen-lead-motion); }

@keyframes cnLeadingDraw { to { stroke-dashoffset: 0; } }

.ccg-network__line {
  stroke-dasharray: 1200;
  stroke-dashoffset: 1200;
  animation: var(--ccg-branchen-line-motion);
}

.ccg-network__line--1 { animation-delay: 0.30s; }
.ccg-network__line--2 { animation-delay: 0.45s; }
.ccg-network__line--3 { animation-delay: 0.60s; }
.ccg-network__line--4 { animation-delay: 0.75s; }
.ccg-network__line--5 { animation-delay: 0.90s; }
.ccg-network__line--6 { animation-delay: 1.05s; }

@keyframes cnLineDraw { to { stroke-dashoffset: 0; } }

.ccg-network__amb {
  opacity: 0;
  animation: var(--ccg-branchen-amb-motion);
}

.ccg-network__amb--1 { animation-delay: 1.60s; }
.ccg-network__amb--2 { animation-delay: 1.75s; }
.ccg-network__amb--3 { animation-delay: 1.90s; }
.ccg-network__amb--4 { animation-delay: 2.05s; }
.ccg-network__amb--5 { animation-delay: 2.20s; }
.ccg-network__amb--6 { animation-delay: 2.35s; }

@keyframes cnAmbLineFade { to { opacity: 0.55; } }

.ccg-network__particles { pointer-events: none; }

.ccg-network__dot {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-branchen-dot-motion);
}

.ccg-network__dot--1  { animation-delay: 0.5s; }
.ccg-network__dot--2  { animation-delay: 1.2s; }
.ccg-network__dot--3  { animation-delay: 0.8s; }
.ccg-network__dot--4  { animation-delay: 2.0s; }
.ccg-network__dot--5  { animation-delay: 1.6s; }
.ccg-network__dot--6  { animation-delay: 2.4s; }
.ccg-network__dot--7  { animation-delay: 2.8s; }
.ccg-network__dot--8  { animation-delay: 3.2s; }
.ccg-network__dot--9  { animation-delay: 0.2s; }
.ccg-network__dot--10 { animation-delay: 1.0s; }
.ccg-network__dot--11 { animation-delay: 2.6s; }
.ccg-network__dot--12 { animation-delay: 3.6s; }

@keyframes cnDotPulse {
  0%, 100% { opacity: 0.30; transform: scale(0.85); }
  50%      { opacity: 0.95; transform: scale(1.5); }
}

.ccg-network__hub {
  position: absolute;
  left: 45.833%;
  top: 53.333%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The hub takes the same share of the stage the six nodes take, and
     `--ccg-branchen-hub-size` is its 175 px. Left in pixels it was the one thing
     on the stage that did not shrink with it: measured at 1025, the gap between
     the first label and the hub was 17.66 px with the nodes proportional and the
     hub not, against 49.96 at 1920, and photographed there the hub read as
     oversized against its own ring of nodes. With this line the gap is 35.47 at
     1025 and 49.92 at 1920 - the remainder is the labels' fixed type size, which
     does not scale and is not made to.

     AND IT STILL READS ITS TOKEN, WHICH IS NOT DECORATION. `--ccg-branchen-hub-size`
     is emitted by ccg.yaml into every page and was read by this one rule. Writing
     `175` into a `calc` here left it emitted and read by nothing, which
     `test/home.sh`'s `should read every design value it emits in a rule it
     serves` is for - it failed on this exact token, by name, before this form
     replaced the literal. `100% * 175px / 1200px` is a percentage times a number,
     which is a percentage: measured, 175.000 px at a 1200 stage and 140.719 at a
     965 one. */
  width: calc(100% * var(--ccg-branchen-hub-size) / var(--ccg-measure));
  height: calc(200% * var(--ccg-branchen-hub-size) / var(--ccg-measure));
  border-radius: 50%;
  border: 1px solid var(--ccg-branchen-hub-border);
  background: var(--ccg-branchen-hub-fill);
  box-shadow: var(--ccg-branchen-hub-shadow);
  transform: translate(-50%, -50%);
  animation: var(--ccg-branchen-hub-motion);
  z-index: 2;
}

@keyframes cnHubFloat {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, -50%) scale(1.04); }
}

.ccg-network__hub-logo {
  display: block;
  max-width: 65%;
  max-height: 65%;
  width: auto;
  height: auto;
  object-fit: contain;
  z-index: 2;
}

.ccg-network__hub-ring {
  position: absolute;
  inset: -14px;
  border-radius: 50%;
  border: 1px dashed var(--ccg-branchen-hub-ring);
  animation: var(--ccg-branchen-ring-motion);
  pointer-events: none;
}

.ccg-network__hub-ring::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 1px solid var(--ccg-branchen-hub-ring-2);
  animation: var(--ccg-branchen-ring-motion-2);
}

@keyframes cnHubRingRotate { to { transform: rotate(360deg); } }

/* The six nodes. Every `left`, `top` and diameter below is the measurement, as a
   percentage of the 1200 x 600 stage - which is the form the live site uses and
   the form that keeps one SVG unit one pixel. */
.ccg-branche {
  position: absolute;
  transform: translate(-50%, -50%);
  animation: var(--ccg-branchen-node-motion);
  z-index: 1;
}

@keyframes cnNodeIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* THE SIX DIAMETERS ARE SHARES OF THE STAGE AND WERE PIXELS, and each pair is
   the same pixel over the stage's own width. A node stays round because the stage
   is 2:1 at every width, so the share of the height is twice the share of the
   width - `200%` against `100%` - and the denominator is the same token in both.
   Written as `calc` rather than as a decimal so the arithmetic is the file's
   rather than a rounding of it, and against `--ccg-measure` rather than a bare
   1200 because that token WAS the stage's width three rules above at every
   width this site had when these diameters were written: measured at 1920 and
   at 1440 against HEAD, every node 150 and 175 px exactly and every label
   identical to 0.0000000000 px.

   THAT PREMISE IS CONDITIONAL SINCE `give-the-layout-a-wide-band`, and the
   arithmetic survives it for a reason worth writing down rather than
   rediscovering. Above 1921 the stage's width is `--ccg-rail` and not
   `--ccg-measure`, so `--ccg-measure` is no longer the denominator of the box
   these percentages resolve against - `100%` is the rail. The node does not
   distort, because `100%` and `200%` are both the rail (the stage stays 2:1),
   and it keeps its SHARE of the stage, because the rail scales width and height
   together: measured at 2560 with the rail at 1600, the 150 px node is 200 x
   200 px, which is 12.5 % of the stage exactly as 150 of 1200 is at 1920. What
   `--ccg-measure` now supplies here is the 1200 that these six pixel values
   were read at, which is what it has to be for the share to hold - so the token
   is still the right one to divide by, and no longer for the reason above it.

   WITHOUT THIS THE NODES WOULD CROWD THE SHRUNKEN STAGE. Measured at 1025 with
   the stage at 965 and the diameters left in pixels: each node occupies 1.2435
   times the share of the stage it has at 1920 - the 175 px node 18.13 % against
   14.58 % - the gap between the first label and the hub falls from 49.96 px to
   2.97, and the `Handel` label hangs 40.84 px below the stage against 29.09. */
.ccg-branche--1 { left: 53.333%; top: 13.333%; width: calc(100% * 150px / var(--ccg-measure)); height: calc(200% * 150px / var(--ccg-measure)); animation-delay: 0.60s; }
.ccg-branche--2 { left: 90%;     top: 23.333%; width: calc(100% * 105px / var(--ccg-measure)); height: calc(200% * 105px / var(--ccg-measure)); animation-delay: 0.75s; }
.ccg-branche--3 { left: 81.667%; top: 71.667%; width: calc(100% * 115px / var(--ccg-measure)); height: calc(200% * 115px / var(--ccg-measure)); animation-delay: 0.90s; }
.ccg-branche--4 { left: 45%;     top: 90%;     width: calc(100% * 130px / var(--ccg-measure)); height: calc(200% * 130px / var(--ccg-measure)); animation-delay: 1.05s; }
.ccg-branche--5 { left: 15%;     top: 75%;     width: calc(100% * 95px / var(--ccg-measure));  height: calc(200% * 95px / var(--ccg-measure));  animation-delay: 1.20s; }
.ccg-branche--6 { left: 10.417%; top: 21.667%; width: calc(100% * 175px / var(--ccg-measure)); height: calc(200% * 175px / var(--ccg-measure)); animation-delay: 1.35s; }

.ccg-branche__circle {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border: 2px solid var(--ccg-page);
  border-radius: 50%;
  background: var(--ccg-page);
  box-shadow: var(--ccg-branchen-node-shadow);
  animation: var(--ccg-branchen-shimmer-motion);
}

.ccg-branche--1 .ccg-branche__circle { animation-delay: 0s; }
.ccg-branche--2 .ccg-branche__circle { animation-delay: 0.7s; }
.ccg-branche--3 .ccg-branche__circle { animation-delay: 1.4s; }
.ccg-branche--4 .ccg-branche__circle { animation-delay: 2.1s; }
.ccg-branche--5 .ccg-branche__circle { animation-delay: 2.8s; }
.ccg-branche--6 .ccg-branche__circle { animation-delay: 3.5s; }

/* THE GLOW IS A KEYFRAME AND NOT A NUMBER. design.md records the alphas it
   sampled - 0.42 to 0.75 and 0.30 to 0.50 - as samples of a running animation
   rather than as authored values, so what is reproduced is the keyframe, its
   duration, its easing and its six delays. */
@keyframes cnNodeShimmer {
  0%, 100% { filter: var(--ccg-branchen-shimmer-rest-applied); }
  50%      { filter: var(--ccg-branchen-shimmer-peak-applied); }
}

/* WHERE THE MEASURED PAIR IS STILL DRAWN. The authored radii reach 12 px past the
   circle, which is where the label sits, so what the label is read against is the
   glow rather than the section - 4.43:1 on this page and 4.78:1 on the live one.
   The applied pair above is the same colours at a shorter reach; the measured pair
   draws the hub's halo, which carries no text. */
.ccg-network__hub-ring { filter: var(--ccg-branchen-shimmer-rest); }
.ccg-network__hub:hover .ccg-network__hub-ring { filter: var(--ccg-branchen-shimmer-peak); }

/* A real element rather than a pseudo-element, which is a deliberate difference
   from the live markup: a transition on a `::before` has no carrier that
   `querySelectorAll('body *')` can return, so the token that holds it would read
   as emitted and applied to nothing - the defect this repository has shipped
   twice in another form. */
.ccg-branche__ring {
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 1px dashed var(--ccg-branchen-node-ring);
  pointer-events: none;
  z-index: 3;
}

.ccg-branche__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--ccg-branchen-node-filter);
}

/* The label's colour is `branche-label-text` and not `heading`, and the reason is
   in ccg.yaml beside the value: this text sits inside a pulsing glow, so its
   ratio against the ground is a range and the measured navy clears only part of
   it. Everything else about the label is the measurement. */
.ccg-branche__label {
  position: absolute;
  top: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%);
  color: var(--ccg-branche-label-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-branchen-label-size);
  line-height: var(--ccg-branchen-label-line);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-branchen-label-tracking);
  text-transform: uppercase;
  white-space: nowrap;
  z-index: 2;
}

.ccg-branche__tooltip {
  position: absolute;
  width: 280px;
  padding: 17.6px 19.2px;
  border: 1px solid var(--ccg-branchen-tooltip-border);
  border-radius: 14px;
  background: var(--ccg-branchen-tooltip-bg);
  box-shadow: var(--ccg-branchen-tooltip-shadow);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  text-align: left;
  z-index: 20;
}

.ccg-branche__tooltip::before {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
  border: 1px solid var(--ccg-branchen-tooltip-border);
  background: var(--ccg-branchen-tooltip-bg);
  transform: rotate(45deg);
}

.ccg-branche__tooltip-title {
  margin: 0 0 9.6px;
  color: var(--ccg-accent);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-branchen-tooltip-title-size);
  line-height: var(--ccg-branchen-tooltip-title-line);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-branchen-tooltip-title-tracking);
  text-transform: uppercase;
}

.ccg-branche__tooltip-text {
  margin: 0;
  color: var(--ccg-tooltip-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-branchen-tooltip-text-size);
  line-height: var(--ccg-branchen-tooltip-text-line);
  font-weight: var(--ccg-weight-body);
}

/* Per node: which side the tooltip stands on and how far it slides. */
.ccg-branche--1 .ccg-branche__tooltip { top: calc(100% + 50px); left: 50%; transform: translateX(-50%) translateY(-10px); }
.ccg-branche--1 .ccg-branche__tooltip::before { top: -7px; left: 50%; transform: translateX(-50%) rotate(45deg); border-right: none; border-bottom: none; }
.ccg-branche--2 .ccg-branche__tooltip,
.ccg-branche--3 .ccg-branche__tooltip { right: calc(100% + 28px); top: 50%; transform: translateY(-50%) translateX(10px); }
.ccg-branche--2 .ccg-branche__tooltip::before,
.ccg-branche--3 .ccg-branche__tooltip::before { right: -7px; top: 50%; transform: translateY(-50%) rotate(45deg); border-left: none; border-bottom: none; }
.ccg-branche--4 .ccg-branche__tooltip { bottom: calc(100% + 28px); left: 50%; transform: translateX(-50%) translateY(10px); }
.ccg-branche--4 .ccg-branche__tooltip::before { bottom: -7px; left: 50%; transform: translateX(-50%) rotate(45deg); border-left: none; border-top: none; }
.ccg-branche--5 .ccg-branche__tooltip,
.ccg-branche--6 .ccg-branche__tooltip { left: calc(100% + 28px); top: 50%; transform: translateY(-50%) translateX(-10px); }
.ccg-branche--5 .ccg-branche__tooltip::before,
.ccg-branche--6 .ccg-branche__tooltip::before { left: -7px; top: 50%; transform: translateY(-50%) rotate(45deg); border-right: none; border-top: none; }

/* --- "Unternehmenszukunft" --------------------------------------------------- */

.ccg-home-zukunft {
  background-color: transparent;
  color: var(--ccg-hero-heading);
}

/* The photograph, darkened under the measured mesh. The darkening is not a
   measurement - it is in the unmeasured block with the ratios it was chosen by -
   and it is here because the live section's own photograph is darker than the one
   this repository holds, and every word in the section is painted over it. */
.ccg-home-zukunft__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
  filter: var(--ccg-zukunft-photo-filter);
  z-index: 0;
}

.ccg-home-zukunft__photo {
  position: absolute;
  inset: 0;
/* THE ANCHOR IS THE PHASE'S ZERO POINT, AND A PIXEL ANCHOR PUTS THE UNCOVERED
   EDGE IN SHOT. js/ccg.js reads this declared value once and adds the parallax
   shift to it, so whatever stands here is the offset at the moment the layer's
   top edge meets the top of the window. `cover` on a box of the image's own
   aspect ratio leaves nought vertical slack, and with nought slack every
   positive offset uncovers exactly that many pixels at the layer's top edge.
   Measured on 2026-09-02 at 1920 by sweeping the document in 40 px steps: this
   layer put 354 px of uncovered ground inside the window at scroll 4000 - the
   strip the defect was reported as.
   Written as `50%`, which is what all five layers of the original declare and
   which on nought slack is nought - so the offset is nought at the moment the
   edge arrives and the uncovered strip is never inside the window. */
  background-position: 50% 50%;
  background-size: cover;
  background-repeat: no-repeat;
}

.ccg-home-zukunft::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--ccg-zukunft-mesh);
  animation: var(--ccg-zukunft-mesh-motion);
}

/* The darkening between the mesh and the content. It is not a measurement - it is
   in the unmeasured block with the ratio it was chosen by - and it exists because
   the mesh's own light half and this repository's photograph together render a
   ground the section's white lettering cannot be read on. */
.ccg-home-zukunft::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--ccg-zukunft-scrim);
}

/* AND IT MUST NOT DARKEN THE TWO WAVES. The scrim above is `inset: 0` at the same
   z-index as the waves and, being an `::after`, paints last - so it was greying
   the white shapes that join this section to the two around it, which is why the
   top of this section read as flat grey-lavender against the live page's white.
   The waves are lifted over it. The correction the scrim exists for is untouched:
   it darkens the photograph under the lettering, and no text on this page sits on
   a wave. */
.ccg-home-zukunft .ccg-home__wave { z-index: 2; }

/* AND THE THREE LAYERS UNDER THAT TOP WAVE START ONE PIXEL LOWER. This is the
   second half of the hairline repair written out at the head of this file, and it
   is here rather than there because it has to come after the three rules above
   that set `inset: 0` on these elements - the same specificity, so the last one
   wins.

   THREE AND NOT FOUR, AND THE FOURTH WAS NEVER DOING ANYTHING. This rule listed
   `.ccg-home-zukunft__particles` as well until 14.09.2026. That selector was
   inert: `.ccg-home-zukunft__particles { position: absolute; inset: 0 }` stands
   BELOW this rule in this file, past the `@keyframes`, at the same specificity
   (0-1-0), so its `inset: 0` won and the particle field never moved. Every measurement in this change -
   the forty-two clean readings of S 2.2 and the 517,309 pixels below - was
   therefore taken with three layers moving and the particle field where it was.
   It is dropped rather than reordered because it does not belong here either:
   the field is `z-index: 2`, the same as the top wave, and it stands AFTER the
   wave in zukunft.html.twig (the wave at line 57, the field at line 93), so it
   paints OVER the wave and is not one of the layers under it. The heading of
   this comment has said three all along.

   The boundary this is about is `leistungen -> zukunft` at css y 3818.75 on the
   demo at 1890 wide. At a device scale where the clip does not land on a whole
   device row the wave's own top edge is antialiased twice - once by its box and
   once by the clip they share - while the mesh, the scrim and the photograph
   behind it are antialiased once, so they show through the wave that is supposed
   to cover them and the row reads 197,188,214 between two rows of white. Moving
   them one pixel down leaves only the wave in that row. The wave is opaque white
   at its top - read off the file, `welle-oben.png` row 0 is 255,255,255 at alpha
   255 across its whole width - so the pixel they give up is one that has never
   been visible. Measured after: the row is 255,255,255 at 1.6 as it already was
   at 1.0. And the cost, which is real and is not the top row: these layers are
   `inset: 0`, so a top of 1 px shortens their box by one and the photograph and
   the mesh, which cover it, are resampled. 517,309 pixels of this band differ at
   1920 x 1080 at scale 1, none by more than 11 of 765. design.md S 2.3. */
.ccg-home-zukunft__media,
.ccg-home-zukunft::before,
.ccg-home-zukunft::after { top: 1px; }

@keyframes ccgZukunftDrift {
  0%   { transform: translate(0, 0) scale(1); }
  100% { transform: translate(-1.5%, 1%) scale(1.02); }
}

.ccg-home-zukunft__particles {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 2;
}

/* The same two renderings on the home page's field, and the same reason. This
   one is switched at 1557 rather than at 1024, because its box is the section's
   token height at every width from 1025 up and the skew against 1600:900 crosses
   1.5 at 1557.3: measured 1.622 at 1440 x 900 and 1.461 at 1599 x 900. */
.ccg-home-zukunft__particles .ccg-home-zukunft__field--tall { display: none; }

.ccg-home-zukunft__particles svg {
  display: block;
  width: 100%;
  height: 100%;
  filter: var(--ccg-zukunft-particle-filter);
}

/* Two columns and no gap between them: 800px and 400px on a 1200px rail - and
   the two tracks are that ratio rather than those pixels, because the rail is not
   always 1200. `.ccg-measure` caps at `calc(100% - 60px)`, so below a 1260 px
   window the rail narrows and two fixed tracks did not: measured on the working
   tree, `.ccg-jobs` stood at x 870..1230 and the card's title and control at
   893..1207 at 1025, at 1100, at 1180 and at 1200 alike, and the last width at
   which anything of this teaser was outside the window was 1229. Photographed at
   1025 the card had no right edge at all - no corner, no border, the badge
   reading `INITIATIVBEWER` and the control `Initiativ bewerben` without its
   arrow.

   `2fr 1fr` IS 800 AND 400 WHEREVER THE RAIL IS 1200, which is every width from
   1260 up - re-measured at 1280, 1600 and 1920, both tracks and every box inside
   them identical to HEAD's. `minmax(0, …)` rather than a bare `fr` for the reason
   the narrow band already gives its rows: `1fr` floors a track at its content's
   min-content and would put the card back outside. */
.ccg-home-zukunft__inner {
  position: relative;
  z-index: 3;
  min-height: var(--ccg-zukunft-cell-height);
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 0;
  align-items: start;
}

.ccg-home-zukunft__main { padding-top: 448px; }

.ccg-home-zukunft__title {
  max-width: 600px;
  margin: 0 0 12.8px;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-zukunft-head-size);
  line-height: var(--ccg-zukunft-head-line);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-zukunft-head-tracking);
  color: var(--ccg-hero-heading);
}

.ccg-home-zukunft__text {
  max-width: 540px;
  margin: 8px 0 32px;
  color: var(--ccg-zukunft-sub-color);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-zukunft-sub-size);
  line-height: var(--ccg-zukunft-sub-line);
}

/* The ghost pill: an outline over the photograph with the filled gradient
   waiting underneath it at opacity nought, which is the live hover. */
.ccg-ghost-button {
  position: relative;
  display: inline-block;
  padding: 14.4px 32px;
  border: 2px solid var(--ccg-ghost-button-border);
  border-radius: 999px;
  background: var(--ccg-ghost-button-bg);
  box-shadow: var(--ccg-ghost-button-shadow);
  color: var(--ccg-hero-heading);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-size-body);
  line-height: 1;
  font-weight: var(--ccg-ghost-button-weight);
  text-decoration: none;
}

.ccg-ghost-button::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--ccg-ghost-button-hover);
  opacity: 0;
  z-index: -1;
}

a.ccg-ghost-button:hover::before, a.ccg-ghost-button:focus-visible::before { opacity: 1; }
a.ccg-ghost-button:hover, a.ccg-ghost-button:focus-visible { text-decoration: none; }

.ccg-ghost-button--missing {
  border-color: var(--ccg-nav-missing-color);
  background: none;
  box-shadow: none;
  color: var(--ccg-nav-missing-hero-color);
  cursor: default;
}

/* 487, NOT THE 152 THE LIVE RULE DECLARES, and this is the case the working rule
   about declarations was written for. The live block carries `margin-top: 152px`
   and renders at y 4632 - 615 px below its section's top edge at 4017 - because
   its containing block starts 336 px lower than ours does. Copying the 152 put
   the label at 280 and the card at 318: the whole right column sat a third of the
   section too high, which is the one difference the screenshots showed at a
   glance. The rendered offset is the measurement; the declared margin is not. */
.ccg-jobs {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 487px 0 0 40px;
  text-align: left;
}

.ccg-jobs__label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  color: var(--ccg-hero-heading);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-job-label-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-job-label-tracking);
  text-transform: uppercase;
}

.ccg-jobs__label::before {
  content: "";
  width: 28px;
  height: 2px;
  border-radius: 2px;
  background: var(--ccg-job-label-rule);
}

.ccg-job-card {
  position: relative;
  display: block;
  overflow: hidden;
  padding: 22px 22px 20px;
  border: 1.5px solid var(--ccg-job-card-border);
  border-radius: 14px;
  background: var(--ccg-job-card-bg-applied);
  -webkit-backdrop-filter: var(--ccg-job-card-blur);
  backdrop-filter: var(--ccg-job-card-blur);
  box-shadow: var(--ccg-job-card-shadow);
  color: var(--ccg-hero-heading);
  text-decoration: none;
}

a.ccg-job-card:hover, a.ccg-job-card:focus-visible { text-decoration: none; }

.ccg-job-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--ccg-top-bar-3);
  background-size: 200% 100%;
  animation: var(--ccg-job-stripe-motion);
}

@keyframes ccgJobStripe {
  0%   { background-position: 0% 0; }
  100% { background-position: 200% 0; }
}

.ccg-job-card--missing { cursor: default; }

.ccg-job-card__badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding: 4px 10px 4px 8px;
  border: 1px solid var(--ccg-job-badge-border);
  border-radius: 999px;
  background: var(--ccg-job-badge-bg);
  color: var(--ccg-job-badge-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-job-badge-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-job-badge-tracking);
  text-transform: uppercase;
}

.ccg-job-card__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ccg-accent-gold-bright);
  animation: var(--ccg-job-dot-motion);
}

@keyframes ccgJobPulse {
  0%   { box-shadow: var(--ccg-job-dot-rest); }
  70%  { box-shadow: var(--ccg-job-dot-spread); }
  100% { box-shadow: var(--ccg-job-dot-gone); }
}

.ccg-job-card__title {
  display: block;
  margin-bottom: 8px;
  color: var(--ccg-hero-heading);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-job-title-size);
  line-height: var(--ccg-job-title-line);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-job-title-tracking);
}

.ccg-job-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-bottom: 14px;
  color: var(--ccg-job-meta-color);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-job-meta-size);
  line-height: var(--ccg-line-body);
}

.ccg-job-card__meta-item { display: inline-flex; align-items: center; gap: 6px; }

.ccg-job-card__cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding-top: 12px;
  border-top: 1px solid var(--ccg-job-cta-rule);
  color: var(--ccg-accent-soft-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-job-cta-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-semi);
  letter-spacing: var(--ccg-job-cta-tracking);
}

/* And the job card, which had no label rule at all: only the card lost its
   pointer, and its call to action kept `--ccg-accent-soft-text`. The grey is the
   hero's rather than the navigation's, because this label sits on the dark card -
   the same choice `.ccg-ghost-button--missing` makes two sections up. It is
   written AFTER the rule it overrides: both selectors are 0-1-0, so source order
   is the whole of the cascade here. */
.ccg-job-card__cta--missing { color: var(--ccg-nav-missing-hero-color); cursor: default; }

/* --- "Aktuelle Fachartikel": six teasers ------------------------------------ */

.ccg-home-news {
  padding-top: var(--ccg-section-space-news);
  padding-bottom: var(--ccg-section-space-news);
  background-color: transparent;
/* THE ANCHOR IS THE PHASE'S ZERO POINT, AND A PIXEL ANCHOR PUTS THE UNCOVERED
   EDGE IN SHOT. js/ccg.js reads this declared value once and adds the parallax
   shift to it, so whatever stands here is the offset at the moment the layer's
   top edge meets the top of the window. An anchor written as an absolute length
   is measured from the box's top edge whatever the image does, so 285 px of it
   was 285 px of ground the image never reached. Measured on 2026-09-02 at 1920
   by sweeping the document in 40 px steps: this layer put 280 px of uncovered
   ground inside the window at scroll 5320.
   Written as `50%`, which is what all five layers of the original declare. This
   is the one of the three that has slack to share: punkte.png is 282.6 px taller
   than its box at `cover`, so `50%` is -141.3 here and the image overhangs the
   box by 141 above and below at every offset the sweep reaches. */
  background-position: 50% 50%;
  background-size: cover;
  background-repeat: no-repeat;
}

/* THE GROUND UNDER THE DOT FIELD, WHICH IS THE ONE IT WAS ALREADY STANDING ON.
   Part of the hairline repair written out at the head of this file, and it has to
   stand after the `transparent` above rather than beside the rest of that repair,
   because the two rules have the same specificity and the last one wins. `body`
   computes `rgb(255, 255, 255)` and `html` and `.ccg-main` are both transparent,
   so this is the colour that was showing through the dot field in the first
   place. */
.ccg-home-news { background-color: var(--ccg-page); }

.ccg-home-news::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--ccg-section-mesh-news);
}

/* The live wrapper keeps 24px of its own inside the rail, so the working column
   is 1152 wide rather than 1200. */
.ccg-home-news__inner { padding: 0 24px var(--ccg-section-space); }

.ccg-home-news__title {
  margin: 0 0 12.8px;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-news-head-size);
  line-height: var(--ccg-news-head-line);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-news-head-tracking);
  color: var(--ccg-heading);
}

.ccg-home-news .ccg-eyebrow--violet { line-height: var(--ccg-news-eyebrow-line); }

.ccg-home-news .ccg-home__subline {
  max-width: 600px;
  margin-left: auto;
  margin-right: auto;
  color: var(--ccg-body-quiet-2);
  font-size: var(--ccg-news-sub-size);
  line-height: var(--ccg-news-sub-line);
}

.ccg-teasers {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ccg-teasers__item { display: flex; }

.ccg-teaser {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow: hidden;
  border: 1px solid var(--ccg-card-hairline);
  border-radius: var(--ccg-card-radius);
  background: var(--ccg-page);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
  color: inherit;
  text-decoration: none;
}

a.ccg-teaser:hover, a.ccg-teaser:focus-visible { text-decoration: none; }

.ccg-teaser::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--ccg-top-bar-3);
  z-index: 1;
}

.ccg-teaser__image {
  display: block;
  width: 100%;
  height: var(--ccg-news-image-height);
  background-color: var(--ccg-card-bg);
  background-position: 50% 50%;
  background-size: cover;
}

.ccg-teaser__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  flex: 1;
  padding: 22.4px 22.4px 24px;
}

.ccg-teaser__date {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  color: var(--ccg-accent);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-news-date-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-news-date-tracking);
  text-transform: uppercase;
}

.ccg-teaser__date::before {
  content: "";
  width: 22px;
  height: 1px;
  background: var(--ccg-accent);
}

.ccg-teaser__title {
  margin: 0 0 10px;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-news-title-size);
  line-height: var(--ccg-news-title-line);
  font-weight: var(--ccg-news-title-weight);
  letter-spacing: var(--ccg-news-title-tracking);
  color: var(--ccg-heading);
}

.ccg-teaser__text {
  display: block;
  margin-bottom: 16px;
  color: var(--ccg-body-quiet-2);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-news-excerpt-size);
  line-height: var(--ccg-news-excerpt-line);
}

.ccg-teaser__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin-top: auto;
  padding-top: 13.6px;
  border-top: 1px solid var(--ccg-card-hairline);
  color: var(--ccg-accent);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-news-cta-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-news-cta-tracking);
  text-transform: uppercase;
}

/* The same pair as the service card, and it was dead for the same reason. */
.ccg-teaser--missing { cursor: default; }
.ccg-teaser__link--missing { color: var(--ccg-nav-missing-color); cursor: default; }

/* The section with nothing to show. It says so in one line rather than leaving a
   gap, because a gap between a heading and a link reads as a page that failed to
   load. Nothing renders it while the front matter carries its six teasers, so
   test/home.sh reports it by name as a skip - "should say so in one line when the
   news section carries no teaser" - rather than as a pass. That sentence used to
   be here without the name, and without one it was false: the branch that would
   have printed it was unreachable and the suite said nothing at all. */
.ccg-teasers__empty { margin-bottom: 0; }

.ccg-home__archive { margin: 48px 0 0; text-align: center; }

.ccg-archive-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 15.2px 33.6px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--ccg-button) 0%, var(--ccg-button-end-applied) 100%);
  color: var(--ccg-button-text);
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-news-archive-size);
  line-height: var(--ccg-line-body);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-news-archive-tracking);
  box-shadow: var(--ccg-shadow-pill-news);
  text-decoration: none;
}

a.ccg-archive-button:hover, a.ccg-archive-button:focus-visible { text-decoration: underline; }

.ccg-archive-button--missing {
  background: var(--ccg-card-bg);
  color: var(--ccg-nav-missing-color);
  border: 1px solid var(--ccg-card-border);
  box-shadow: none;
  cursor: default;
}

.ccg-accent { color: var(--ccg-accent); }

/* --- what the page does ------------------------------------------------------
 *
 * The live site's behaviour, and the same rule as everything above it: not one
 * duration and not one curve is written here. Each is a `var(--ccg-t-*)` holding
 * a whole measured `transition` value, emitted into the document from ccg.yaml,
 * so a check reads what a visitor is served rather than what this file says.
 */

/* --- the reveal --------------------------------------------------------------
 *
 * THE HIDING IS CONDITIONAL AND THE SHOWING IS NOT. The transition sits on the
 * element unconditionally; the `opacity: 0` sits behind `[data-ccg-motion="on"]`,
 * an attribute one inline line in the document head sets and nothing else does. A
 * visitor whose browser runs no scripts gets the page at full opacity.
 */
[data-ccg-reveal] { transition: var(--ccg-t-reveal); }
[data-ccg-motion="on"] [data-ccg-reveal] { opacity: 0; }
[data-ccg-motion="on"] [data-ccg-reveal="in"] { opacity: 1; }

/* A reader who arrives by focus, or by the browser's own find, rather than by
   scrolling. */
[data-ccg-motion="on"] [data-ccg-reveal]:focus-within { opacity: 1; }

/* Print. There is no viewport to arrive in and no scrolling to do. */
@media print {
  [data-ccg-motion="on"] [data-ccg-reveal] { opacity: 1; transition: none; }
}

/* --- the hover set -----------------------------------------------------------
 *
 * Each rule below names the live element the value was read from. The mapping is
 * per element and it is the deliverable, not a decoration.
 *
 * FIFTEEN VALUES NOW LAND WHERE TWELVE WERE MEASURED AND FOUR HAD NOWHERE TO GO.
 * The arrow, the node circle, its photograph and its label were recorded in
 * ccg.yaml as having no carrier in this theme; the composition this cycle builds
 * renders all four, so all four are applied here.
 */

/* The header's menu: 50 elements on the live site carry `all 0.2s ease`. */
.ccg-nav__list,
.ccg-nav__sublist,
.ccg-nav__link { transition: var(--ccg-t-all); }

/* `header.mainHeader` and the 9 `li.menu-item` inside the sub-menus. */
.ccg-header,
.ccg-nav__sublist .ccg-nav__item { transition: var(--ccg-t-opacity); }

/* --- FOUR FAMILIES THAT MOVED AND NOTHING ELSE ------------------------------
 *
 * Until this change each of the four rules below set `transform` and only
 * `transform`, so a card answered a pointer by sliding six pixels and keeping
 * every edge it had. The live site answers with the edge: measured 17.09.2026 at
 * 1920 on https://www.consulting-concept.de/ with a dispatched pointer move, the
 * border and the shadow both change on all four, and the values are in ccg.yaml
 * beside the lifts, each read rest -> hover off the element it belongs to.
 *
 * THE REST STATES WERE ALREADY EXACT AND ARE NOT TOUCHED. The same run read ours
 * at rest against theirs at rest on all four and they agree to the value; what
 * the migration lost was the hover alone.
 *
 * `:focus-within` TAKES THE SAME VALUES AS `:hover` HERE, which is the shape the
 * five families that already did this use. It is not a claim about a keyboard
 * visitor's experience: what it says is that the two states paint alike.
 */

/* `div.ccg-warum-card`, three elements. */
.ccg-card { transition: var(--ccg-t-card); }
.ccg-card:hover,
.ccg-card:focus-within {
  transform: translateY(var(--ccg-hover-lift-card));
  border-color: var(--ccg-hover-card-border);
  box-shadow: var(--ccg-hover-card-shadow);
}

/* `a.ccg-home-svc-card`, five elements. THE BORDER IS NOT IN THIS RULE AND THAT
   IS THE READING: the live card's border is opaque purple at rest and stays
   exactly that under the pointer, so the only thing that moves is the shadow -
   three layers, the middle of which is a 1 px ring rather than a shadow. Its
   transition names `transform` and `box-shadow` and no border-color, on the live
   element and here, which is the same fact said in the other file. */
.ccg-service { transition: var(--ccg-t-service); }
.ccg-service:hover,
.ccg-service:focus-within {
  transform: translateY(var(--ccg-hover-lift-service));
  box-shadow: var(--ccg-hover-service-shadow);
}

/* `div.ccg-home-svc-thumb` and `svg.ccg-home-svc-icon`, ten elements, on the one
   curve in the set that overshoots. */
.ccg-service__photo,
.ccg-service__icon { transition: var(--ccg-t-overshoot); }
.ccg-service:hover .ccg-service__photo,
.ccg-service:focus-within .ccg-service__photo {
  transform: scale(var(--ccg-hover-zoom-photo)) rotate(var(--ccg-hover-turn-photo));
}

/* `a.ccg-news-card`, six elements, and `h3.ccg-news-card-title` inside them.
   Both carry something on this page for the first time: the section renders its
   six teasers rather than one line saying there are none. */
.ccg-teaser { transition: var(--ccg-t-teaser); }
.ccg-teaser:hover,
.ccg-teaser:focus-within {
  transform: translateY(var(--ccg-hover-lift-teaser));
  border-color: var(--ccg-hover-teaser-border);
  box-shadow: var(--ccg-hover-teaser-shadow);
}

.ccg-teaser__title { transition: var(--ccg-t-title); }
.ccg-teaser:hover .ccg-teaser__title,
.ccg-teaser:focus-within .ccg-teaser__title { color: var(--ccg-accent); }

/* `a.ccg-home-svc-cta-pill`, one element, and the archive pill that repeats it. */
.ccg-button,
.ccg-archive-button,
.ccg-ghost-button { transition: var(--ccg-t-button); }
a.ccg-button:hover, a.ccg-button:focus-visible,
a.ccg-archive-button:hover, a.ccg-archive-button:focus-visible,
a.ccg-ghost-button:hover, a.ccg-ghost-button:focus-visible { transform: translateY(var(--ccg-hover-lift-button)); }

/* `span.arrow`, 14 elements on the live page. */
.ccg-arrow { transition: var(--ccg-t-arrow); }
.ccg-service:hover .ccg-arrow,
.ccg-teaser:hover .ccg-arrow,
a.ccg-button:hover .ccg-arrow,
a.ccg-archive-button:hover .ccg-arrow,
a.ccg-job-card:hover .ccg-arrow { transform: translateX(4px); }

/* `div.cn-circle`, `div.cn-circle > img` and `span.cn-label`, six each. */
.ccg-branche__circle { transition: var(--ccg-t-node); }
.ccg-branche:hover { z-index: 15; }
.ccg-branche:hover .ccg-branche__circle,
.ccg-branche:focus-within .ccg-branche__circle {
  transform: scale(var(--ccg-hover-scale-node));
  box-shadow: var(--ccg-branchen-node-shadow-hover);
}

.ccg-branche__image { transition: var(--ccg-t-nodeimg); }
.ccg-branche:hover .ccg-branche__image,
.ccg-branche:focus-within .ccg-branche__image {
  transform: scale(var(--ccg-hover-scale-node-image));
  filter: var(--ccg-branchen-node-filter-hover);
}

.ccg-branche__label { transition: var(--ccg-t-label); }
.ccg-branche:hover .ccg-branche__label,
.ccg-branche:focus-within .ccg-branche__label { color: var(--ccg-accent); }

.ccg-branche__ring { transition: var(--ccg-t-ring); }
.ccg-branche:hover .ccg-branche__ring,
.ccg-branche:focus-within .ccg-branche__ring {
  border-color: var(--ccg-branchen-node-ring-hover);
  animation: var(--ccg-branchen-node-ring-motion);
}

@keyframes cnNodeRingRotate { to { transform: rotate(360deg); } }

.ccg-branche__tooltip { transition: var(--ccg-t-tooltip); }
.ccg-branche:hover .ccg-branche__tooltip,
.ccg-branche:focus-within .ccg-branche__tooltip { opacity: 1; visibility: visible; }
.ccg-branche--1:hover .ccg-branche__tooltip { transform: translateX(-50%) translateY(0); }
.ccg-branche--2:hover .ccg-branche__tooltip,
.ccg-branche--3:hover .ccg-branche__tooltip { transform: translateY(-50%) translateX(0); }
.ccg-branche--4:hover .ccg-branche__tooltip { transform: translateX(-50%) translateY(0); }
.ccg-branche--5:hover .ccg-branche__tooltip,
.ccg-branche--6:hover .ccg-branche__tooltip { transform: translateY(-50%) translateX(0); }

/* `a.ccg-job-card`, one element. Its shadow carries an inset highlight at rest
   and the live element moves that too, .18 -> .22, so the hover value carries
   both layers rather than only the outer one. */
.ccg-job-card { transition: var(--ccg-t-jobcard); }
a.ccg-job-card:hover,
a.ccg-job-card:focus-visible {
  transform: translateY(var(--ccg-hover-lift-job));
  border-color: var(--ccg-hover-job-border);
  box-shadow: var(--ccg-hover-job-shadow);
}

/* --- the inner pages ---------------------------------------------------------
 *
 * WHAT AN INNER PAGE IS MADE OF, AND WHY IT IS NOT THE HOME PAGE'S MODULAR SHAPE.
 * The home page has ten heterogeneous sections and one template per section. The
 * five service pages are one family: the same six components in the same order on
 * all five, differing only in text and in the one section each adds. So they get
 * one page template driven by the page's own front matter, and the six components
 * are partials it includes. That is the measurement made structural - a sixth
 * service page is a content file and no code at all.
 *
 * THE FRAME EVERY INNER SECTION SHARES. A section runs the full width of the
 * window and carries its own ground; inside it a port supplies the 128px of top
 * and bottom spacing every section was measured with; inside that the 1200 rail;
 * inside that a twelve-column row. The row is 1240 wide - it carries -20 on each
 * side of the rail - and every column is padded 20, which is what puts a column's
 * content at x = 360 rather than at x = 380. Both numbers are the live row's.
 */

/* An inner page lays out full-bleed sections, so `main` supplies no spacing of
   its own - the same seam `ccg--modular` uses on the home page, for the same
   reason and set by the page rather than by the template's name. */
.ccg--inner .ccg-main { padding-bottom: 0; }

.ccg-sv { position: relative; z-index: 2; overflow: hidden; }

.ccg-sv__port {
  position: relative;
  z-index: 3;
  padding: var(--ccg-section-space) 0;
}

/* The row and its columns. Only the two splits the family uses are declared;
   a third would be a column nothing renders.

   `.ccg-lt-row` IS NOT THIS RULE AND IS NOT GOING TO BECOME IT. The Leistung
   family lays out on a row that reproduces this one - same negative gutter, same
   column padding, 66.6667 and 33.3333 in both - and it stays a second rule on
   purpose. The whole argument, property by property, is written where that rule
   is declared. What the two do share is `--ccg-sv-gutter`: 20 px, read on both
   families, shared as the measurement it is rather than as a rule. */
.ccg-sv__row {
  display: flex;
  align-items: center;
  margin: 0 calc(-1 * var(--ccg-sv-gutter));
}

.ccg-sv__col { padding: 0 var(--ccg-sv-gutter); }
.ccg-sv__col--8 { width: 66.6667%; }
.ccg-sv__col--4 { width: 33.3333%; display: flex; align-items: center; }

/* The waves, as the live sections carry them: a full-bleed image at the edge of
   the section, under the port and over the section's own mesh. The hero's
   particle layer sits above it, which is why the layer's z-index is higher and
   not because a particle is more important than a wave. */
.ccg-sv__wave {
  position: absolute;
  left: 0;
  right: 0;
  width: 100%;
  height: var(--ccg-wave-hero);
  z-index: 1;
  pointer-events: none;
  display: block;
}
.ccg-sv__wave--bottom { bottom: 0; }
.ccg-sv__wave--top { top: 0; }

/* The dot field. It is the section's own layer rather than a repeat, for the
   reason the home page's is: repeating it puts a seam in the middle of the
   section. `data-ccg-parallax` is the shift rate the live layer carries. */
.ccg-sv__dots {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-repeat: no-repeat;
}
/* THE VERTICAL ANCHOR IS THE TOP, LIKE ITS TWO SIBLINGS, AND THAT IS WHAT THE
   LIVE LAYER PAINTS. js/ccg.js keeps each layer's own anchor and adds the shift to
   it, so a layer anchored `50%` is written `calc(50% + Npx)`. Measured at 1920 on
   26 August 2026, this layer's box is 1920 x 1091.8 over an image of 1920 x 1800,
   so `50%` is -354 px and the layer landed 354 px above the live one, which
   computes to `50% 210px` on /single-service/ and `50% 185px` on
   /lohnabrechnung/. Anchored at the top the same handler writes the bare pixel
   value the live handler writes, and the two computed values are identical.
   The horizontal `50%` is the live value and is kept; the image is exactly as
   wide as the box, so it moves nothing. */
.ccg-sv__dots--cover { background-size: cover; background-position: 50% 0; }

/* --- the service hero -------------------------------------------------------
 *
 * FOUR LAYERS, AND THE ORDER IS THE MEASUREMENT. The photograph is its own
 * element inside a wrapper that carries the blur and the oversize, so that
 * `[data-ccg-parallax]` stays an element with no filter on it - test/motion.sh
 * reads that at twenty scroll positions and requires it, because the live site
 * never changes a filter while scrolling. Then the mesh as the section's own
 * ::before, then the wave, then the particle layer, then the content.
 */
.ccg-sv-hero {
  background: var(--ccg-sv-hero-bg);
  color: var(--ccg-hero-heading);
  isolation: isolate;
}

/* THE HERO RESERVES THE HEADER, AND ONLY A SHORT WINDOW CAN SEE IT.
 *
 * This rule read `padding: 0` and `align-items: center` until 14.09.2026. A
 * centred flex item overflows BOTH ends of a box it does not fit, so on a window
 * shorter than the hero's own content the first line of the page was drawn above
 * the header. Measured on /about at 390 wide against a header ending at 70, with
 * the content 812.55 px tall: the content box began at y 0 at every window height
 * of 812 or less - 568, 667, 736 and 812 alike - so the `h1` started at 44.19,
 * 25.81 px inside the header band, and the eyebrow above it at 1.00, 69 px inside
 * it and drawn across the CCG mark. An iPhone SE is 375 x 667 and sits squarely
 * inside that band. The position is `44.19 + max(0, winH - 812.55) / 2`, so the
 * plateau ends at 813 and not at 844.
 *
 * THE TWO DECLARATIONS ARE ONE MOVE AND NEITHER WORKS ALONE.
 *
 * `safe center` is what stops the overflow going upwards: where the item does not
 * fit, the alignment falls back to `start` instead of centring and spilling over
 * both edges. `center` on its own would keep the negative offset; a reserve on
 * its own would be centred away with everything else.
 *
 * The padding is the reserve, and it is SYMMETRIC, which is the whole reason the
 * desktop does not move. Symmetric padding does not shift a centred item: with a
 * content height C in a box of height H the item's top is (H - C - 2P)/2 + P =
 * (H - C)/2, which is where it was with no padding at all. Measured against HEAD
 * on /about and on the five service pages: at 1920x1080 the item's top is 306.67
 * before and after, at 1440x900 it is 216.67 before and after. The rule changes
 * the page only where `2P + C` exceeds the window - that is, only on a screen too
 * short to hold the hero - which is the defect and nothing else.
 *
 * THE RESERVE IS THE HEADER PLUS ONE UNIT OF THE BAND'S OWN SECTION SPACE, which
 * is the reserve `lay-out-the-inner-families-for-a-phone` chose for the same
 * clearance one band down, and both terms are tokens this file already reads: 198
 * where `--ccg-section-space` is 128 and 134 where it is 64. No number is
 * introduced here.
 *
 * AND IT REACHES `/about`, WHICH THE `:not(.ccg-ab-hero)` GUARD BELOW DOES NOT.
 * That guard is on eight rules - six in the 1024 band and two at 640, counted on
 * 14.09.2026 - and it stays on all eight: they rebuild the hero's row, its
 * columns, its graphic and its type scale, and /about's other seven sections are
 * still not laid out for a phone, which is the reason that change gave for the
 * exclusion and it has not changed. This rule is not one of them - it is the
 * shared construction, asked of every hero at every width - and what it fixes on
 * /about is the one thing the exclusion was never meant to keep.
 *
 * THE FIRST OF THE EIGHT IS THIS SAME ELEMENT AND IT OVERRIDES BOTH DECLARATIONS
 * BELOW 1025, which is why the five service heroes do not move at any narrow
 * width: `.ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv__port` sets `align-items:
 * flex-start` and its own `padding` shorthand there. Measured on all five at 375
 * x 667, 390 x 568/667/736/812/844/932/1180, 393 x 852, 641 x 844 and 1024 x 768:
 * the first line of each is at exactly the same y before and after.
 *
 * WHERE THEY DO MOVE is a short window at a WIDE width, which no rule in this
 * file had ever reserved anything for. Measured on /single-service at 1180 x 667:
 * the first line was at y 98.55 and is at 179.71. That is the same repair on a
 * geometry nobody had looked at, and it is declared in this change's design.md
 * rather than left to be found.
 */
.ccg-sv-hero .ccg-sv__port {
  padding: calc(var(--ccg-header-height) + var(--ccg-section-space)) 0;
  min-height: 100vh;
  display: flex;
  align-items: safe center;
}

.ccg-sv-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  filter: var(--ccg-sv-hero-photo-filter);
  transform: scale(var(--ccg-sv-hero-photo-scale));
  transform-origin: center;
}

/* THE PHOTOGRAPH IS DRAWN AT ITS OWN SIZE, ANCHORED TOP LEFT, WHICH IS WHAT THE
 * ORIGINAL DOES WITH IT. Measured on 2026-09-02 at 1920 on all five service
 * pages of https://www.consulting-concept.de/: the layer is 1958.4 x 1101.6 at
 * (-19.2, -10.8), the file behind it is `hero_testimonials.jpg` at 1920 x 1200,
 * and it is painted at `background-size: auto`, `background-position: 0% <shift>`
 * - so the original shows the top 1101.6 px of the photograph at 1:1 and crops
 * the rest.
 *
 * This was `cover` at `50% 50%`, which scales the same file up to fill the layer
 * and crops it from the middle instead. The file is not the difference: it is
 * byte-identical to the original's, sha256
 * 9dc49d92e27154d971fee4053ac89a7a25a320fffad00e6459740d22b628cadb on both
 * sides. The photograph standing a little differently is this rule.
 *
 * `0%` AND NOT `left`, `0` AND NOT `50%`, both deliberately. js/ccg.js reads the
 * vertical component as the parallax's anchor and treats a nought as the case
 * where the original's bare pixel write is the same offset, which is what the
 * original's own handler writes here - it was at `0% 3px` at scroll nought. */
.ccg-sv-hero__photo {
  position: absolute;
  left: var(--ccg-sv-hero-photo-bleed-x);
  right: var(--ccg-sv-hero-photo-bleed-x);
  top: var(--ccg-sv-hero-photo-bleed-y);
  bottom: var(--ccg-sv-hero-photo-bleed-y);
  background-repeat: no-repeat;
  background-size: auto;
  background-position: 0% 0;
}

.ccg-sv-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--ccg-sv-hero-mesh);
  filter: var(--ccg-sv-hero-mesh-blur);
  animation: var(--ccg-sv-hero-mesh-motion);
}

.ccg-sv-hero__particles {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}

/* TWO RENDERINGS, ONE DRAWN. `partials/inner/graphics/particles.html.twig`
   serves the 1600x900 field and a portrait 400x1000 one; `preserveAspectRatio`
   is an attribute and cannot be set from a stylesheet - measured in three
   spellings during `repair-the-mobile-home-page` - so the choice has to be which
   element exists rather than how one of them fits. The portrait one is hidden
   here and turned on in the `max-width: 1024px` block, which is where this
   element's box stops being the window and becomes twice its height: measured on
   11.09.2026, 1025 x 800 gives a box of 1025 x 800 and 1024 x 800 gives
   1024 x 2152.5, because the hero stacks. */
.ccg-sv-hero__particles .ccg-sv-particles--tall { display: none; }

.ccg-sv-hero__particles svg {
  width: 100%;
  height: 100%;
  display: block;
  filter: var(--ccg-sv-particles-filter);
}

.ccg-sv-hero__text {
  max-width: var(--ccg-sv-hero-text-width);
  transform: translateY(var(--ccg-sv-hero-text-lift));
}

.ccg-sv-hero__label {
  display: inline-block;
  font-size: var(--ccg-sv-hero-label-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-hero-label-tracking);
  color: var(--ccg-sv-hero-label-color);
  text-transform: uppercase;
  margin-bottom: var(--ccg-sv-hero-label-space);
}

.ccg-sv-hero__title {
  margin: 0 0 var(--ccg-sv-hero-h1-space);
  line-height: 1;
  letter-spacing: var(--ccg-sv-hero-h1-tracking);
  color: var(--ccg-hero-heading);
}

.ccg-sv-hero__setup {
  display: block;
  font-size: var(--ccg-sv-hero-setup-size);
  font-weight: var(--ccg-sv-hero-setup-weight);
  letter-spacing: var(--ccg-sv-hero-setup-tracking);
  opacity: var(--ccg-sv-hero-setup-opacity);
  line-height: 1;
  margin-bottom: var(--ccg-sv-hero-setup-space);
}

/* The punch line and the rule that is drawn under it once. The rule ends at 80
   per cent of the line box rather than at its text, which is what the live
   keyframe says and what the 576px against a 720px block measures. */
.ccg-sv-hero__punch {
  display: block;
  position: relative;
  font-size: var(--ccg-sv-hero-punch-size);
  font-weight: var(--ccg-sv-hero-punch-weight);
  line-height: var(--ccg-sv-hero-punch-line);
  letter-spacing: var(--ccg-sv-hero-h1-tracking);
}

.ccg-sv-hero__punch::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: var(--ccg-sv-hero-rule-drop);
  height: var(--ccg-sv-hero-rule-height);
  width: 0;
  border-radius: 2px;
  background: var(--ccg-sv-hero-rule-fill);
  box-shadow: var(--ccg-sv-hero-rule-glow);
  animation: var(--ccg-sv-hero-rule-motion);
}

@keyframes bhUnderline { to { width: 80%; opacity: 1; } }

@keyframes bhMeshDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  25%      { transform: translate(-3%, 2%) scale(1.05); }
  50%      { transform: translate(2%, -2%) scale(0.97); }
  75%      { transform: translate(-1.5%, -1%) scale(1.03); }
}

@keyframes bhShimmerPulse {
  0%, 100% { opacity: 0.3; transform: scale(0.85); }
  50%      { opacity: 0.95; transform: scale(1.4); }
}

.ccg-sv-hero__sub {
  font-size: var(--ccg-sv-hero-sub-size);
  line-height: var(--ccg-sv-hero-sub-line);
  color: var(--ccg-sv-hero-sub-color);
  max-width: var(--ccg-sv-hero-sub-width);
  margin: 0 0 var(--ccg-sv-hero-sub-space);
}

.ccg-sv-hero__anim {
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  position: relative;
  z-index: 5;
  transform: scale(var(--ccg-sv-hero-anim-scale));
  transform-origin: center;
  overflow: visible;
}

.ccg-sv-hero__anim svg {
  width: 100%;
  height: auto;
  display: block;
  opacity: var(--ccg-sv-hero-anim-opacity);
  filter: var(--ccg-sv-hero-anim-filter);
}

/* The five scattered dots inside the hero graphic, each pulsing on its own
   offset. The offsets are the live sheet's nth-child rules. */
.ccg-sv-hero__anim .ccg-sv-shimmer {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-sv-shimmer-motion);
}
.ccg-sv-hero__anim .ccg-sv-shimmer:nth-child(odd) { animation-delay: 0.7s; }
.ccg-sv-hero__anim .ccg-sv-shimmer:nth-child(3n) { animation-delay: 1.5s; }
.ccg-sv-hero__anim .ccg-sv-shimmer:nth-child(5n) { animation-delay: 2.3s; }

/* --- the call to action, in both its sizes ----------------------------------
 *
 * ONE CONSTRUCTION AND TWO SIZES, which is what the live page has: the hero's
 * pill and the band's pill differ in font size and padding and in nothing else.
 * The sheen is a ::before that crosses the pill on hover; it is a decoration the
 * live pill carries and it is reproduced rather than left out.
 */
.ccg-sv-pill {
  font-family: var(--ccg-font-body);
  display: inline-flex;
  align-items: center;
  gap: var(--ccg-sv-pill-gap);
  padding: var(--ccg-sv-pill-padding);
  background: var(--ccg-sv-pill-fill);
  color: var(--ccg-sv-pill-text);
  border-radius: var(--ccg-sv-pill-radius);
  font-weight: var(--ccg-sv-pill-weight);
  font-size: var(--ccg-sv-pill-size);
  text-decoration: none;
  box-shadow: var(--ccg-sv-pill-shadow);
  transition: var(--ccg-t-pill);
  overflow: hidden;
  position: relative;
  isolation: isolate;
}

.ccg-sv-pill::before {
  content: "";
  position: absolute;
  top: 0;
  left: -120%;
  width: 80%;
  height: 100%;
  background: var(--ccg-sv-pill-sheen);
  transform: skewX(-20deg);
  transition: var(--ccg-t-sheen);
}

.ccg-sv-pill:hover, .ccg-sv-pill:focus-visible {
  transform: translateY(var(--ccg-sv-pill-lift));
  box-shadow: var(--ccg-sv-pill-shadow-hover);
  gap: var(--ccg-sv-pill-gap-hover);
  text-decoration: none;
}
.ccg-sv-pill:hover::before, .ccg-sv-pill:focus-visible::before { left: 120%; }

.ccg-sv-pill__arrow { font-size: var(--ccg-sv-pill-arrow-size); transition: var(--ccg-t-arrow); }
.ccg-sv-pill:hover .ccg-sv-pill__arrow,
.ccg-sv-pill:focus-visible .ccg-sv-pill__arrow { transform: translateX(var(--ccg-sv-pill-arrow-shift)); }

.ccg-sv-pill--large {
  font-size: var(--ccg-sv-pill-large-size);
  padding: var(--ccg-sv-pill-large-padding);
}

/* A pill whose page does not exist. It keeps its shape so the section still
   reads, and loses the gradient, the shadow and the sheen so it does not read as
   a control that works - the same distinction the navigation, the footer and the
   home page's own pills already make, and the reason `test/composition.sh`'s
   paint case exists: twelve marked controls shipped for one round painting the
   accent colour of a working one, with the pointer shape as the only difference.
   Both pills on a service page point at the contact page, which this change
   builds, so on a complete installation neither is in this state - and if the
   contact page is ever removed, this is what says so. */
.ccg-sv-pill--missing {
  background: var(--ccg-card-bg);
  color: var(--ccg-nav-missing-color);
  border: 1px solid var(--ccg-card-border);
  box-shadow: none;
  cursor: default;
}
.ccg-sv-pill--missing::before { display: none; }

/* --- the section header, four times a page ---------------------------------- */

.ccg-sv-head {
  text-align: center;
  margin: 0 auto var(--ccg-sv-head-space);
  max-width: var(--ccg-sv-head-width);
}

.ccg-sv-head__label {
  display: block;
  font-size: var(--ccg-sv-head-label-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-head-label-tracking);
  color: var(--ccg-accent);
  text-transform: uppercase;
  margin-bottom: var(--ccg-sv-head-label-space);
}

.ccg-sv-head__title {
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-sv-head-title-size);
  font-weight: var(--ccg-sv-head-title-weight);
  line-height: var(--ccg-sv-head-title-line);
  letter-spacing: var(--ccg-sv-head-title-tracking);
  color: var(--ccg-heading);
  margin: 0 auto var(--ccg-sv-head-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  max-width: var(--ccg-sv-head-title-width);
}

.ccg-sv-head__sub {
  font-size: var(--ccg-sv-head-sub-size);
  line-height: var(--ccg-sv-head-sub-line);
  color: var(--ccg-body-quiet-2);
  margin: var(--ccg-sv-head-sub-space) auto 0;
  max-width: var(--ccg-sv-head-sub-width);
}

/* --- the feature grid ------------------------------------------------------- */

.ccg-sv-features { background: var(--ccg-sv-features-bg); }

.ccg-sv-feature-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--ccg-sv-feat-gap);
}

.ccg-sv-feature {
  position: relative;
  background: var(--ccg-page);
  border-radius: var(--ccg-sv-feat-radius);
  padding: var(--ccg-sv-feat-padding);
  border: 1px solid var(--ccg-sv-feat-border);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
  transition: var(--ccg-t-feature);
  text-align: left;
  overflow: hidden;
}

.ccg-sv-feature::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--ccg-sv-feat-bar-height);
  background: var(--ccg-sv-feat-bar-fill);
  border-radius: var(--ccg-sv-feat-radius) var(--ccg-sv-feat-radius) 0 0;
  opacity: var(--ccg-sv-feat-bar-opacity);
}

.ccg-sv-feature:hover, .ccg-sv-feature:focus-within {
  transform: translateY(var(--ccg-sv-feat-lift));
  border-color: var(--ccg-sv-feat-border-hover);
  box-shadow: var(--ccg-sv-feat-shadow-hover);
}

.ccg-sv-feature__tile {
  width: 100%;
  aspect-ratio: 1 / 1;
  margin-bottom: var(--ccg-sv-feat-tile-space);
  border-radius: var(--ccg-sv-feat-tile-radius);
  overflow: hidden;
  background: var(--ccg-sv-feat-tile-fill);
  border: 1px solid var(--ccg-sv-feat-tile-border);
  position: relative;
}

.ccg-sv-feature__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: var(--ccg-t-featureimg);
}

.ccg-sv-feature:hover .ccg-sv-feature__image,
.ccg-sv-feature:focus-within .ccg-sv-feature__image {
  transform: scale(var(--ccg-sv-feat-image-scale-hover));
  filter: var(--ccg-sv-feat-image-filter-hover);
}

.ccg-sv-feature__title {
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-sv-feat-title-size);
  font-weight: var(--ccg-sv-feat-title-weight);
  line-height: var(--ccg-sv-feat-title-line);
  letter-spacing: var(--ccg-sv-feat-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-feat-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-sv-feature__text {
  font-size: var(--ccg-sv-feat-desc-size);
  line-height: var(--ccg-sv-feat-desc-line);
  color: var(--ccg-body-quiet-2);
  margin: 0;
}


/* --- the industries constellation -------------------------------------------
 *
 * The block is 1200 wide and 632 tall with the line graphic stretched over it.
 * A card is 347x217 with 64 px of top padding, and the 64 is not a taste: it is
 * what clears the photograph, which hangs 45 px above the card's own top edge
 * (120 tall, lifted 110, on a card dropped 55).
 */
.ccg-sv-cons {
  position: relative;
  max-width: var(--ccg-measure);
  margin: 0 auto;
  padding: var(--ccg-sv-cons-pad);
}

.ccg-sv-cons__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

.ccg-sv-cons__line {
  stroke-dashoffset: 1000;
  animation: var(--ccg-sv-cons-line-motion);
}
.ccg-sv-cons__line:nth-child(1) { animation-delay: 0.3s; }
.ccg-sv-cons__line:nth-child(2) { animation-delay: 0.5s; }
.ccg-sv-cons__line:nth-child(3) { animation-delay: 0.7s; }
.ccg-sv-cons__line:nth-child(4) { animation-delay: 0.9s; }
.ccg-sv-cons__line:nth-child(5) { animation-delay: 1.1s; }
.ccg-sv-cons__line:nth-child(6) { animation-delay: 1.3s; }
.ccg-sv-cons__line:nth-child(7) { animation-delay: 1.5s; }

@keyframes bhCstlDraw { to { stroke-dashoffset: 0; } }
@keyframes bhCstlFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
@keyframes bhCstlRingRotate { to { transform: rotate(360deg); } }

.ccg-sv-cons__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ccg-sv-cons-gap-row) var(--ccg-sv-cons-gap-col);
  padding: var(--ccg-sv-cons-grid-pad);
}

.ccg-sv-cons__card {
  position: relative;
  text-align: center;
  background: var(--ccg-sv-cons-card-fill);
  border: 1px solid var(--ccg-sv-cons-card-border);
  border-radius: var(--ccg-sv-cons-card-radius);
  padding: var(--ccg-sv-cons-card-padding);
  margin-top: var(--ccg-sv-cons-card-drop);
  box-shadow: var(--ccg-sv-cons-card-shadow);
  transition: var(--ccg-t-cons);
  animation: var(--ccg-sv-cons-card-motion);
  overflow: visible;
}

/* Six cards, six offsets, so the row does not rise and fall as one block. The
   live sheet writes six numbered classes; here it is the child position, because
   a class a template builds out of a loop index never appears in a template as a
   literal and test/home.sh's class inventory - correctly - reports that as a rule
   with nothing to land on. The position is the same fact without the seam. */
.ccg-sv-cons__card:nth-child(1) { animation-delay: 0s; }
.ccg-sv-cons__card:nth-child(2) { animation-delay: 0.6s; }
.ccg-sv-cons__card:nth-child(3) { animation-delay: 1.2s; }
.ccg-sv-cons__card:nth-child(4) { animation-delay: 1.8s; }
.ccg-sv-cons__card:nth-child(5) { animation-delay: 2.4s; }
.ccg-sv-cons__card:nth-child(6) { animation-delay: 3s; }

.ccg-sv-cons__card:hover, .ccg-sv-cons__card:focus-within {
  border-color: var(--ccg-sv-cons-card-border-hover);
  box-shadow: var(--ccg-sv-cons-card-shadow-hover);
  animation-play-state: paused;
}

.ccg-sv-cons__card::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--ccg-sv-cons-bar-inset);
  right: var(--ccg-sv-cons-bar-inset);
  height: var(--ccg-sv-cons-bar-height);
  background: var(--ccg-sv-cons-bar-fill);
  border-radius: 2px 2px 0 0;
  opacity: var(--ccg-sv-cons-bar-opacity);
  z-index: 6;
  pointer-events: none;
}

.ccg-sv-cons__card::after {
  content: attr(data-num);
  position: absolute;
  top: 14px;
  right: 18px;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-cons-num-size);
  font-weight: var(--ccg-sv-cons-num-weight);
  line-height: 1;
  letter-spacing: var(--ccg-sv-cons-num-tracking);
  color: transparent;
  -webkit-text-stroke: var(--ccg-sv-cons-num-stroke) var(--ccg-sv-cons-num-color);
  opacity: var(--ccg-sv-cons-num-opacity);
  z-index: 4;
  pointer-events: none;
  transition: var(--ccg-t-consnum);
}

.ccg-sv-cons__card:hover::after, .ccg-sv-cons__card:focus-within::after {
  opacity: var(--ccg-sv-cons-num-opacity-hover);
  transform: translateY(var(--ccg-sv-cons-num-lift-hover));
}

.ccg-sv-cons__ring {
  width: var(--ccg-sv-cons-photo-size);
  height: var(--ccg-sv-cons-photo-size);
  margin: var(--ccg-sv-cons-photo-rise) auto var(--ccg-sv-cons-photo-space);
  position: relative;
  border-radius: 50%;
  z-index: 6;
}

.ccg-sv-cons__ring::before {
  content: "";
  position: absolute;
  inset: var(--ccg-sv-cons-ring-inner);
  border-radius: 50%;
  border: var(--ccg-sv-cons-ring-inner-border);
  animation: var(--ccg-sv-cons-ring-inner-motion);
  pointer-events: none;
}

.ccg-sv-cons__ring::after {
  content: "";
  position: absolute;
  inset: var(--ccg-sv-cons-ring-outer);
  border-radius: 50%;
  border: var(--ccg-sv-cons-ring-outer-border);
  animation: var(--ccg-sv-cons-ring-outer-motion);
  pointer-events: none;
}

.ccg-sv-cons__photo {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  background-color: var(--ccg-sv-cons-photo-ground);
  box-shadow: var(--ccg-sv-cons-photo-shadow);
  transition: var(--ccg-t-consimg);
  position: relative;
  z-index: 1;
}

.ccg-sv-cons__card:hover .ccg-sv-cons__photo,
.ccg-sv-cons__card:focus-within .ccg-sv-cons__photo {
  transform: scale(var(--ccg-sv-cons-photo-scale-hover));
  box-shadow: var(--ccg-sv-cons-photo-shadow-hover);
}

.ccg-sv-cons__title {
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-sv-cons-title-size);
  font-weight: var(--ccg-sv-cons-title-weight);
  line-height: var(--ccg-sv-cons-title-line);
  letter-spacing: var(--ccg-sv-cons-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-cons-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  transition: var(--ccg-t-title);
}

.ccg-sv-cons__card:hover .ccg-sv-cons__title,
.ccg-sv-cons__card:focus-within .ccg-sv-cons__title { color: var(--ccg-accent); }

.ccg-sv-cons__text {
  font-size: var(--ccg-sv-cons-text-size);
  line-height: var(--ccg-sv-cons-text-line);
  color: var(--ccg-body-quiet-2);
  margin: 0;
}

/* --- the process chapters ---------------------------------------------------- */

.ccg-sv-process::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--ccg-sv-process-mesh);
}

/* THE CHAPTER TEXT IS CENTRED, AND IT IS CENTRED HERE RATHER THAN ON THE BODY.
   `getComputedStyle` on the live `.ccg-bh-chapters` reads `text-align: center`,
   measured at 1920 on 26 August 2026, and every inline thing inside a chapter
   inherits it: the label pill, the title and the paragraph. The meta row is a
   flex container, so its pills are unaffected and stay packed to the start -
   which is what the live row does too, measured at the same time: the pills of
   chapter 1 sit within 3.5 px of ours while the label pill sat 216 px apart,
   x = 727.8 live against x = 511.4 here.
   It was left out of the first build and the whole of three chapters on five
   pages read left-aligned against a centred original. */
.ccg-sv-chapters {
  text-align: center;
  max-width: var(--ccg-sv-chapters-width);
  margin: 0 auto;
  padding: var(--ccg-sv-chapters-pad);
  display: flex;
  flex-direction: column;
  gap: var(--ccg-sv-chapters-gap);
  position: relative;
  z-index: 1;
}

.ccg-sv-chapter {
  position: relative;
  padding: var(--ccg-sv-chapter-pad);
  overflow: hidden;
  transition: var(--ccg-t-chapter);
}

/* The mirror. Odd chapters keep the body left and the decoration right; the even
   ones swap both, which is the whole of the alternation. The live sheet writes it
   as three numbered classes because it has exactly three chapters; the child
   position says the same thing for any number of them, and it says it without a
   class built from a loop index that no template contains as a literal. */
.ccg-sv-chapter:nth-child(even) { padding-left: 25%; padding-right: 8%; }

.ccg-sv-chapter:hover, .ccg-sv-chapter:focus-within {
  transform: translateX(var(--ccg-sv-chapter-shift-hover));
}

.ccg-sv-chapter__watermark {
  position: absolute;
  top: 50%;
  right: 5%;
  transform: translateY(-50%);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-chapter-mark-size);
  font-weight: var(--ccg-sv-chapter-mark-weight);
  line-height: var(--ccg-sv-chapter-mark-line);
  letter-spacing: var(--ccg-sv-chapter-mark-tracking);
  color: transparent;
  -webkit-text-stroke: var(--ccg-sv-chapter-mark-stroke) var(--ccg-sv-chapter-mark-color);
  opacity: var(--ccg-sv-chapter-mark-opacity);
  pointer-events: none;
  z-index: 0;
  transition: var(--ccg-t-watermark);
}

.ccg-sv-chapter:nth-child(even) .ccg-sv-chapter__watermark { right: auto; left: 5%; }

.ccg-sv-chapter:hover .ccg-sv-chapter__watermark,
.ccg-sv-chapter:focus-within .ccg-sv-chapter__watermark {
  opacity: var(--ccg-sv-chapter-mark-opacity-hover);
  -webkit-text-stroke-color: var(--ccg-sv-chapter-mark-color-hover);
}

.ccg-sv-chapter__decor {
  position: absolute;
  top: 30%;
  right: 18%;
  width: var(--ccg-sv-chapter-decor-size);
  height: var(--ccg-sv-chapter-decor-size);
  z-index: 0;
  pointer-events: none;
  opacity: var(--ccg-sv-chapter-decor-opacity);
}

.ccg-sv-chapter:nth-child(even) .ccg-sv-chapter__decor { right: auto; left: 18%; }

.ccg-sv-chapter__body {
  position: relative;
  z-index: 2;
  max-width: var(--ccg-sv-chapter-body-width);
}

.ccg-sv-chapter__label {
  display: inline-block;
  font-size: var(--ccg-sv-chapter-label-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-chapter-label-tracking);
  color: var(--ccg-accent);
  text-transform: uppercase;
  margin-bottom: var(--ccg-sv-chapter-label-space);
  padding: var(--ccg-sv-chapter-label-padding);
  background: var(--ccg-sv-chapter-label-fill);
  border: 1px solid var(--ccg-sv-chapter-label-border);
  border-radius: var(--ccg-sv-pill-radius);
}

.ccg-sv-chapter__title {
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-sv-chapter-title-size);
  font-weight: var(--ccg-sv-chapter-title-weight);
  line-height: var(--ccg-sv-chapter-title-line);
  letter-spacing: var(--ccg-sv-chapter-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-chapter-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  transition: var(--ccg-t-title);
}

.ccg-sv-chapter:hover .ccg-sv-chapter__title,
.ccg-sv-chapter:focus-within .ccg-sv-chapter__title { color: var(--ccg-accent); }

.ccg-sv-chapter__text {
  font-size: var(--ccg-sv-chapter-text-size);
  line-height: var(--ccg-sv-chapter-text-line);
  color: var(--ccg-body-quiet-2);
  margin: 0 0 var(--ccg-sv-chapter-text-space);
}

.ccg-sv-chapter__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ccg-sv-chapter-meta-gap);
}

.ccg-sv-chapter__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--ccg-sv-chapter-pill-gap);
  padding: var(--ccg-sv-chapter-pill-padding);
  font-size: var(--ccg-sv-chapter-pill-size);
  font-weight: var(--ccg-sv-chapter-pill-weight);
  color: var(--ccg-heading);
  background: var(--ccg-sv-chapter-pill-fill);
  border: 1px solid var(--ccg-sv-chapter-pill-border);
  border-radius: var(--ccg-sv-pill-radius);
  transition: var(--ccg-t-metapill);
}

/* A hover state design.md's map does not record. Read off the live rule set at
   the same time as the rest and written down here rather than left out. */
.ccg-sv-chapter__pill:hover {
  transform: translateY(var(--ccg-sv-chapter-pill-lift-hover));
  border-color: var(--ccg-sv-chapter-pill-border-hover);
}

.ccg-sv-chapter__glyph { display: inline-block; }

/* --- the closing band -------------------------------------------------------- */

.ccg-sv-band {
  background: var(--ccg-sv-band-bg);
  color: var(--ccg-hero-heading);
  padding: var(--ccg-sv-band-pad);
  isolation: isolate;
}

.ccg-sv-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--ccg-sv-band-mesh);
}

.ccg-sv-band .ccg-sv__wave--top { height: var(--ccg-sv-band-wave-height); }

.ccg-sv-band__wrap {
  position: relative;
  z-index: 1;
  text-align: center;
  max-width: var(--ccg-sv-head-width);
  margin: 0 auto;
  padding: var(--ccg-sv-band-wrap-pad);
}

.ccg-sv-band__title {
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-sv-band-title-size);
  font-weight: var(--ccg-sv-band-title-weight);
  line-height: var(--ccg-sv-band-title-line);
  letter-spacing: var(--ccg-sv-band-title-tracking);
  color: var(--ccg-hero-heading);
  margin: 0 auto var(--ccg-sv-band-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  max-width: var(--ccg-sv-band-title-width);
}

.ccg-sv-band__text {
  font-size: var(--ccg-sv-band-text-size);
  line-height: var(--ccg-sv-band-text-line);
  color: var(--ccg-sv-hero-sub-color);
  margin: 0 auto var(--ccg-sv-band-text-space);
  max-width: var(--ccg-sv-band-text-width);
}

/* A section the page names and the theme does not build. It is visible on
   purpose: a hole nobody can see is worse than one that says so. */
.ccg-sv-unbuilt {
  max-width: var(--ccg-measure);
  margin: 0 auto;
  padding: var(--ccg-sv-gutter);
  color: var(--ccg-nav-missing-color);
}

/* --- the inner pages, part two: /karriere/ -----------------------------------
 *
 * ITS OWN FAMILY, MEASURED AS ONE. The five service pages share a namespace, a
 * geometry and an animation set; `/karriere/` shares none of the three with them
 * and none with `/about/` either. Four components, all flat, and the page is
 * 3432 tall - the smallest on the site.
 *
 * WHAT IS NOT HERE IS AS MEASURED AS WHAT IS. No photograph, no wave, no dot
 * field, no parallax layer, no particle layer, and no mesh: every section's
 * `::before` on the live page computes to `content: none`, and not one element
 * anywhere on it carries an animation name other than `none`. So this block
 * declares no `::before` on a section, no `@keyframes` and no `animation`
 * property at all. The only motion the page has is the theme's shared reveal,
 * the sticky header, and the hover set below.
 *
 * THE FRAME IS THE SHARED ONE. `.ccg-sv` and `.ccg-sv__port` supply the
 * full-bleed section and the 128px top and bottom every section on this site was
 * measured with, and `.ccg-measure` supplies the 1200 rail. This page needs no
 * `.ccg-sv__row`: all four of its blocks are centred on the rail rather than
 * laid on the twelve-column grid, which is what puts the header band's 820 at
 * x = 550, both section headers' 680 at x = 620, the CTA box's 720 at x = 600
 * and the job frame's 1000 at x = 460.
 *
 * NOTHING HERE IS SHARED WITH `.ccg-sv-*` BEYOND THAT FRAME, and the temptation
 * was checked rather than resisted on taste. The button differs from the service
 * pill in padding, gap, fill, shadow and sheen; the section header differs from
 * `.ccg-sv-head` in width, label size, label colour, title size, title weight
 * and title tracking. Two of them would have had to render wrong for one rule to
 * serve both.
 *
 * THE CARD IS NOT A TWIN OF ANYTHING, AND THAT IS A CORRECTION. design.md said
 * `.ccg-ka-card` and the Sanierung card on `/strategieberatung/` were one
 * construction with the chip, 50 there against 48 here, as their only
 * difference. Both were re-measured on the live pages and that is not so. This
 * card is 284.39 x 242.48 padded `27.2 22.4`, chip 48, title 17.6 w800, text
 * 14.72; the Sanierung card is 351.73 x 229.2 padded `30.4 27.2`, chip 50,
 * title 17.92, text 14.88. What the two do share is radius 16, the
 * `rgba(27,42,74,.06) 0 10 30` shadow, the `rgba(91,47,212,.1)` border, the
 * chip's radius 12 and the `translateY(-6px)` hover - which is why they look
 * alike and why the summary got it wrong. Five card kinds on this site are a
 * white box with a chip, a title and a paragraph; all five are separate rule
 * sets, `data-ccg-card` on the element says which one is which, and a check
 * reads the separating properties off the rendered cards.
 */

/* --- the header band, 662 ---------------------------------------------------- */

.ccg-ka-band { background: var(--ccg-ka-band-bg); }

.ccg-ka-band__inner {
  max-width: var(--ccg-ka-band-width);
  margin: 0 auto;
  padding: var(--ccg-ka-band-pad);
  text-align: center;
}

.ccg-ka-band__label {
  display: inline-block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-band-label-size);
  font-weight: var(--ccg-ka-band-label-weight);
  letter-spacing: var(--ccg-ka-band-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-ka-band-label-color);
  margin-bottom: var(--ccg-ka-band-label-space);
}

/* THE ONLY H1 ON THIS SITE THAT IS NOT THE DISPLAY FACE. Every other one is
   Spartan; this is Inter 900 at 48/52.8 tracked -1.2, read off the live element.
   The descender pad is the original theme's own rule and not a decision here -
   see `--ccg-sv-heading-descender` in ccg.yaml - and it is 4.8 on this heading,
   which is a tenth of 48. */
.ccg-ka-band__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-band-title-size);
  line-height: var(--ccg-ka-band-title-line);
  font-weight: var(--ccg-ka-band-title-weight);
  letter-spacing: var(--ccg-ka-band-title-tracking);
  color: var(--ccg-ka-band-title-color);
  margin: 0 0 var(--ccg-ka-band-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

/* The emphasised run. `font-style: normal` because it is an `<em>` in the live
   markup and is not rendered italic there: the element carries the emphasis for
   a reader who gets no CSS, and the colour carries it for one who does. */
.ccg-ka-band__accent {
  font-style: normal;
  color: var(--ccg-ka-band-accent-color);
}

.ccg-ka-band__sub {
  max-width: var(--ccg-ka-band-sub-width);
  margin: 0 auto var(--ccg-ka-band-sub-space);
  font-size: var(--ccg-ka-band-sub-size);
  line-height: var(--ccg-ka-band-sub-line);
  color: var(--ccg-ka-band-sub-color);
}

/* --- the call to action, in the two colourways the page uses -----------------
 *
 * Both are 54.375 tall: 24 of line box inside 15.2 of padding top and bottom.
 * `--ccg-ka-btn-light-*` overrides the fill and the ink and nothing else, which
 * is why the purple button keeps the black shadow the gold one has rather than
 * a purple one - measured on the live element, not inferred from the rule.
 */
.ccg-ka-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--ccg-ka-btn-gap);
  padding: var(--ccg-ka-btn-padding);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-btn-size);
  font-weight: var(--ccg-ka-btn-weight);
  border-radius: var(--ccg-ka-btn-radius);
  background: var(--ccg-ka-btn-fill);
  color: var(--ccg-ka-btn-text);
  text-decoration: none;
  box-shadow: var(--ccg-ka-btn-shadow);
  transition: var(--ccg-t-karbtn);
}

.ccg-ka-btn:hover, .ccg-ka-btn:focus-visible {
  transform: translateY(var(--ccg-ka-btn-lift));
  box-shadow: var(--ccg-ka-btn-shadow-hover);
  text-decoration: none;
}

.ccg-ka-btn__arrow {
  display: inline-block;
  transition: var(--ccg-t-arrow);
}

.ccg-ka-btn:hover .ccg-ka-btn__arrow,
.ccg-ka-btn:focus-visible .ccg-ka-btn__arrow {
  transform: translateX(var(--ccg-ka-btn-arrow-shift));
}

.ccg-ka-btn--light {
  background: var(--ccg-ka-btn-light-fill);
  color: var(--ccg-ka-btn-light-text);
}

/* A route the running application cannot find. It says so rather than looking
   like a control that works - the same treatment `.ccg-sv-pill--missing` gets,
   for the same reason. */
.ccg-ka-btn--missing {
  cursor: not-allowed;
  opacity: var(--ccg-ka-btn-missing-opacity);
}

/* --- the section header, above the cards and above the job list --------------
 *
 * The sub-line is optional and its absence is the measurement: 82.67 tall over
 * the cards with no sub-line, 143.45 over the job list with one.
 */
.ccg-ka-head {
  max-width: var(--ccg-ka-head-width);
  margin: 0 auto var(--ccg-ka-head-space);
  text-align: center;
}

.ccg-ka-head__label {
  display: inline-block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-head-label-size);
  font-weight: var(--ccg-ka-head-label-weight);
  letter-spacing: var(--ccg-ka-head-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-ka-head-label-color);
  margin-bottom: var(--ccg-ka-head-label-space);
}

.ccg-ka-head__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-head-title-size);
  line-height: var(--ccg-ka-head-title-line);
  font-weight: var(--ccg-ka-head-title-weight);
  letter-spacing: var(--ccg-ka-head-title-tracking);
  color: var(--ccg-heading);
  margin: 0;
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-ka-head__sub {
  margin: var(--ccg-ka-head-sub-space) 0 0;
  font-size: var(--ccg-ka-head-sub-size);
  line-height: var(--ccg-ka-head-sub-line);
  color: var(--ccg-body-quiet);
}

/* --- the why-CCG cards, four across ------------------------------------------
 *
 * 1200 = 4 x 284.4 + 3 x 20.8, which is where the odd column width comes from:
 * the gap is 1.3rem and the columns are what is left.
 *
 * `--ccg-t-card` is the theme's existing token and is reused rather than
 * duplicated: the live declaration on this card is
 * `transform .4s cubic-bezier(.4,0,.2,1), box-shadow .4s, border-color .4s`,
 * which is that token exactly.
 */
.ccg-ka-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--ccg-ka-grid-gap);
}

.ccg-ka-card {
  background: var(--ccg-page);
  border: 1px solid var(--ccg-ka-card-border);
  border-radius: var(--ccg-ka-card-radius);
  padding: var(--ccg-ka-card-padding);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
  text-align: left;
  transition: var(--ccg-t-card);
}

.ccg-ka-card:hover, .ccg-ka-card:focus-within {
  transform: translateY(var(--ccg-ka-card-lift));
  border-color: var(--ccg-ka-card-border-hover);
  box-shadow: var(--ccg-ka-card-shadow-hover);
}

/* The chip. 48 here and 50 on the Sanierung card is the only difference between
   two otherwise identical constructions; see the note at the head of this
   block. The graphic inside it is 26 and takes its colour from the chip through
   `currentColor`, which is what the live markup does. */
.ccg-ka-card__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ccg-ka-chip-size);
  height: var(--ccg-ka-chip-size);
  border-radius: var(--ccg-ka-chip-radius);
  background: var(--ccg-ka-chip-fill);
  color: var(--ccg-ka-chip-ink);
  margin-bottom: var(--ccg-ka-chip-space);
}

.ccg-ka-card__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-card-title-size);
  line-height: var(--ccg-ka-card-title-line);
  font-weight: var(--ccg-ka-card-title-weight);
  letter-spacing: var(--ccg-ka-card-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ka-card-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-ka-card__text {
  margin: 0;
  font-size: var(--ccg-ka-card-text-size);
  line-height: var(--ccg-ka-card-text-line);
  color: var(--ccg-body-quiet);
}

/* --- the CTA box, 449 -------------------------------------------------------- */

.ccg-ka-cta { background: var(--ccg-ka-cta-bg); }

.ccg-ka-cta__box {
  max-width: var(--ccg-ka-cta-width);
  margin: 0 auto;
  text-align: center;
}

.ccg-ka-cta__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ka-cta-title-size);
  line-height: var(--ccg-ka-cta-title-line);
  font-weight: var(--ccg-ka-cta-title-weight);
  letter-spacing: var(--ccg-ka-cta-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ka-cta-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-ka-cta__text {
  max-width: var(--ccg-ka-cta-text-width);
  margin: 0 auto var(--ccg-ka-cta-text-space);
  font-size: var(--ccg-ka-cta-text-size);
  line-height: var(--ccg-ka-cta-text-line);
  color: var(--ccg-body-quiet);
}

/* --- the job frame, 1000 x 762 -----------------------------------------------
 *
 * A FRAME AND ITS CONTENT ARE TWO THINGS HERE ON PURPOSE. The box is the theme's
 * - white, 1px `rgba(91,47,212,.14)`, radius 16, shadow
 * `rgba(27,42,74,.08) 0 14 40`, and `overflow: hidden` so the embed's square
 * corners are cut by the radius rather than sticking out of it. What sits inside
 * it is the page's: today an iframe into a recruiting system this site does not
 * operate, and the same box holds a paragraph instead when the page names no
 * address. That seam is what lets a consent gate, a static list or a link out
 * replace the embed later without any of these values changing.
 *
 * 762 AND 760 ARE BOTH MEASURED. The frame is 762 because the embed is 760 and
 * the border is 1 on each side; the height is the live element's own attribute
 * and not a guess about how tall a job list is.
 */
.ccg-ka-frame {
  max-width: var(--ccg-ka-frame-width);
  margin: 0 auto;
  background: var(--ccg-page);
  border: 1px solid var(--ccg-ka-frame-border);
  border-radius: var(--ccg-ka-frame-radius);
  box-shadow: var(--ccg-ka-frame-shadow);
  overflow: hidden;
}

.ccg-ka-frame__embed {
  display: block;
  width: 100%;
  height: var(--ccg-ka-frame-height);
  border: 0;
}

/* What the frame holds when the page names no address. It fills the same 760 so
   that removing the embed does not move the footer. */
.ccg-ka-frame__note {
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--ccg-ka-frame-height);
  margin: 0;
  padding: var(--ccg-sv-gutter);
  text-align: center;
  color: var(--ccg-body-quiet);
}

/* --- the inner pages, part three: the five extra sections --------------------
 *
 * ONE SECTION PER PAGE, EXCEPT ON /strategieberatung/, WHICH ADDS TWO. The five
 * service pages are one family carrying the same six components in the same
 * order at the same geometry; what distinguishes four of them from
 * /single-service/ is the text and exactly one section each. Those sections are
 * here: the performance dashboard, the capability matrix, the case cards, the
 * Sanierung strip and the roadmap. Together they are what turns 6163 px of
 * reference build into 7079, 7572, 8083 and 6986.
 *
 * THEY ARE NOT ONE COMPONENT WITH FIVE FACES AND THE ALTERNATIVE WAS COSTED.
 * Four of the five are a grid of white boxes with a heading and a paragraph, so
 * a single card with modifiers is the obvious economy. It is wrong in every
 * property that was measured: 681.33 + 486.66 against 1200 against 385.06 x 3
 * against 351.73 x 3 against 188 x 6; radius 18/22/18/16/14; borders purple at
 * .1 twice, gold at .18 and gold at .16; and three different grounds - white to
 * lilac, white to mint and white to peach. Only the Sanierung card is one of the
 * five constructions test/lib/pages_read.py's CARDS table separates by value,
 * and it is the only one of these five sections that carries `data-ccg-card`.
 *
 * THE FRAME IS THE SHARED ONE AND SO IS THE HEADER. Four of the five sit inside
 * `.ccg-sv` + `.ccg-sv__port` + `.ccg-measure` under a `.ccg-sv-head`, exactly
 * as the feature grid and the constellation do; the Sanierung strip is the one
 * exception and it is an exception twice over - its block is 1100 wide inside
 * the 1200 rail, and its head is 11.84/900/deep-gold over 38.4/900 where the
 * shared one is 12.48/700/purple over 41.6/800. Six values differ, so it has a
 * head of its own rather than the shared one wearing a modifier.
 *
 * WHERE THE MOTION LIVES, AND IT IS NOT WHERE THE REST OF THIS FAMILY PUTS IT.
 * The hero graphic, the constellation and the particle layer animate themselves
 * with SMIL inside the document. Every graphic in this block is static and every
 * second of its motion is a keyframe here: the dashboard's 42 shapes are scaled
 * out of the baseline with `transform-box: fill-box`, the three case
 * trajectories are drawn by running a dash offset of 100 to zero over paths
 * declared `pathLength="100"`, and the roadmap's track is a scaleX. The reason
 * is in the files themselves - the live dashboard SVG carries no `<animate>` at
 * all - and the reason it matters is that a keyframe can be stopped by the
 * `less motion` block below and a SMIL animation cannot.
 *
 * THE STAGGERS READ `--n` OFF THE ELEMENT, which the content writes and the
 * template copies from the loop index. Eight matrix dots, three trajectories and
 * six roadmap phases arrive one after another that way, with no class built out
 * of a counter - a class a template never emits as a literal is a rule the class
 * inventory reports as dead, and it would be reported here eight times.
 */

/* --- the performance dashboard, on /lohnabrechnung/ --------------------------
 *
 * WHAT THE SECTION IS. A 1200-wide two-column block on the rail: a chart card at
 * 681.33 and a column of four stat tiles at 486.66, 32 apart, both stretched to
 * the same height. The 1.4fr/1fr split is the live grid's own and is what
 * produces those two widths at this rail; writing 681/487 instead would be two
 * numbers that stop being right the moment the rail changes.
 *
 * THE CHART IS AN SVG WITH NO SMIL ON IT. Every other graphic in this family
 * animates itself from inside the document; this one is 42 static shapes and the
 * motion is a stylesheet keyframe scaling each bar up from its own foot -
 * `transform-box: fill-box` plus `transform-origin: bottom`, which is what makes
 * a rect grow out of the baseline rather than out of the viewBox's top left.
 * The five pairs come in 0.2 s apart, the value labels fade in from 1.55 s, and
 * the 200-employee pair's gold outline keeps pulsing after it has arrived.
 *
 * THE PER-BAR TOOLTIPS ARE NOT IN design.md's HOVER MAP AND ARE IN THE LIVE
 * PAGE. Each pair carries a transparent 64x180 hit rectangle and a tooltip group
 * that is `opacity: 0` until the pair is hovered or focused; while one pair is
 * hovered the other four drop to opacity .32 and half saturation, and the
 * hovered pair's bars take a brightness and drop-shadow filter. It was read off
 * the live rule set and the live markup, both of which carry it in full, and it
 * is reproduced here rather than dropped because the map is a summary of the
 * page and not its specification.
 */

.ccg-sv-dash {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--ccg-sv-dash-gap);
  align-items: stretch;
}

.ccg-sv-dash__chart {
  position: relative;
  background: var(--ccg-sv-dash-chart-bg);
  border: 1px solid var(--ccg-sv-dash-chart-border);
  border-radius: var(--ccg-sv-dash-chart-radius);
  padding: var(--ccg-sv-dash-chart-padding);
  box-shadow: var(--ccg-sv-dash-chart-shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: var(--ccg-t-teaser);
}

.ccg-sv-dash__chart:hover, .ccg-sv-dash__chart:focus-within {
  transform: translateY(var(--ccg-sv-dash-chart-lift));
  border-color: var(--ccg-sv-dash-chart-border-hover);
  box-shadow: var(--ccg-sv-dash-chart-shadow-hover);
}

/* The card's top rule is inset 14 on each side rather than run to the corners,
   because the card's own radius is 18 and a full-width rule would show its
   square ends over the curve. */
.ccg-sv-dash__chart::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--ccg-sv-dash-rule-inset);
  right: var(--ccg-sv-dash-rule-inset);
  height: var(--ccg-sv-dash-rule-height);
  background: var(--ccg-sv-dash-rule-fill);
  border-radius: 2px;
  z-index: 5;
}

.ccg-sv-dash__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--ccg-sv-dash-head-gap);
  margin-bottom: var(--ccg-sv-dash-head-space);
}

.ccg-sv-dash__title {
  font-size: var(--ccg-sv-dash-title-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-dash-title-tracking);
  color: var(--ccg-heading);
}

.ccg-sv-dash__meta {
  font-size: var(--ccg-sv-dash-meta-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-dash-meta-tracking);
  text-transform: uppercase;
  color: var(--ccg-sv-dash-meta-color);
  white-space: nowrap;
}

.ccg-sv-dash__legend {
  display: flex;
  gap: var(--ccg-sv-dash-legend-gap);
  margin: 0 0 var(--ccg-sv-dash-legend-space);
  padding-bottom: var(--ccg-sv-dash-legend-pad);
  border-bottom: 1px dashed var(--ccg-sv-dash-legend-border);
}

.ccg-sv-dash__legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-size: var(--ccg-sv-dash-legend-size);
  font-weight: var(--ccg-weight-semi);
  color: var(--ccg-body-quiet-2);
}

.ccg-sv-dash__swatch {
  display: inline-block;
  width: var(--ccg-sv-dash-swatch-size);
  height: var(--ccg-sv-dash-swatch-size);
  border-radius: var(--ccg-sv-dash-swatch-radius);
  box-shadow: var(--ccg-sv-dash-swatch-shadow);
}
.ccg-sv-dash__swatch--inhouse { background: var(--ccg-sv-dash-swatch-inhouse); }
.ccg-sv-dash__swatch--ccg { background: var(--ccg-sv-dash-swatch-ccg); }

/* `flex: 1` and not a height: the card is stretched to the stat column's 483.52
   and the graphic takes what is left, which is why it renders 628x308 against a
   600x280 viewBox and why `preserveAspectRatio` on the element matters. */
.ccg-sv-dash__svg {
  width: 100%;
  height: auto;
  margin-top: var(--ccg-sv-dash-svg-space);
}

/* THE WIDE RENDERING, WHICH IS THE ONE THIS RULE ALWAYS DESCRIBED. `flex: 1` and
   not a height: the card is stretched to the stat column's 483.52 and the graphic
   takes what is left, which is why it renders 628x308 against a 600x280 viewBox
   and why `preserveAspectRatio` on the element matters. */
.ccg-sv-dash__svg--wide {
  flex: 1;
  aspect-ratio: 600 / 280;
  overflow: visible;
}

/* --- AND THE PORTRAIT RENDERING, SERVED BELOW THE LAP BAND -------------------
 *
 * Two elements and `display` rather than one element and a different
 * `preserveAspectRatio`, for the reason the spectrum's two drawings record:
 * `preserveAspectRatio` cannot be set from CSS. Both are always in the document;
 * this one is withdrawn here and taken up in the 1024 block.
 *
 * `flex: 0 0 auto` AND NOT `flex: 1`. The wide rendering is stretched to fill
 * what the stat column leaves, which is what makes it 628x308 rather than
 * 628x293. Stretching this one would hand `meet` a box taller than the drawing
 * and leave the scale governed by the width anyway, with the drawing floating in
 * the middle of the leftover - so it is laid out at its own proportion instead.
 *
 * AND IT IS NEVER DRAWN WIDER THAN ITS OWN UNIT COUNT. 320 px against 320 units,
 * the same rule `--ccg-ab-spec-narrow-measure` follows: above the cap one unit
 * would paint more than one pixel and a phone composition would be magnified
 * into a poster. */
.ccg-sv-dash__svg--tall {
  display: none;
  flex: 0 0 auto;
  aspect-ratio: 320 / 600;
  max-width: var(--ccg-sv-dash-narrow-measure);
  margin-inline: auto;
}

/* THE ONE THING THE PORTRAIT DRAWING CANNOT INHERIT, and the reason it is one
   rule rather than a second class on every bar. `.ccg-sv-dash__bar` below rises
   out of its own foot - `transform-box: fill-box`, so the origin is the bar's own
   box and no coordinate of the wide field reaches in here, which is the trap the
   spectrum's `transform-origin` recorded. What DOES reach in is the axis: a bar
   lying on its side, grown out of its own bottom edge, is a bar that gets taller
   rather than longer. `animation-name` is all that is overridden, so the
   duration, the curve, the fill and the five per-pair delays stay the token's. */
.ccg-sv-dash__svg--tall .ccg-sv-dash__bar,
.ccg-sv-dash__svg--tall .ccg-sv-dash__shine {
  transform-origin: left;
  transform: scaleX(0);
  animation: var(--ccg-sv-dash-bar-motion-tall);
}

/* The value labels are 13 units here against the wide drawing's 11, because 320
   units in a 267 px box is not the same unit as 600 in a 581 px one. The two
   readings are in ccg.yaml. */
.ccg-sv-dash__svg--tall .ccg-sv-dash__val { font-size: var(--ccg-sv-dash-val-size-tall); }

/* NOTHING IN THIS DRAWING OPENS ON A POINTER, SO IT IS TAKEN OUT OF THE HIT TEST
   RATHER THAN GIVEN A DIFFERENT CURSOR. The five tooltips are the wide drawing's
   and are not carried here, so there is nothing for a pointer to open. Until this
   line the rule read `cursor: default`, which said that and then left three
   inherited pointer behaviours standing through the shared class: the
   `:has(... :hover)` dimming below fades the other four rows to
   `--ccg-sv-dash-dim-opacity`, `.ccg-sv-dash__pair:hover .ccg-sv-dash__bar`
   saturates the pointed row's bars, and `--ccg-t-pair` transitions both. A row
   that dims its neighbours and brightens itself is an affordance; a band on which
   nothing opens should not be offering one, and a cursor is not what says so.

   `pointer-events: none` IS THE WHOLE OF IT, and that is why it is one line and
   not four overrides. The rows are never hit, so `:hover` never matches inside
   this drawing, the `:has()` above it never fires, the bar filter never applies
   and the cursor is whatever the chart card under it carries. Nothing here is
   focusable or selectable to lose: the element is `aria-hidden="true"` and the
   rows hold no link and no control.

   `cursor` IS SET AS WELL, AND IT IS NOT DOING THE WORK. `pointer-events: none`
   takes the row out of the hit test, so the shared rule's `cursor: pointer` can
   never be the cursor a visitor sees - but it is still the value `getComputedStyle`
   reports on these ten elements, and a reading of the served page that says
   `pointer` about a band with no pointer is a reading somebody has to explain.
   Measured 17.09.2026 at 390 on a container built from this tree: with the line
   below alone the pairs computed `pointer-events: none` and `cursor: pointer`. */
.ccg-sv-dash__svg--tall .ccg-sv-dash__pair {
  pointer-events: none;
  cursor: default;
}

/* AND THE SAVINGS LINE TAKES AN INK THAT READS ON THIS GROUND. In the wide
   drawing `.ccg-sv-dash__tip-save` sits inside a tooltip whose panel is filled
   `#0F1A30`, and `--ccg-accent-gold-bright` (#F5C04E) was chosen against that.
   Here the same five labels sit straight on `--ccg-sv-dash-chart-bg`, which is
   near-white. Measured 17.09.2026 at 390 on a container built from this tree, by
   screenshotting the served page and taking the modal colour inside the label's
   own box, the ground under these lines is `rgb(253,252,255)` and the gold reads
   1.64:1 against it. That is the one graphic this change exists to make readable,
   so the ink moves rather than the ground: the value labels beside these lines
   already carry `--ccg-sv-dash-val-ccg` (#5B2FD4), the saving is the CCG side's
   number, and the two therefore read as one row. Same method, same width, after:
   7.33:1. Both readings are in design.md 2.5. */
.ccg-sv-dash__svg--tall .ccg-sv-dash__tip-save { fill: var(--ccg-sv-dash-val-ccg); }

.ccg-sv-dash__bar,
.ccg-sv-dash__shine,
.ccg-sv-dash__glow {
  transform-box: fill-box;
  transform-origin: bottom;
  transform: scaleY(0);
  animation: var(--ccg-sv-dash-bar-motion);
  transition: var(--ccg-t-barfilter);
}

/* Five pairs, five offsets. The live sheet writes the offsets against the pair's
   position and so does this: the pairs are five siblings of one group and there
   is no class for a template to build out of a loop index. */
.ccg-sv-dash__pair:nth-child(1) .ccg-sv-dash__bar,
.ccg-sv-dash__pair:nth-child(1) .ccg-sv-dash__shine,
.ccg-sv-dash__pair:nth-child(1) .ccg-sv-dash__glow { animation-delay: 0.2s; }
.ccg-sv-dash__pair:nth-child(2) .ccg-sv-dash__bar,
.ccg-sv-dash__pair:nth-child(2) .ccg-sv-dash__shine,
.ccg-sv-dash__pair:nth-child(2) .ccg-sv-dash__glow { animation-delay: 0.4s; }
.ccg-sv-dash__pair:nth-child(3) .ccg-sv-dash__bar,
.ccg-sv-dash__pair:nth-child(3) .ccg-sv-dash__shine,
.ccg-sv-dash__pair:nth-child(3) .ccg-sv-dash__glow { animation-delay: 0.6s; }
.ccg-sv-dash__pair:nth-child(4) .ccg-sv-dash__bar,
.ccg-sv-dash__pair:nth-child(4) .ccg-sv-dash__shine,
.ccg-sv-dash__pair:nth-child(4) .ccg-sv-dash__glow { animation-delay: 0.8s; }
.ccg-sv-dash__pair:nth-child(5) .ccg-sv-dash__bar,
.ccg-sv-dash__pair:nth-child(5) .ccg-sv-dash__shine,
.ccg-sv-dash__pair:nth-child(5) .ccg-sv-dash__glow { animation-delay: 1s; }

.ccg-sv-dash__shine {
  pointer-events: none;
  mix-blend-mode: overlay;
}

.ccg-sv-dash__val {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-dash-val-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-dash-val-tracking);
  pointer-events: none;
  opacity: 0;
  animation: var(--ccg-sv-dash-val-motion);
}
.ccg-sv-dash__val--inhouse { fill: var(--ccg-sv-dash-val-inhouse); }
.ccg-sv-dash__val--ccg { fill: var(--ccg-sv-dash-val-ccg); }

.ccg-sv-dash__pair:nth-child(1) .ccg-sv-dash__val { animation-delay: 1.55s; }
.ccg-sv-dash__pair:nth-child(2) .ccg-sv-dash__val { animation-delay: 1.75s; }
.ccg-sv-dash__pair:nth-child(3) .ccg-sv-dash__val { animation-delay: 1.95s; }
.ccg-sv-dash__pair:nth-child(4) .ccg-sv-dash__val { animation-delay: 2.15s; }
.ccg-sv-dash__pair:nth-child(5) .ccg-sv-dash__val { animation-delay: 2.35s; }

.ccg-sv-dash__shadow {
  transform-origin: center;
  opacity: 0;
  animation: var(--ccg-sv-dash-shadow-motion);
}

.ccg-sv-dash__callout {
  opacity: 0;
  animation: var(--ccg-sv-dash-callout-motion);
}

/* The featured pair's outline arrives with the bars and then keeps breathing,
   which is two animations on one element and not a second element. */
.ccg-sv-dash__pair--feat .ccg-sv-dash__glow { animation: var(--ccg-sv-dash-glow-motion); }

.ccg-sv-dash__pair {
  cursor: pointer;
  transition: var(--ccg-t-pair);
}

.ccg-sv-dash__tip {
  pointer-events: none;
  opacity: 0;
  transform-box: fill-box;
  transition: var(--ccg-t-tip);
}
.ccg-sv-dash__tip-save { fill: var(--ccg-accent-gold-bright); }

.ccg-sv-dash__pairs:has(.ccg-sv-dash__pair:hover) .ccg-sv-dash__pair:not(:hover),
.ccg-sv-dash__pairs:has(.ccg-sv-dash__pair:focus-visible) .ccg-sv-dash__pair:not(:focus-visible) {
  opacity: var(--ccg-sv-dash-dim-opacity);
  filter: var(--ccg-sv-dash-dim-filter);
}

.ccg-sv-dash__pair:hover .ccg-sv-dash__bar,
.ccg-sv-dash__pair:focus-visible .ccg-sv-dash__bar {
  filter: var(--ccg-sv-dash-bar-filter-hover);
}

.ccg-sv-dash__pair:hover .ccg-sv-dash__tip,
.ccg-sv-dash__pair:focus-visible .ccg-sv-dash__tip { opacity: 1; }

@keyframes bhBarRise { to { transform: scaleY(1); } }
/* The portrait drawing's bars, on the other axis. Same shape as `rmLineDrawY`
   further down this file and for the same reason: the arrangement changed, the
   motion did not. */
@keyframes bhBarRun { to { transform: scaleX(1); } }
@keyframes bhValFade { to { opacity: 1; } }
@keyframes bhFeatPulse {
  0%, 100% { stroke-opacity: 0.3; stroke-width: 1.5; }
  50%      { stroke-opacity: 0.75; stroke-width: 2.5; }
}
@keyframes bhLivePulse {
  0%   { box-shadow: 0 0 0 0 var(--ccg-sv-dash-live-ring-from); }
  70%  { box-shadow: 0 0 0 8px var(--ccg-sv-dash-live-ring-to); }
  100% { box-shadow: 0 0 0 0 var(--ccg-sv-dash-live-ring-to); }
}

.ccg-sv-dash__foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: var(--ccg-sv-dash-foot-space);
  padding-top: var(--ccg-sv-dash-foot-pad);
  border-top: 1px solid var(--ccg-sv-dash-foot-border);
}

.ccg-sv-dash__live {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-size: var(--ccg-sv-dash-live-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-dash-live-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent-gold-bright);
}

.ccg-sv-dash__live-dot {
  width: var(--ccg-sv-dash-live-dot-size);
  height: var(--ccg-sv-dash-live-dot-size);
  border-radius: 50%;
  background: var(--ccg-accent-gold-bright);
  box-shadow: 0 0 0 0 var(--ccg-sv-dash-live-ring-from);
  animation: var(--ccg-sv-dash-live-motion);
}

.ccg-sv-dash__summary {
  font-size: var(--ccg-sv-dash-summary-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-dash-summary-tracking);
  color: var(--ccg-accent-gold-bright);
}

.ccg-sv-dash__data {
  display: grid;
  grid-template-rows: repeat(4, 1fr);
  gap: var(--ccg-sv-dash-data-gap);
}

.ccg-sv-dash__stat {
  position: relative;
  background: var(--ccg-sv-dash-stat-bg);
  border: 1px solid var(--ccg-sv-dash-chart-border);
  border-radius: var(--ccg-sv-dash-stat-radius);
  padding: var(--ccg-sv-dash-stat-padding);
  box-shadow: var(--ccg-sv-dash-stat-shadow);
  transition: var(--ccg-t-stat);
  text-align: left;
  overflow: hidden;
}

.ccg-sv-dash__stat::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--ccg-sv-dash-stat-bar-width);
  background: var(--ccg-sv-dash-stat-bar-fill);
}

.ccg-sv-dash__stat:hover, .ccg-sv-dash__stat:focus-within {
  transform: translateX(var(--ccg-sv-dash-stat-shift));
  border-color: var(--ccg-sv-dash-stat-border-hover);
  box-shadow: var(--ccg-sv-dash-stat-shadow-hover);
}

.ccg-sv-dash__num {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-dash-stat-num-size);
  font-weight: 900;
  line-height: 1;
  letter-spacing: var(--ccg-sv-dash-stat-num-tracking);
  color: var(--ccg-heading);
  margin-bottom: var(--ccg-sv-dash-stat-num-space);
  display: flex;
  align-items: baseline;
  gap: var(--ccg-sv-dash-stat-num-gap);
}

.ccg-sv-dash__unit {
  font-size: var(--ccg-sv-dash-stat-unit-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-dash-stat-unit-tracking);
  color: var(--ccg-accent-gold-bright);
}

.ccg-sv-dash__label {
  font-size: var(--ccg-sv-dash-stat-label-size);
  font-weight: var(--ccg-weight-strong);
  letter-spacing: var(--ccg-sv-dash-stat-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent);
  margin: var(--ccg-sv-dash-stat-label-space) 0;
}

.ccg-sv-dash__desc {
  font-size: var(--ccg-sv-dash-stat-desc-size);
  line-height: var(--ccg-sv-dash-stat-desc-line);
  color: var(--ccg-body-quiet-2);
  margin: 0;
}

/* --- the capability matrix, on /externer-cfo/ -------------------------------
 *
 * A 1200x767 card holding a five-column grid: a row-label column and four role
 * columns, a head row of 80 and eight body rows of 52. The 80 is not a taste
 * either: the CCG column's head is a flex column carrying a word and a badge
 * under it, and 8 of padding plus a 24 line plus 4.8 of gap plus a 26.78 badge
 * plus 14.4 of padding plus a 2 px rule is what makes it.
 *
 * `minmax(170px, 1.5fr) repeat(4, 1fr)` IS THE LIVE TRACK LIST AND RESOLVES TO
 * 307.52 / 205 x 4 at this rail. The minmax matters at no width this theme lays
 * out and is carried anyway, because it is what was measured and the resolved
 * pair of numbers is not a track list.
 *
 * THREE DOT STATES, AND THE PARTIAL ONE IS A HALF-FILLED CIRCLE RATHER THAN A
 * TINTED ONE: a 135 degree gradient with a hard stop at 50 per cent inside a
 * 1.5 px gold ring. Read off the live rule, not off the screenshot, because a
 * hard-stop gradient and a half-opacity fill are indistinguishable at 14 px.
 */

.ccg-sv-mtx { max-width: var(--ccg-measure); margin: 0 auto; }

.ccg-sv-mtx__card {
  position: relative;
  background: var(--ccg-sv-mtx-card-bg);
  border: 1px solid var(--ccg-sv-mtx-card-border);
  border-radius: var(--ccg-sv-mtx-card-radius);
  padding: var(--ccg-sv-mtx-card-padding);
  box-shadow: var(--ccg-sv-mtx-card-shadow);
  transition: var(--ccg-t-matrix);
}

.ccg-sv-mtx__card:hover, .ccg-sv-mtx__card:focus-within {
  transform: translateY(var(--ccg-sv-mtx-card-lift));
  box-shadow: var(--ccg-sv-mtx-card-shadow-hover);
}

.ccg-sv-mtx__card::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--ccg-sv-mtx-rule-inset);
  right: var(--ccg-sv-mtx-rule-inset);
  height: var(--ccg-sv-mtx-rule-height);
  background: var(--ccg-sv-mtx-rule-fill);
  border-radius: 2px;
}

.ccg-sv-mtx__grid {
  display: grid;
  grid-template-columns: minmax(170px, 1.5fr) repeat(4, 1fr);
}

.ccg-sv-mtx__cell {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--ccg-sv-mtx-cell-padding);
  border-bottom: 1px solid var(--ccg-sv-mtx-cell-border);
  text-align: center;
  font-size: var(--ccg-sv-mtx-cell-size);
  font-weight: var(--ccg-weight-semi);
  color: var(--ccg-body-quiet-2);
  min-height: var(--ccg-sv-mtx-cell-min);
}

/* The last row carries no rule under it, because the card's own edge is the
   rule there. Five cells, so the last five. */
.ccg-sv-mtx__grid > .ccg-sv-mtx__cell:nth-last-child(-n+5) { border-bottom: none; }

.ccg-sv-mtx__cell--label {
  justify-content: flex-start;
  text-align: left;
  font-size: var(--ccg-sv-mtx-label-size);
  color: var(--ccg-heading);
  padding-left: var(--ccg-sv-mtx-label-pad-left);
  padding-right: var(--ccg-sv-mtx-label-pad-right);
}

.ccg-sv-mtx__cell--head {
  font-size: var(--ccg-sv-mtx-head-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-mtx-head-tracking);
  border-bottom: 2px solid var(--ccg-sv-mtx-head-border);
  padding-top: var(--ccg-sv-mtx-head-pad-top);
  padding-bottom: var(--ccg-sv-mtx-head-pad-bottom);
  color: var(--ccg-body-quiet-2);
}

.ccg-sv-mtx__cell--head.ccg-sv-mtx__cell--label {
  font-size: var(--ccg-sv-mtx-headlabel-size);
  text-transform: uppercase;
  letter-spacing: var(--ccg-sv-mtx-headlabel-tracking);
  color: var(--ccg-sv-mtx-headlabel-color);
}

.ccg-sv-mtx__cell--ccg {
  background: var(--ccg-sv-mtx-ccg-fill);
  border-bottom-color: var(--ccg-sv-mtx-ccg-border);
}

.ccg-sv-mtx__cell--headccg {
  flex-direction: column;
  gap: var(--ccg-sv-mtx-headccg-gap);
  font-weight: 900;
  color: var(--ccg-accent-gold-bright);
  background: var(--ccg-sv-mtx-headccg-fill);
  border-top-left-radius: var(--ccg-sv-mtx-ccg-radius);
  border-top-right-radius: var(--ccg-sv-mtx-ccg-radius);
  border-bottom-color: var(--ccg-accent-gold-bright);
  border-bottom-width: 2px;
}

.ccg-sv-mtx__grid > .ccg-sv-mtx__cell--ccg:last-of-type {
  border-bottom-left-radius: var(--ccg-sv-mtx-ccg-radius);
  border-bottom-right-radius: var(--ccg-sv-mtx-ccg-radius);
  border-bottom: none;
}

.ccg-sv-mtx__badge {
  display: inline-block;
  padding: var(--ccg-sv-mtx-badge-padding);
  background: var(--ccg-accent-gold-bright);
  color: var(--ccg-page);
  font-size: var(--ccg-sv-mtx-badge-size);
  font-weight: 800;
  border-radius: 4px;
  letter-spacing: var(--ccg-sv-mtx-badge-tracking);
  margin-left: 0.4em;
  vertical-align: middle;
}

.ccg-sv-mtx__dot {
  display: inline-block;
  width: var(--ccg-sv-mtx-dot-size);
  height: var(--ccg-sv-mtx-dot-size);
  border-radius: 50%;
  box-sizing: border-box;
  flex-shrink: 0;
}
.ccg-sv-mtx__dot--full { background: var(--ccg-sv-mtx-dot-full); }
.ccg-sv-mtx__dot--partial {
  background: var(--ccg-sv-mtx-dot-partial);
  border: 1.5px solid var(--ccg-accent-gold-bright);
}
.ccg-sv-mtx__dot--none {
  background: transparent;
  border: 1.5px solid var(--ccg-sv-mtx-dot-none-border);
}

/* The stagger is the row index, and the row index is a number the content
   supplies on the element as `--n`. It is a custom property in the markup and
   not a class, so nothing here is a rule with no literal behind it. */
.ccg-sv-mtx__cell--ccg .ccg-sv-mtx__dot--full {
  animation: var(--ccg-sv-mtx-dot-motion);
  animation-delay: calc(var(--n, 0) * 0.18s + 0.3s);
}

@keyframes cmDotPulse {
  0%, 70%, 100% { box-shadow: 0 0 0 0 var(--ccg-sv-mtx-dot-ring-to); }
  35%           { box-shadow: 0 0 0 5px var(--ccg-sv-mtx-dot-ring-from); }
}

.ccg-sv-mtx__legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ccg-sv-mtx-legend-gap);
  margin-top: var(--ccg-sv-mtx-legend-space);
  padding-top: var(--ccg-sv-mtx-legend-pad);
  border-top: 1px dashed var(--ccg-sv-mtx-legend-border);
}

.ccg-sv-mtx__legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-size: var(--ccg-sv-mtx-legend-size);
  font-weight: var(--ccg-weight-semi);
  color: var(--ccg-body-quiet-2);
}

.ccg-sv-mtx__claim {
  margin-top: var(--ccg-sv-mtx-claim-space);
  padding: var(--ccg-sv-mtx-claim-padding);
  background: var(--ccg-sv-mtx-claim-fill);
  border-left: 3px solid var(--ccg-accent-gold-bright);
  border-radius: 10px;
  text-align: left;
}

.ccg-sv-mtx__claim-eyebrow {
  display: block;
  font-size: var(--ccg-sv-mtx-claim-eyebrow-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-mtx-claim-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent-gold-bright);
  margin-bottom: var(--ccg-sv-mtx-claim-eyebrow-space);
}

.ccg-sv-mtx__claim-text {
  margin: 0;
  font-size: var(--ccg-sv-mtx-claim-size);
  line-height: var(--ccg-sv-mtx-claim-line);
  color: var(--ccg-heading);
}

.ccg-sv-mtx__claim-strong {
  font-weight: 800;
  color: var(--ccg-accent-gold-bright);
}

/* --- the case cards, on /strategieberatung/ ---------------------------------
 *
 * Three cards across the rail, 22.4 apart, each 385 wide and all three as tall
 * as the tallest - 640 here, which the first card sets. The border is gold at
 * .18 and not purple: this is the only card in the family whose frame is not the
 * purple one, and the ground under it is a white-to-mint gradient rather than a
 * white-to-lilac one.
 *
 * THE TRAJECTORY IS DRAWN, NOT ANIMATED IN THE FILE. Each SVG is six static
 * shapes with `pathLength="100"` and a dash offset of 100, and the stylesheet
 * runs the offset to zero - which is why the line appears to be drawn left to
 * right at a rate that does not depend on the path's real length. The three
 * cards are staggered by the same `--n` the content supplies.
 */

.ccg-sv-cases {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ccg-sv-case-gap);
  max-width: var(--ccg-measure);
  margin: 0 auto;
}

.ccg-sv-case {
  position: relative;
  background: var(--ccg-sv-case-bg);
  border: 1px solid var(--ccg-sv-case-border);
  border-radius: var(--ccg-sv-case-radius);
  padding: var(--ccg-sv-case-padding);
  box-shadow: var(--ccg-sv-case-shadow);
  display: flex;
  flex-direction: column;
  transition: var(--ccg-t-teaser);
  text-align: left;
  overflow: hidden;
}

.ccg-sv-case:hover, .ccg-sv-case:focus-within {
  transform: translateY(var(--ccg-sv-case-lift));
  border-color: var(--ccg-sv-case-border-hover);
  box-shadow: var(--ccg-sv-case-shadow-hover);
}

.ccg-sv-case::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--ccg-sv-case-rule-inset);
  right: var(--ccg-sv-case-rule-inset);
  height: var(--ccg-sv-case-rule-height);
  background: var(--ccg-sv-case-rule-fill);
  border-radius: 2px;
}

.ccg-sv-case__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--ccg-sv-case-head-space);
}

.ccg-sv-case__num {
  font-size: var(--ccg-sv-case-num-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-case-num-tracking);
  text-transform: uppercase;
  color: var(--ccg-sv-case-num-color);
}

.ccg-sv-case__sector {
  display: inline-block;
  padding: var(--ccg-sv-case-sector-padding);
  background: var(--ccg-sv-case-sector-fill);
  color: var(--ccg-sv-case-sector-color);
  font-size: var(--ccg-sv-case-sector-size);
  font-weight: 800;
  border-radius: 4px;
  letter-spacing: var(--ccg-sv-case-sector-tracking);
}

/* The profile line states its own padding-bottom, which is the reason it does
   NOT carry `--ccg-sv-heading-descender` like every other h3 in the family: the
   12.8 that separates it from its dashed rule is the same property, and the live
   element measures 59.14 rather than the 60.8 a descender pad would give. */
.ccg-sv-case__profile {
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-sv-case-profile-size);
  font-weight: var(--ccg-sv-case-profile-weight);
  line-height: var(--ccg-sv-case-profile-line);
  letter-spacing: var(--ccg-sv-case-profile-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-case-profile-space);
  padding-bottom: var(--ccg-sv-case-profile-pad);
  border-bottom: 1px dashed var(--ccg-sv-case-profile-border);
}

.ccg-sv-case__section {
  position: relative;
  padding-left: var(--ccg-sv-case-section-pad);
  margin-bottom: var(--ccg-sv-case-section-space);
}

.ccg-sv-case__section::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--ccg-sv-case-tick-top);
  bottom: var(--ccg-sv-case-tick-bottom);
  width: 2px;
  background: var(--ccg-sv-case-tick-fill);
  border-radius: 1px;
}

.ccg-sv-case__section--result::before {
  background: var(--ccg-sv-case-tick-fill-result);
  width: 3px;
}

.ccg-sv-case__label {
  display: block;
  font-size: var(--ccg-sv-case-label-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-case-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent-gold-bright);
  margin-bottom: var(--ccg-sv-case-label-space);
}

.ccg-sv-case__section--result .ccg-sv-case__label { color: var(--ccg-sv-case-result-color); }

.ccg-sv-case__text {
  margin: 0;
  font-size: var(--ccg-sv-case-text-size);
  line-height: var(--ccg-sv-case-text-line);
  color: var(--ccg-body-quiet-2);
}

.ccg-sv-case__traj {
  margin-top: var(--ccg-sv-case-traj-space);
  padding-top: var(--ccg-sv-case-traj-space);
  border-top: 1px dashed var(--ccg-sv-case-profile-border);
}

.ccg-sv-case__traj-svg {
  width: 100%;
  height: var(--ccg-sv-case-traj-height);
  display: block;
  overflow: visible;
}

.ccg-sv-case__traj-labels {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: var(--ccg-sv-case-traj-label-space);
  padding: 0 2px;
}

.ccg-sv-case__traj-lbl {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-case-traj-label-size);
  font-weight: 800;
  letter-spacing: var(--ccg-sv-case-traj-label-tracking);
}
.ccg-sv-case__traj-lbl--before { color: var(--ccg-sv-case-num-color); }
.ccg-sv-case__traj-lbl--after { color: var(--ccg-accent-gold-bright); }
.ccg-sv-case__traj-lbl--mid {
  color: var(--ccg-accent-gold-bright);
  opacity: 0;
  animation: var(--ccg-sv-case-fade-motion);
  animation-delay: calc(var(--n, 1) * 0.18s + 1.7s);
}

.ccg-sv-case__line--before {
  animation: var(--ccg-sv-case-draw-motion);
  animation-delay: calc(var(--n, 1) * 0.18s + 0.3s);
}
.ccg-sv-case__mark {
  animation: var(--ccg-sv-case-mark-motion);
  animation-delay: calc(var(--n, 1) * 0.18s + 1.4s);
}
.ccg-sv-case__mid {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-sv-case-pop-motion);
  animation-delay: calc(var(--n, 1) * 0.18s + 1.5s);
}
.ccg-sv-case__line--after {
  animation: var(--ccg-sv-case-draw-motion-after);
  animation-delay: calc(var(--n, 1) * 0.18s + 1.8s);
}
.ccg-sv-case__end {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-sv-case-pop-motion);
  animation-delay: calc(var(--n, 1) * 0.18s + 3s);
  filter: var(--ccg-sv-case-end-glow);
}

/* THE CENTRING IS DECLARED HERE AND IS INHERITED ON THE LIVE PAGE. Its own rule
   states only `margin-top: 2rem`; the centre comes from the section's column,
   which is `bt_bb_align_center` and hands `text-align: center` to everything
   inside it. This theme centres each block by its own rule instead, so a block
   that took the alignment from an ancestor has to state it - and the disclaimer
   is the only element in these five sections where the difference is visible:
   it computed `start` here against `center` on the live page and rendered
   against the rail's left edge. The bottom margin is deliberately not stated,
   because the paragraph's own 16 is what makes the live section measure 144
   below this line where the roadmap's note, whose rule sets `margin: 1.6rem 0
   0`, measures 128. */
.ccg-sv-cases__note {
  margin-top: var(--ccg-sv-case-note-space);
  text-align: center;
}

@keyframes caseTrajDraw { to { stroke-dashoffset: 0; } }
@keyframes caseTrajFade { to { opacity: 1; } }
@keyframes caseTrajPop {
  0%   { opacity: 0; transform: scale(0.3); }
  100% { opacity: 1; transform: scale(1); }
}

/* --- the Sanierung strip, on /strategieberatung/ ----------------------------
 *
 * The one section in this family that is not on the 1200 rail: the block is
 * 1100 wide and centred, and its head is 720 inside that. It is also the one
 * section whose ground is the lilac tint rather than white or transparent, and
 * the only one of the five added sections with no dot field behind it - the live
 * section carries no background layer at all.
 *
 * ITS HEADING IS NOT `.ccg-sv-head`. The section header partial produces a
 * label, a title and a sub-line at the family's sizes; this block's eyebrow is
 * 11.84 at weight 900 in gold-deep, its title is 38.4 at weight 900 - the live
 * `clamp(1.7rem, 3vw, 2.4rem)` resolved at 1920 - and its sub-line is 16.8/26.88.
 * Three different sizes, so it is its own head and not the shared one wearing a
 * modifier.
 */

.ccg-sv-sanierung { background: var(--ccg-sv-san-bg); }

.ccg-sv-san { max-width: var(--ccg-sv-san-width); margin: 0 auto; }

.ccg-sv-san__head {
  text-align: center;
  max-width: var(--ccg-sv-san-head-width);
  margin: 0 auto var(--ccg-sv-san-head-space);
}

.ccg-sv-san__eyebrow {
  display: inline-block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-san-eyebrow-size);
  font-weight: 900;
  letter-spacing: var(--ccg-sv-san-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent-2);
  margin-bottom: var(--ccg-sv-san-eyebrow-space);
}

.ccg-sv-san__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-san-title-size);
  font-weight: 900;
  line-height: var(--ccg-sv-san-title-line);
  letter-spacing: var(--ccg-sv-san-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-san-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

/* The one word the live headline sets apart, and it does so with colour rather
   than with italics - which is why the element is an `em` with its style undone
   rather than a span: the live markup is an `em`. */
.ccg-sv-san__title em {
  font-style: normal;
  color: var(--ccg-accent);
}

.ccg-sv-san__sub {
  font-size: var(--ccg-sv-san-sub-size);
  line-height: var(--ccg-sv-san-sub-line);
  color: var(--ccg-body-quiet);
  margin: 0;
}

.ccg-sv-san__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ccg-sv-san-gap);
}

.ccg-sv-san__card {
  background: var(--ccg-page);
  border: 1px solid var(--ccg-sv-san-card-border);
  border-radius: var(--ccg-sv-san-card-radius);
  padding: var(--ccg-sv-san-card-padding);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
  transition: var(--ccg-t-sanierung);
  text-align: left;
}

.ccg-sv-san__card:hover, .ccg-sv-san__card:focus-within {
  transform: translateY(var(--ccg-sv-san-card-lift));
  box-shadow: var(--ccg-sv-san-card-shadow-hover);
  border-color: var(--ccg-sv-san-card-border-hover);
}

/* The chip carries the colour and the glyph inherits it: the three icons are
   stroked with `currentColor`, which is what lets one rule paint all three. */
.ccg-sv-san__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ccg-sv-san-chip-size);
  height: var(--ccg-sv-san-chip-size);
  border-radius: var(--ccg-sv-san-chip-radius);
  background: var(--ccg-sv-san-chip-fill);
  color: var(--ccg-accent);
  margin-bottom: var(--ccg-sv-san-chip-space);
}

.ccg-sv-san__card-title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-san-ct-size);
  font-weight: 800;
  line-height: var(--ccg-sv-san-ct-line);
  letter-spacing: var(--ccg-sv-san-ct-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-san-ct-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-sv-san__card-text {
  font-size: var(--ccg-sv-san-cp-size);
  line-height: var(--ccg-sv-san-cp-line);
  color: var(--ccg-body-quiet);
  margin: 0;
}

/* --- the roadmap, on /existenzgruender-beratung/ ----------------------------
 *
 * Six phases in a row on a drawn gold track. The track is an absolute 1000x3
 * bar inset 8.33 per cent on each side - one twelfth, which is where the first
 * and last dot centres fall - and it is under the phases rather than through
 * them, which is why the dots carry a 4 px white ring: the ring is what hides
 * the track where it would otherwise cross the dot's own edge.
 *
 * THE PHASES ARRIVE ONE AFTER ANOTHER AND THE TRACK IS DRAWN UNDER THEM, both
 * from the same `--n` the content supplies: `scaleX(0)` to 1 over 2.4 s for the
 * track, and a 0.18 s step per phase for the cards. The sixth card is 152 tall
 * where the other five are 170, and that is its paragraph running to three lines
 * instead of four rather than a rule of its own.
 */

.ccg-sv-road {
  position: relative;
  max-width: var(--ccg-measure);
  margin: 0 auto;
  padding: var(--ccg-sv-road-pad);
}

.ccg-sv-road__track {
  position: absolute;
  top: var(--ccg-sv-road-track-top);
  left: var(--ccg-sv-road-track-inset);
  right: var(--ccg-sv-road-track-inset);
  height: var(--ccg-sv-road-track-height);
  pointer-events: none;
  z-index: 0;
}

.ccg-sv-road__line {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--ccg-sv-road-track-fill);
  border-radius: 2px;
  transform-origin: left center;
  transform: scaleX(0);
  animation: var(--ccg-sv-road-line-motion);
}

.ccg-sv-road__phases {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--ccg-sv-road-gap);
  position: relative;
  z-index: 1;
}

.ccg-sv-road__phase {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  opacity: 0;
  transform: translateY(8px);
  animation: var(--ccg-sv-road-phase-motion);
  animation-delay: calc(var(--n, 1) * 0.18s + 0.3s);
}

.ccg-sv-road__dot-wrap {
  display: flex;
  justify-content: center;
  margin-bottom: var(--ccg-sv-road-dot-space);
}

.ccg-sv-road__dot {
  width: var(--ccg-sv-road-dot-size);
  height: var(--ccg-sv-road-dot-size);
  border-radius: 50%;
  background: var(--ccg-page);
  border: 2px solid var(--ccg-accent-gold-bright);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--ccg-sv-road-dot-shadow);
  position: relative;
  z-index: 2;
  transition: var(--ccg-t-roaddot);
}

.ccg-sv-road__phase:hover .ccg-sv-road__dot,
.ccg-sv-road__phase:focus-within .ccg-sv-road__dot {
  transform: scale(var(--ccg-sv-road-dot-scale-hover));
  box-shadow: var(--ccg-sv-road-dot-shadow-hover);
}

.ccg-sv-road__num {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-sv-road-num-size);
  font-weight: 900;
  letter-spacing: var(--ccg-sv-road-num-tracking);
  color: var(--ccg-accent-gold-bright);
}

.ccg-sv-road__card {
  background: var(--ccg-sv-road-card-bg);
  border: 1px solid var(--ccg-sv-road-card-border);
  border-radius: var(--ccg-sv-road-card-radius);
  padding: var(--ccg-sv-road-card-padding);
  width: 100%;
  box-shadow: var(--ccg-sv-road-card-shadow);
  transition: var(--ccg-t-stat);
  text-align: left;
}

.ccg-sv-road__phase:hover .ccg-sv-road__card,
.ccg-sv-road__phase:focus-within .ccg-sv-road__card {
  transform: translateY(var(--ccg-sv-road-card-lift));
  border-color: var(--ccg-sv-road-card-border-hover);
  box-shadow: var(--ccg-sv-road-card-shadow-hover);
}

.ccg-sv-road__title {
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-sv-road-title-size);
  font-weight: var(--ccg-sv-road-title-weight);
  line-height: var(--ccg-sv-road-title-line);
  letter-spacing: var(--ccg-sv-road-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-sv-road-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-sv-road__duration {
  display: inline-block;
  padding: var(--ccg-sv-road-duration-padding);
  background: var(--ccg-sv-road-duration-fill);
  color: var(--ccg-sv-road-duration-color);
  font-size: var(--ccg-sv-road-duration-size);
  font-weight: 800;
  border-radius: 4px;
  letter-spacing: var(--ccg-sv-road-duration-tracking);
  text-transform: uppercase;
  margin-bottom: var(--ccg-sv-road-duration-space);
}

.ccg-sv-road__desc {
  font-size: var(--ccg-sv-road-desc-size);
  line-height: var(--ccg-sv-road-desc-line);
  color: var(--ccg-body-quiet-2);
  margin: 0;
}

.ccg-sv-road__note {
  text-align: center;
  margin: var(--ccg-sv-road-note-space) 0 0;
  font-size: var(--ccg-sv-road-note-size);
  font-weight: var(--ccg-weight-semi);
  letter-spacing: var(--ccg-sv-road-note-tracking);
  color: var(--ccg-sv-road-note-color);
}

@keyframes rmLineDraw { to { transform: scaleX(1); } }
@keyframes rmPhaseIn { to { opacity: 1; transform: translateY(0); } }

/* --- the inner pages, part four: /about/ -------------------------------------
 *
 * THE THIRD FAMILY, AND THE EXPENSIVE ONE. 7686 px over eight sections, five of
 * which are constructions nothing else on this site renders: a two-sided
 * timeline with a graphic of its own behind every entry, a dark quote band used
 * twice, a two-column mission block with a compass hung off the rail, a 1200 x
 * 480 process graphic in which every word is an SVG text node, and a closing
 * block that ends in a region badge and a signature.
 *
 * THE HERO IS THE ONE THING IT BORROWS, AND IT BORROWS RATHER THAN COPIES.
 * design.md counts exactly eleven `ccg-bh-*` elements shared with the service
 * family and they are all in the hero: the section, the media wrapper, the photo
 * layer, the particle layer, the wave, the text block, the label, the H1 with
 * its two runs, the sub-line and the pill. So `.ccg-ab-hero` declares no
 * geometry at all - it re-points fifteen of the service hero's own custom
 * properties at this page's measured values and lets `.ccg-sv-hero` render. The
 * alternative was a second hero rule set that would have had to be kept level
 * with the first for ever.
 *
 * THE PARTICLE LAYER IS THE SAME SIX CIRCLES IN A DIFFERENT COLOUR, WHICH IS A
 * CORRECTION TO design.md. That document records the particle SVG as
 * byte-identical on all five service pages and on this one. It is not: the live
 * /about/ layer draws the same six circles at the same six cx values on the same
 * twelve SMIL animations, but fills them `#9B6DFF` and `#B284FF` where the
 * service pages fill them `#F5C04E` and `#FFD480`. Rather than ship a second
 * copy of the file for two colours, the four rules below repaint circles 1, 3, 4
 * and 6 through `fill`, which overrides the presentation attribute the shared
 * graphic carries. Circles 2 and 5 are white on both pages and are left alone.
 *
 * THE FACE THIS BLOCK ASKS FOR IS SHIPPED, AND AN EARLIER VERSION OF THIS
 * COMMENT SAID IT IS NOT. The blockquote in the quote band is measured at Inter
 * 700 - re-measured on the live page on 26 August 2026: weight 700, 35.2/45.76 -
 * and css/fonts.css declares Inter 700 from fonts/inter-700.woff2, in the same
 * block as Inter 500 and for the same reason. Both faces arrived with this page.
 * The old wording said the theme carries 600, 800 and 900 only and pointed at
 * report-about.md, a document that does not exist, as the place the gap was
 * reported to the parent. R4's instruction still applies to the value: the
 * declaration below states the measured 700 rather than rounding it to a weight
 * that happened to be present.
 *
 * WHAT THE LIVE PAGE DOES THAT IS NOT REPRODUCED IS IN design.md 9.5, and every
 * one of them is a declared departure: the hidden mobile twin of the spectrum
 * (9.4); the `aboutSparkle` animation, which three live rules name, six live
 * elements run and no `@keyframes` block defines, so it does nothing there
 * either; and `.ccg-about-spec-outlet`, a rule whose class no element on the
 * live page carries.
 */

/* --- the hero, which is the service hero recoloured -------------------------
 *
 * FIFTEEN PROPERTIES AND NO GEOMETRY. The photograph is a different file behind
 * a different blur on a different scale; the mesh is four purple stops where the
 * service one has a gold stop among them; the text block has no lift, because
 * the live rule that lifts it by -4vh is bound to `#ccg-bh-hero` and this
 * section is not that; and the label, the underline, the two graphic filters and
 * the pill are purple. Everything else - the 720 block, the 35.2 setup, the 57.6
 * punch on a 67.97 line, the sub-line at 17.6/27.28 and the pill's
 * 14.44/31.92 - is the service hero's, unchanged, which is what "borrows eleven
 * elements" means when it is made structural.
 *
 * THE PILL KEEPS A GOLD SHADOW UNDER A PURPLE FILL, and that is measured rather
 * than an oversight carried across: the live `.ccg-bh-cta-pill` states its
 * `box-shadow` as a literal `rgba(245,192,78,.40)`, and the About page's
 * override replaces the background and the ink and nothing else. So the purple
 * pill on this page drops the gold pill's shadow, on the live site and here.
 */
.ccg-ab-hero {
  --ccg-sv-hero-bg: var(--ccg-ab-hero-bg);
  --ccg-sv-hero-photo-filter: var(--ccg-ab-hero-photo-filter);
  --ccg-sv-hero-photo-scale: var(--ccg-ab-hero-photo-scale);
  --ccg-sv-hero-photo-bleed-x: var(--ccg-ab-hero-photo-bleed);
  --ccg-sv-hero-photo-bleed-y: var(--ccg-ab-hero-photo-bleed);
  --ccg-sv-hero-mesh: var(--ccg-ab-hero-mesh);
  --ccg-sv-hero-mesh-motion: var(--ccg-ab-hero-mesh-motion);
  --ccg-sv-hero-text-lift: var(--ccg-ab-hero-text-lift);
  --ccg-sv-hero-label-color: var(--ccg-ab-accent);
  --ccg-sv-hero-rule-fill: var(--ccg-ab-hero-rule-fill);
  --ccg-sv-hero-rule-glow: var(--ccg-ab-hero-rule-glow);
  --ccg-sv-hero-anim-filter: var(--ccg-ab-hero-anim-filter);
  --ccg-sv-particles-filter: var(--ccg-ab-particles-filter);
  --ccg-sv-pill-fill: var(--ccg-ab-pill-fill);
  --ccg-sv-pill-text: var(--ccg-ab-pill-text);
}

/* The four circles of the shared particle layer this page paints differently.
   `fill` in a stylesheet beats the `fill=` attribute in the markup, which is how
   one graphic serves six pages in two colourways without a second copy. */
/* AND ITS PHOTOGRAPH IS NOT THE SERVICE PHOTOGRAPH, so the two values the
 * service hero paints its own file with do not come with the rest. Measured on
 * 2026-09-02 at 1920 on https://www.consulting-concept.de/about/: the layer is
 * 2073.6 x 1166.4 at (-76.8, -43.2), the file behind it is Logo-im-Buero.png at
 * 1640 x 924, and it is painted at `background-size: cover`. The service family's
 * `auto` at `0% 0` is the original's arrangement for a 1920 x 1200 file in a
 * narrower box and is not this page's; this is written here rather than left to
 * the variable block above, because the two are values of the photograph and not
 * of the hero's colour scheme. /about/kontakt has a photo element of its own and
 * takes neither. */
.ccg-ab-hero .ccg-sv-hero__photo {
  background-size: cover;
  background-position: 50% 50%;
}

.ccg-ab-hero .ccg-sv-hero__particles circle:nth-child(1),
.ccg-ab-hero .ccg-sv-hero__particles circle:nth-child(3) { fill: var(--ccg-ab-particle-lead); }
.ccg-ab-hero .ccg-sv-hero__particles circle:nth-child(4),
.ccg-ab-hero .ccg-sv-hero__particles circle:nth-child(6) { fill: var(--ccg-ab-particle-pale); }

/* The mesh drift. It is not `bhMeshDrift`: the live About keyframe moves -2.5%
   at its midpoint where the service one moves -2%, which is a quarter of a
   percent of 1080 px and is still a value somebody measured. */
@keyframes abMeshDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  25%      { transform: translate(-3%, 2%) scale(1.05); }
  50%      { transform: translate(2%, -2.5%) scale(0.97); }
  75%      { transform: translate(-1.5%, -1%) scale(1.03); }
}

/* The hero emblem's two moving parts. The dashed outer ring turns once every 55
   seconds; the four cardinal diamonds breathe on a one-second stagger. Both are
   CSS reaching into the inline graphic through a class, which is how the live
   page drives them and why the classes in the graphic are not decoration.

   THE SIX AMBIENT SPARKLES DO NOT MOVE AND THAT IS THE ORIGINAL'S OWN STATE.
   The live markup marks them `ccg-about-spark` and the live sheet asks them for
   `aboutSparkle 4s ease-in-out infinite` - and `@keyframes aboutSparkle` is in
   none of the 291977 bytes those rules live in, searched for and not found. So
   the original names an animation nobody wrote, the dots are static there, and
   there is no rule for them here: a rule that animates nothing is exactly what
   R1 keeps out of this file. */
/* The footline's size changes by band: the field is 400 units across and the
   element is laid out between 133 px and 811 px over the widths this site is used
   at, so one declared size cannot clear the type floor at the bottom without
   shouting at the top. The three values, the eight measured scales and what each
   value paints are in ccg.yaml; the other two are set on this same element in the
   1024 and the 640 blocks, beside the rest of what those bands change. */
.ccg-ab-emblem__foot { font-size: var(--ccg-ab-emblem-foot-size); }

.ccg-ab-emblem__ring {
  transform-box: fill-box;
  transform-origin: 200px 250px;
  animation: var(--ccg-ab-emblem-ring-motion);
}

.ccg-ab-emblem__cardinals polygon {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-ab-emblem-diamond-motion);
}
.ccg-ab-emblem__cardinals polygon:nth-child(2) { animation-delay: 1s; }
.ccg-ab-emblem__cardinals polygon:nth-child(3) { animation-delay: 2s; }
.ccg-ab-emblem__cardinals polygon:nth-child(4) { animation-delay: 3s; }

@keyframes abEmblemRotate {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

@keyframes abEmblemDiamond {
  0%, 100% { opacity: 0.55; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.15); }
}

/* --- the timeline, 1572 -----------------------------------------------------
 *
 * A 1100 WRAP INSIDE THE 1200 RAIL, AND A 1000 LIST INSIDE THAT. Both are the
 * live element's own widths and neither is the rail: the header's paragraph is
 * boxed to 720 inside the 1100, and the five entries sit on a 1000 track whose
 * centre line is the ::before below.
 *
 * THE SIDE IS `:nth-child`, NOT A CLASS. The live page writes `tl-left` and
 * `tl-right` onto every entry, and across all five those are the entry's ordinal
 * spelled out - first left, second right, with no exception. So the ordinal is
 * read here and the markup carries nothing about it. `:last-child` does the same
 * job for the current entry, whose year, marker and decoration all differ.
 *
 * THE ENTRY IS FLEX AND NOT THE GRID design.md DESCRIBES. That document records
 * `grid-template-columns: 1fr 56px 1fr` with a 32 gap, which is a rule the live
 * sheet states and then overrides twice; the last declaration wins and it is
 * `display: flex` with a card of `calc(50% - 40px)`. The measured card is 460
 * wide, and 460 is what `calc(50% - 40px)` of 1000 gives - the grid would give
 * 440. So the flex form is the one that renders and the correction is recorded
 * in design.md, as correction 2 of `Four corrections to the measurement above`
 * and re-measured in 9.5 item 5.
 *
 * THE ENTRIES DO NOT USE `data-ccg-reveal`. The live entries run `tlEntryIn` on
 * a 0.15 s stagger from page load rather than on arrival in the viewport, which
 * is a different behaviour from the theme's shared reveal and is the one
 * measured. The header above them does use the shared reveal, because that is
 * what the live header does.
 */
.ccg-ab-tl {
  max-width: var(--ccg-ab-tl-width);
  margin: 0 auto;
  padding: var(--ccg-ab-tl-pad);
}

.ccg-ab-tl__header {
  text-align: center;
  margin-bottom: var(--ccg-ab-tl-head-space);
}

/* Two elements because the live header has two, and because each carries half
   of what the eyebrow is: the outer one sets the size, the weight and the
   uppercasing, and the inner one overrides the colour and the tracking. */
.ccg-ab-tl__meta {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--ccg-ab-eyebrow-size);
  font-weight: var(--ccg-ab-eyebrow-weight);
  text-transform: uppercase;
  margin-bottom: var(--ccg-ab-tl-meta-space);
}

.ccg-ab-tl__eyebrow {
  color: var(--ccg-ab-eyebrow-quiet);
  letter-spacing: var(--ccg-ab-eyebrow-tracking);
}

.ccg-ab-tl__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-h2-size);
  line-height: var(--ccg-ab-h2-line);
  font-weight: var(--ccg-ab-h2-weight);
  letter-spacing: var(--ccg-ab-h2-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-tl-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  text-wrap: balance;
}

.ccg-ab-tl__sub {
  font-size: var(--ccg-ab-tl-sub-size);
  line-height: var(--ccg-ab-tl-sub-line);
  color: var(--ccg-ab-body);
  margin: 0 auto;
  max-width: var(--ccg-ab-tl-sub-width);
}

.ccg-ab-tl__list {
  position: relative;
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: var(--ccg-ab-tl-list-width);
}

/* The centre line. It stops 18 px short at each end so the first and the last
   marker sit on a line that does not run past them. */
.ccg-ab-tl__list::before {
  content: "";
  position: absolute;
  left: 50%;
  top: var(--ccg-ab-tl-rail-inset);
  bottom: var(--ccg-ab-tl-rail-inset);
  width: var(--ccg-ab-tl-rail-width);
  background: var(--ccg-ab-tl-rail-fill);
  transform: translateX(-50%);
  border-radius: 1.5px;
  z-index: 0;
}

.ccg-ab-tl__entry {
  position: relative;
  display: flex;
  align-items: center;
  min-height: var(--ccg-ab-tl-entry-min);
  margin-bottom: var(--ccg-ab-tl-entry-space);
  width: 100%;
  opacity: 0;
  transform: translateY(20px);
  animation: var(--ccg-ab-tl-entry-motion);
  z-index: 1;
}

.ccg-ab-tl__entry:nth-child(1) { animation-delay: 0.1s; }
.ccg-ab-tl__entry:nth-child(2) { animation-delay: 0.25s; }
.ccg-ab-tl__entry:nth-child(3) { animation-delay: 0.4s; }
.ccg-ab-tl__entry:nth-child(4) { animation-delay: 0.55s; }
.ccg-ab-tl__entry:nth-child(5) { animation-delay: 0.7s; }
.ccg-ab-tl__entry:last-child { margin-bottom: 0; }

@keyframes abEntryIn { to { opacity: 1; transform: translateY(0); } }

.ccg-ab-tl__card {
  width: calc(50% - var(--ccg-ab-tl-card-inset));
  display: flex;
  flex-direction: column;
  gap: var(--ccg-ab-tl-card-gap);
  position: relative;
  z-index: 2;
}

.ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__card {
  margin-right: auto;
  text-align: right;
  padding-right: var(--ccg-ab-tl-card-pad);
}

.ccg-ab-tl__entry:nth-child(even) .ccg-ab-tl__card {
  margin-left: auto;
  text-align: left;
  padding-left: var(--ccg-ab-tl-card-pad);
}

/* The year and the badge are inline-blocks in a column flex, so they take the
   full width unless they are told which end to sit at. On a right-aligned card
   that end is the right one. */
.ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__year,
.ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__badge { align-self: flex-end; }

.ccg-ab-tl__year {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-tl-year-size);
  font-weight: var(--ccg-ab-tl-year-weight);
  letter-spacing: var(--ccg-ab-tl-year-tracking);
  color: var(--ccg-accent);
  margin-bottom: var(--ccg-ab-tl-year-space);
  display: inline-block;
}

.ccg-ab-tl__entry:last-child .ccg-ab-tl__year { color: var(--ccg-ab-accent); }

.ccg-ab-tl__badge {
  display: inline-block;
  align-self: flex-start;
  padding: var(--ccg-ab-tl-badge-pad);
  background: var(--ccg-ab-tl-badge-fill);
  color: var(--ccg-page);
  font-size: var(--ccg-ab-tl-badge-size);
  font-weight: var(--ccg-ab-tl-badge-weight);
  letter-spacing: var(--ccg-ab-tl-badge-tracking);
  border-radius: var(--ccg-ab-tl-badge-radius);
  text-transform: uppercase;
  margin-bottom: var(--ccg-ab-tl-badge-space);
}

.ccg-ab-tl__headline {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-tl-headline-size);
  line-height: var(--ccg-ab-tl-headline-line);
  font-weight: var(--ccg-ab-tl-headline-weight);
  letter-spacing: var(--ccg-ab-tl-headline-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-tl-headline-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-ab-tl__text {
  font-size: var(--ccg-ab-tl-text-size);
  line-height: var(--ccg-ab-tl-text-line);
  color: var(--ccg-ab-body);
  margin: 0;
}

/* The marker sits on the centre line rather than in a column of the entry, which
   is why it is absolute: the card is half the track and the marker is over the
   seam between the two halves, not between them. */
.ccg-ab-tl__marker {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: var(--ccg-ab-tl-marker-size);
  height: var(--ccg-ab-tl-marker-size);
  border-radius: 50%;
  background: var(--ccg-ab-tl-marker-fill);
  border: 3px solid var(--ccg-page);
  box-shadow: var(--ccg-ab-tl-marker-shadow);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
  transition: var(--ccg-t-button);
}

.ccg-ab-tl__entry:hover .ccg-ab-tl__marker {
  transform: translate(-50%, -50%) scale(1.1);
  box-shadow: var(--ccg-ab-tl-marker-shadow-hover);
}

/* The current entry's marker, which is the only element on this page with a
   halo that keeps moving after everything else has settled. */
.ccg-ab-tl__entry:last-child .ccg-ab-tl__marker {
  background: var(--ccg-ab-tl-marker-now-fill);
  box-shadow: var(--ccg-ab-tl-marker-now-shadow);
  animation: var(--ccg-ab-tl-marker-motion);
}

@keyframes abMarkerPulse {
  0%, 100% { box-shadow: var(--ccg-ab-tl-marker-now-shadow); }
  50%      { box-shadow: var(--ccg-ab-tl-marker-halo-shadow); }
}

/* The decoration behind an entry: 240 square at 14 per cent, hung 4 per cent in
   from the side the card is not on, and translated up by half its own height so
   its centre is the entry's. The current entry brightens to 22 and hover to 28. */
.ccg-ab-tl__decor {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: var(--ccg-ab-tl-decor-size);
  height: var(--ccg-ab-tl-decor-size);
  opacity: var(--ccg-ab-tl-decor-opacity);
  pointer-events: none;
  z-index: 1;
  transition: var(--ccg-t-decor);
}

.ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__decor { right: var(--ccg-ab-tl-decor-inset); }
.ccg-ab-tl__entry:nth-child(even) .ccg-ab-tl__decor { left: var(--ccg-ab-tl-decor-inset); }
.ccg-ab-tl__entry:last-child .ccg-ab-tl__decor { opacity: var(--ccg-ab-tl-decor-now-opacity); }
.ccg-ab-tl__entry:hover .ccg-ab-tl__decor { opacity: var(--ccg-ab-tl-decor-hover-opacity); }

/* The five decorations' own motion. Every one of these is CSS reaching into an
   inline graphic through a class; none of the five carries a SMIL animation.
   The delays are the live sheet's `nth-child` sets, written out. */
.ccg-ab-seed__leaf-a {
  transform-box: fill-box;
  transform-origin: 120px 150px;
  animation: var(--ccg-ab-seed-leaf-a-motion);
}

.ccg-ab-seed__leaf-b {
  transform-box: fill-box;
  transform-origin: 120px 130px;
  animation: var(--ccg-ab-seed-leaf-b-motion);
}

.ccg-ab-seed__sparks circle {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-ab-seed-spark-motion);
}
.ccg-ab-seed__sparks circle:nth-child(2) { animation-delay: 0.7s; }
.ccg-ab-seed__sparks circle:nth-child(3) { animation-delay: 1.4s; }
.ccg-ab-seed__sparks circle:nth-child(4) { animation-delay: 2.1s; }

.ccg-ab-bars rect {
  transform-box: fill-box;
  transform-origin: bottom;
  animation: var(--ccg-ab-bars-motion);
}
.ccg-ab-bars rect:nth-child(2) { animation-delay: 0.25s; }
.ccg-ab-bars rect:nth-child(3) { animation-delay: 0.5s; }
.ccg-ab-bars rect:nth-child(4) { animation-delay: 0.75s; }
.ccg-ab-bars rect:nth-child(5) { animation-delay: 1s; }
.ccg-ab-bars rect:nth-child(6) { animation-delay: 1.25s; }

.ccg-ab-donut__slice {
  transform-box: fill-box;
  transform-origin: 120px 120px;
  animation: var(--ccg-ab-donut-motion);
}
.ccg-ab-donut__slice:nth-of-type(2) { animation-delay: 1s; }
.ccg-ab-donut__slice:nth-of-type(3) { animation-delay: 2s; }
.ccg-ab-donut__slice:nth-of-type(4) { animation-delay: 3s; }

.ccg-ab-donut__ring {
  transform-box: fill-box;
  transform-origin: 120px 120px;
  animation: var(--ccg-ab-donut-ring-motion);
}

.ccg-ab-pentagon__nodes > g {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-ab-node-motion);
}
.ccg-ab-pentagon__nodes > g:nth-child(2) { animation-delay: 0.55s; }
.ccg-ab-pentagon__nodes > g:nth-child(3) { animation-delay: 1.1s; }
.ccg-ab-pentagon__nodes > g:nth-child(4) { animation-delay: 1.65s; }
.ccg-ab-pentagon__nodes > g:nth-child(5) { animation-delay: 2.2s; }

.ccg-ab-pentagon__frame {
  transform-box: fill-box;
  transform-origin: 120px 115px;
  animation: var(--ccg-ab-pentagon-motion);
}

/* The two rings around the Freiburg marker, 0.9 s apart. The live sheet gives
   them two classes and one keyframe; the ordinal carries the offset here, for
   the reason the timeline's sides are ordinals too. */
.ccg-ab-dach__pulse {
  transform-box: fill-box;
  transform-origin: 78px 118px;
  animation: var(--ccg-ab-dach-pulse-motion);
}
.ccg-ab-dach__pulse:nth-of-type(2) { animation-delay: 0.9s; }

/* Germany, Austria and Switzerland breathing on a one-second stagger. The live
   keyframe reads `var(--start-opacity, 0.5)` for its resting value and nothing
   on the live page ever sets that property, so all three rest at 0.5 there and
   all three rest at 0.5 here - the fallback is the value, and carrying the
   custom property would carry a seam nobody uses. */
.ccg-ab-dach__shape {
  transform-box: fill-box;
  transform-origin: center;
  animation: var(--ccg-ab-dach-breathe-motion);
}
.ccg-ab-dach__shape:nth-of-type(2) { animation-delay: 1s; }
.ccg-ab-dach__shape:nth-of-type(3) { animation-delay: 2s; }

@keyframes abSeedLeafA { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(-4deg); } }
@keyframes abSeedLeafB { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(4deg); } }
@keyframes abSeedSparkle {
  0%, 100% { opacity: 0.3; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.4); }
}
@keyframes abBarPulse {
  0%, 100% { transform: scaleY(1); opacity: 1; }
  50%      { transform: scaleY(1.06); opacity: 0.75; }
}
@keyframes abDonutBreathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.4; }
}
@keyframes abDonutRing {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes abNodePulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.18); }
}
@keyframes abPentagonRotate {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes abLocPulse {
  0%   { transform: scale(0.55); opacity: 0.75; stroke-width: 1.5; }
  100% { transform: scale(1.45); opacity: 0; stroke-width: 0.5; }
}
@keyframes abDachBreathe {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 0.75; }
}

/* --- the quote band, 561, twice ---------------------------------------------
 *
 * ONE CONSTRUCTION AND THE PAGE USES IT TWICE. Sections 2 and 6 differ in
 * nothing but their words, so there is one rule set here and the page names it
 * twice.
 *
 * THE 160 PX AT THE BOTTOM IS THE SECTION'S AND NOT THE PORT'S, and it is why
 * this is the one family on the site whose port carries no 128. The live section
 * is 561.27 with a port of 401.27; the 160 is `padding-bottom: 10rem` on the
 * section itself and it exists to clear the wave that is drawn across the bottom
 * of the band. Folding it into the port would move the words 80 px down.
 */
.ccg-ab-quote-section {
  background: var(--ccg-ab-quote-bg);
  padding-bottom: var(--ccg-ab-quote-drop);
}

.ccg-ab-quote-section .ccg-sv__port { padding: var(--ccg-ab-quote-port); }

.ccg-ab-quote {
  position: relative;
  max-width: var(--ccg-ab-quote-width);
  margin: 0 auto;
  padding: var(--ccg-ab-quote-pad);
  text-align: center;
}

/* A 96 px glyph on a 57.6 line box: the mark overflows its own line upward,
   which is what puts it that close to the first line of the quote. */
.ccg-ab-quote__mark {
  display: block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-quote-mark-size);
  font-weight: var(--ccg-ab-quote-mark-weight);
  line-height: var(--ccg-ab-quote-mark-line);
  color: var(--ccg-hero-heading);
  margin-bottom: var(--ccg-ab-quote-mark-space);
  user-select: none;
}

/* INTER 700, MEASURED ON THE LIVE ELEMENT AND SHIPPED BY THIS THEME.
   css/fonts.css declares Inter 500, 600, 700, 800 and 900; 500 and 700 arrived
   with this page. Stated as measured rather than rounded to a weight that was
   already there - see the head of this block, and design.md 9.5 item 1 for the
   re-measurement and for what this comment used to say. */
.ccg-ab-quote__text {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-quote-size);
  line-height: var(--ccg-ab-quote-line);
  font-weight: var(--ccg-ab-quote-weight);
  letter-spacing: var(--ccg-ab-quote-tracking);
  color: var(--ccg-hero-heading);
  margin: 0 auto var(--ccg-ab-quote-space);
  padding: var(--ccg-ab-quote-text-pad);
  max-width: var(--ccg-ab-quote-text-width);
  text-wrap: balance;
}

.ccg-ab-quote__attr {
  display: inline-block;
  font-size: var(--ccg-ab-quote-attr-size);
  font-weight: var(--ccg-ab-quote-attr-weight);
  letter-spacing: var(--ccg-ab-quote-attr-tracking);
  text-transform: uppercase;
  color: var(--ccg-ab-accent);
}

/* --- the mission block, 722 -------------------------------------------------
 *
 * TWO COLUMNS BY `column-count` AND NOT BY GRID. The live body is one 820-wide
 * element carrying both paragraphs, with `column-count: 2` and a 3 rem gutter -
 * which is where the 386 column width comes from and why a paragraph can break
 * across the two. A two-column grid would put one paragraph in each and would be
 * a different block at a different height.
 *
 * THE COMPASS IS CLIPPED ON PURPOSE. It is 640 square hung at `right: -120px` of
 * an 1100 wrap, so 120 px of it are outside the wrap and the section's own
 * `overflow: hidden` takes them off. What a visitor sees is a compass entering
 * from the right edge at 12 per cent opacity, which is the composition.
 */
.ccg-ab-mission {
  position: relative;
  max-width: var(--ccg-ab-mission-width);
  margin: 0 auto;
  padding: var(--ccg-ab-mission-pad);
}

.ccg-ab-mission__compass {
  position: absolute;
  top: 50%;
  right: var(--ccg-ab-compass-inset);
  transform: translateY(-50%);
  width: var(--ccg-ab-compass-size);
  height: var(--ccg-ab-compass-size);
  pointer-events: none;
  opacity: var(--ccg-ab-compass-opacity);
  z-index: 0;
  filter: var(--ccg-ab-compass-shadow);
}

.ccg-ab-mission__compass svg { width: 100%; height: 100%; }

.ccg-ab-mission__compass .ccg-ab-compass__ring {
  transform-origin: 100px 100px;
  animation: var(--ccg-ab-compass-ring-motion);
}

.ccg-ab-mission__compass .ccg-ab-compass__needle {
  transform-origin: 100px 100px;
  animation: var(--ccg-ab-compass-needle-motion);
}

@keyframes abCompassRing {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

@keyframes abCompassNeedle {
  0%   { transform: rotate(0deg); }
  20%  { transform: rotate(22deg); }
  40%  { transform: rotate(-8deg); }
  60%  { transform: rotate(15deg); }
  80%  { transform: rotate(-4deg); }
  100% { transform: rotate(0deg); }
}

/* Both text blocks are lifted over the compass rather than the compass being
   pushed under them, which is the live stacking and is why they carry a z-index
   of their own. */
.ccg-ab-mission__header,
.ccg-ab-mission__body {
  position: relative;
  z-index: 2;
}

.ccg-ab-mission__header {
  max-width: var(--ccg-ab-mission-head-width);
  margin-bottom: var(--ccg-ab-mission-head-space);
}

/* The eyebrow carries a 2 px rule under it, which is the one place on this page
   where a label is underlined rather than only coloured. */
.ccg-ab-mission__eyebrow {
  display: inline-block;
  font-size: var(--ccg-ab-mission-eyebrow-size);
  font-weight: var(--ccg-ab-eyebrow-weight);
  letter-spacing: var(--ccg-ab-mission-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent);
  padding-bottom: var(--ccg-ab-mission-eyebrow-pad);
  margin-bottom: var(--ccg-ab-mission-eyebrow-space);
  border-bottom: 2px solid var(--ccg-accent);
}

.ccg-ab-mission__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-h2-size);
  line-height: var(--ccg-ab-h2-line);
  font-weight: var(--ccg-ab-h2-weight);
  letter-spacing: var(--ccg-ab-h2-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-mission-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  text-wrap: balance;
}

/* `font-style: normal` because it is an `<em>` in the live markup and is not
   rendered italic there: the element carries the emphasis for a reader who gets
   no CSS, and the clipped gradient carries it for one who does. */
.ccg-ab-mission__accent {
  font-style: normal;
  background: var(--ccg-ab-mission-accent-fill);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.ccg-ab-mission__dims {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7em;
  padding: var(--ccg-ab-mission-dims-pad);
  background: var(--ccg-ab-mission-dims-fill);
  border: 1px solid var(--ccg-ab-mission-dims-border);
  border-radius: var(--ccg-ab-mission-dims-radius);
}

.ccg-ab-mission__dim {
  font-size: var(--ccg-ab-mission-dim-size);
  font-weight: var(--ccg-ab-mission-dim-weight);
  letter-spacing: var(--ccg-ab-mission-dim-tracking);
  color: var(--ccg-accent);
}

.ccg-ab-mission__sep {
  color: var(--ccg-ab-mission-sep-color);
  font-weight: var(--ccg-ab-mission-sep-weight);
}

.ccg-ab-mission__body {
  max-width: var(--ccg-ab-mission-head-width);
  column-count: 2;
  column-gap: var(--ccg-ab-mission-column-gap);
}

.ccg-ab-mission__text {
  font-size: var(--ccg-ab-mission-text-size);
  line-height: var(--ccg-ab-mission-text-line);
  color: var(--ccg-ab-body);
  margin: 0 0 var(--ccg-ab-mission-text-space);
  break-inside: avoid;
}

.ccg-ab-mission__text:last-child { margin-bottom: 0; }

.ccg-ab-mission__text strong {
  color: var(--ccg-heading);
  font-weight: var(--ccg-ab-mission-strong-weight);
}

/* --- the spectrum, 1052 -----------------------------------------------------
 *
 * A CENTRED HEADER AND ONE `<svg>`, and the svg is 86 shapes and 22 animations
 * of which eleven are CSS keyframes reached through the classes below. Nothing
 * about the graphic's geometry is here: it is all in the markup, because it was
 * copied from the live document rather than re-drawn.
 *
 * `.ccg-about-spec-outlet` IS NOT REPRODUCED, AND THAT IS NOT AN OMISSION. The
 * live sheet fades the output stream and the destination card in through that
 * class and no element on the live page carries it - the group is called
 * `ccg-about-spec-output`. So on the original those two parts do not fade; they
 * are simply there, and they are simply there here.
 */
.ccg-ab-spec {
  max-width: var(--ccg-ab-spec-width);
  margin: 0 auto;
  padding: var(--ccg-ab-spec-pad);
}

.ccg-ab-spec__header {
  text-align: center;
  margin-bottom: var(--ccg-ab-spec-head-space);
}

.ccg-ab-spec__eyebrow {
  display: inline-block;
  font-size: var(--ccg-ab-label-size);
  font-weight: var(--ccg-ab-eyebrow-weight);
  letter-spacing: var(--ccg-ab-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent);
  margin-bottom: var(--ccg-ab-spec-eyebrow-space);
}

.ccg-ab-spec__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-spec-title-size);
  line-height: var(--ccg-ab-spec-title-line);
  font-weight: var(--ccg-ab-h2-weight);
  letter-spacing: var(--ccg-ab-spec-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-spec-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  text-wrap: balance;
}

.ccg-ab-spec__sub {
  font-size: var(--ccg-ab-spec-sub-size);
  line-height: var(--ccg-ab-spec-sub-line);
  color: var(--ccg-ab-body);
  margin: 0 auto;
  max-width: var(--ccg-ab-spec-sub-width);
}

/* `display: block` is load-bearing and is not tidiness. An `<svg>` is inline by
   default, which puts it in a line box - so its own top margin does not collapse
   with the header's bottom margin and the line box adds a descender under it.
   Between them that is 22 px on a section measured at 1052. */
.ccg-ab-spec__svg {
  display: block;
  width: 100%;
  height: auto;
  margin-top: var(--ccg-ab-spec-svg-space);
  overflow: visible;
}

/* The narrow drawing is hidden HERE rather than shown in its own band, so a
   window this file has no block for gets the arrangement the rest of the file
   was written against - the same way `.ccg-home-hero__constellation--narrow` is
   declared, and for the same reason. */
.ccg-ab-spec__svg--narrow { display: none; }

/* The five streams draw themselves over two seconds on a 0.15 s stagger. 2000 is
   the live `stroke-dasharray`, chosen there to be longer than the longest of the
   five paths. */
.ccg-ab-spec__stream {
  stroke-dasharray: 2000;
  stroke-dashoffset: 2000;
  animation: var(--ccg-ab-spec-draw-motion);
}
.ccg-ab-spec__stream:nth-of-type(1) { animation-delay: 0.2s; }
.ccg-ab-spec__stream:nth-of-type(2) { animation-delay: 0.35s; }
.ccg-ab-spec__stream:nth-of-type(3) { animation-delay: 0.5s; }
.ccg-ab-spec__stream:nth-of-type(4) { animation-delay: 0.65s; }
.ccg-ab-spec__stream:nth-of-type(5) { animation-delay: 0.8s; }

.ccg-ab-spec__source {
  opacity: 0;
  animation: var(--ccg-ab-spec-fade-motion);
}
.ccg-ab-spec__source:nth-of-type(1) { animation-delay: 0.4s; }
.ccg-ab-spec__source:nth-of-type(2) { animation-delay: 0.55s; }
.ccg-ab-spec__source:nth-of-type(3) { animation-delay: 0.7s; }
.ccg-ab-spec__source:nth-of-type(4) { animation-delay: 0.85s; }
.ccg-ab-spec__source:nth-of-type(5) { animation-delay: 1s; }

.ccg-ab-spec__label {
  opacity: 0;
  animation: var(--ccg-ab-spec-fade-motion);
}
.ccg-ab-spec__label:nth-of-type(1) { animation-delay: 1s; }
.ccg-ab-spec__label:nth-of-type(2) { animation-delay: 1.15s; }
.ccg-ab-spec__label:nth-of-type(3) { animation-delay: 1.3s; }
.ccg-ab-spec__label:nth-of-type(4) { animation-delay: 1.45s; }
.ccg-ab-spec__label:nth-of-type(5) { animation-delay: 1.6s; }

.ccg-ab-spec__tick {
  opacity: 0;
  animation: var(--ccg-ab-spec-tick-motion);
}

.ccg-ab-spec__hub-ring {
  transform-origin: 1080px 300px;
  animation: var(--ccg-ab-spec-ring-motion);
}

.ccg-ab-spec__hub {
  opacity: 0;
  transform: scale(0.4);
  transform-origin: 1080px 300px;
  animation: var(--ccg-ab-spec-hub-motion);
}

.ccg-ab-spec__hub-pulse {
  transform-origin: 1080px 300px;
  animation: var(--ccg-ab-spec-pulse-motion);
}

/* AND THE THREE HUB RULES ABOVE ARE THE ONE THING THE SHARED CLASS NAMES DO NOT
   CARRY ACROSS, BECAUSE THEY NAME A POINT AND NOT A BEHAVIOUR.
   `transform-origin` on an SVG element resolves in that element's own viewBox
   units - `transform-box` is `view-box` for SVG - so `1080px 300px` is the hub
   of the WIDE field, `0 0 1500 600`. The narrow drawing reuses these class names
   on purpose, and its field is `0 0 400 912` with the hub at 200, 596. Left
   alone, the same three declarations put the narrow drawing's rotation centre
   928 units away from the circles it turns.

   IT WAS PHOTOGRAPHED AND NOT COUNTED, WHICH IS THE POINT OF THE REPAIR.
   `.ccg-ab-spec__svg` is `overflow: visible`, so nothing clipped the result: the
   dashed ring swung out of the field on its 45 s cycle, the pulse travelled off
   the left edge every 3 s instead of expanding around the hub, and the hub body
   flew in from outside the drawing. A census of animations saw five graphics and
   seventy-four SMIL nodes running in both states, because a count of what is
   declared cannot see where it is drawn. */
.ccg-ab-spec__svg--narrow .ccg-ab-spec__hub-ring,
.ccg-ab-spec__svg--narrow .ccg-ab-spec__hub,
.ccg-ab-spec__svg--narrow .ccg-ab-spec__hub-pulse {
  transform-origin: 200px 596px;
}

@keyframes abSpecDraw { to { stroke-dashoffset: 0; } }
@keyframes abSpecFade { to { opacity: 1; } }
@keyframes abSpecHubIn { to { opacity: 1; transform: scale(1); } }
@keyframes abSpecHubPulse {
  0%   { transform: scale(0.95); opacity: 0.6; }
  100% { transform: scale(1.6); opacity: 0; }
}
@keyframes abSpecRing {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* --- the method cards, 936 --------------------------------------------------
 *
 * THREE ACROSS THE FULL 1200 RAIL, 22.4 APART, WHICH IS WHERE 385.06 COMES FROM:
 * 1200 = 3 x 385.0667 + 2 x 22.4. The header above them is 720 centred on the
 * same rail, which puts it at x = 600.
 *
 * THE CARD IS THE THIRD OF FIVE LOOK-ALIKES AND ITS SEPARATING VALUES ARE HERE.
 * Radius 14 where three of the other four are 16; padded `32 32 28.8` where the
 * Kontakt card is `32 28.8 27.2`; shadowed `rgba(27,42,74,.04) 0 8 28` where the
 * Kontakt card is `.08 0 12 36`; title 20 where the Kontakt card is 20.8. The
 * chip is 56 at radius 12, which the Kontakt card also is - that one property is
 * shared and the other four are not. `data-ccg-card="methode"` on the element is
 * what lets a check read those four off the rendered card.
 *
 * THE 3 PX BAR ACROSS THE TOP IS A `::before` AND CARRIES NO TEXT, so it is here
 * and not in the markup. It is clipped by the card's own `overflow: hidden`,
 * which is what rounds its two top corners with the card's.
 */
.ccg-ab-scene {
  display: grid;
  grid-template-columns: 1fr;
  /* THE 48 IS THE LIVE GRID'S OWN ROW GAP AND IT IS EASY TO LOSE. The live
     `.ccg-about-scene` states `gap: 3rem` and the centred variant overrides only
     the columns, so the gap survives - and because a grid container's children
     do not collapse their margins, the space between the header and the card row
     is the paragraph's 17.6, plus 48, plus the row's 38.4. Dropping the gap and
     letting the two margins collapse takes 65 px out of a section measured at
     936, which is five times the tolerance. */
  gap: var(--ccg-ab-scene-gap);
  max-width: var(--ccg-ab-scene-width);
  margin: 0 auto;
  text-align: center;
  position: relative;
}

.ccg-ab-scene__body {
  max-width: var(--ccg-ab-scene-body-width);
  margin: 0 auto;
  position: relative;
  z-index: 1;
}

/* The label is the one on this page that is boxed by a rule under it AND ends
   the header's first line; the mission eyebrow is the other. */
.ccg-ab-scene__label {
  display: inline-block;
  font-size: var(--ccg-ab-label-size);
  font-weight: var(--ccg-ab-eyebrow-weight);
  letter-spacing: var(--ccg-ab-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent);
  margin-bottom: var(--ccg-ab-scene-label-space);
  padding-bottom: var(--ccg-ab-scene-label-pad);
  border-bottom: 2px solid var(--ccg-accent);
}

.ccg-ab-scene__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-h2-size);
  line-height: var(--ccg-ab-h2-line);
  font-weight: var(--ccg-ab-h2-weight);
  letter-spacing: var(--ccg-ab-h2-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-scene-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  text-wrap: balance;
}

.ccg-ab-scene__text {
  font-size: var(--ccg-ab-scene-text-size);
  line-height: var(--ccg-ab-scene-text-line);
  color: var(--ccg-ab-body);
  margin: 0 0 var(--ccg-ab-scene-text-space);
}

.ccg-ab-method-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ccg-ab-method-gap);
  width: 100%;
  margin: var(--ccg-ab-method-grid-space) auto 0;
  text-align: left;
}

.ccg-ab-method {
  position: relative;
  background: var(--ccg-page);
  border: 1px solid var(--ccg-ab-method-border);
  border-radius: var(--ccg-ab-method-radius);
  padding: var(--ccg-ab-method-padding);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
  transition: var(--ccg-t-method);
  overflow: hidden;
}

.ccg-ab-method::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--ccg-ab-method-bar-height);
  background: var(--ccg-ab-method-bar-fill);
  opacity: var(--ccg-ab-method-bar-opacity);
}

.ccg-ab-method:hover, .ccg-ab-method:focus-within {
  transform: translateY(var(--ccg-ab-method-lift));
  box-shadow: var(--ccg-ab-method-shadow-hover);
  border-color: var(--ccg-ab-method-border-hover);
}

.ccg-ab-method__chip {
  width: var(--ccg-ab-method-chip-size);
  height: var(--ccg-ab-method-chip-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ccg-ab-method-chip-fill);
  border-radius: var(--ccg-ab-method-chip-radius);
  margin-bottom: var(--ccg-ab-method-chip-space);
}

.ccg-ab-method__num {
  display: block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-method-num-size);
  font-weight: var(--ccg-ab-method-num-weight);
  letter-spacing: var(--ccg-ab-method-num-tracking);
  color: var(--ccg-ab-eyebrow-quiet);
  margin-bottom: var(--ccg-ab-method-num-space);
}

.ccg-ab-method__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-method-title-size);
  line-height: var(--ccg-ab-method-title-line);
  font-weight: var(--ccg-ab-method-title-weight);
  letter-spacing: var(--ccg-ab-method-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-method-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

.ccg-ab-method__text {
  font-size: var(--ccg-ab-method-text-size);
  line-height: var(--ccg-ab-method-text-line);
  color: var(--ccg-ab-body);
  margin: 0;
}

.ccg-ab-method__mark { display: block; }

/* --- the closing block, 707 -------------------------------------------------
 *
 * AN 820 COLUMN CENTRED ON THE 1200 RAIL, on the only `#F7F7F5` ground on this
 * page. Six things in a row, each with its own spacing measured off the live
 * element, and two of the six - the region badge and the signature - exist on
 * no other page of this site.
 */
.ccg-ab-close-section { background: var(--ccg-card-bg); }

.ccg-ab-close {
  position: relative;
  max-width: var(--ccg-ab-close-width);
  margin: 0 auto;
  text-align: center;
  padding: var(--ccg-ab-close-pad);
}

.ccg-ab-close__deco {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-bottom: var(--ccg-ab-close-deco-space);
}

.ccg-ab-close__bar {
  display: block;
  width: var(--ccg-ab-close-bar-width);
  height: 2px;
  background: var(--ccg-ab-close-bar-fill);
  border-radius: 1px;
}

.ccg-ab-close__dot {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ccg-ab-close-dot-fill);
  box-shadow: var(--ccg-ab-close-dot-halo);
}

.ccg-ab-close__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-close-title-size);
  line-height: var(--ccg-ab-close-title-line);
  font-weight: var(--ccg-ab-h2-weight);
  letter-spacing: var(--ccg-ab-close-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ab-close-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  text-wrap: balance;
}

.ccg-ab-close__text {
  font-size: var(--ccg-ab-close-text-size);
  line-height: var(--ccg-ab-close-text-line);
  color: var(--ccg-ab-body);
  margin: 0 0 var(--ccg-ab-close-text-space);
  text-wrap: balance;
}

.ccg-ab-close__actions {
  margin-top: var(--ccg-ab-close-actions-space);
  display: flex;
  justify-content: center;
}

/* Radius 50 and not 999, a negative-spread shadow, and an inline SVG for the
   arrow: three things that separate this control from the service pill and from
   the Karriere button, all three measured. */
.ccg-ab-close__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--ccg-ab-close-cta-gap);
  padding: var(--ccg-ab-close-cta-pad);
  background: var(--ccg-ab-close-cta-fill);
  color: var(--ccg-hero-heading);
  font-size: var(--ccg-ab-close-cta-size);
  font-weight: var(--ccg-ab-close-cta-weight);
  letter-spacing: var(--ccg-ab-close-cta-tracking);
  text-decoration: none;
  border-radius: var(--ccg-ab-close-cta-radius);
  box-shadow: var(--ccg-ab-close-cta-shadow);
  transition: var(--ccg-t-button);
}

.ccg-ab-close__cta:hover, .ccg-ab-close__cta:focus-visible {
  transform: translateY(var(--ccg-ab-close-cta-lift));
  box-shadow: var(--ccg-ab-close-cta-shadow-hover);
  color: var(--ccg-hero-heading);
  text-decoration: none;
}

.ccg-ab-close__arrow {
  display: inline-block;
  color: currentColor;
  transition: var(--ccg-t-arrow);
}

.ccg-ab-close__cta:hover .ccg-ab-close__arrow,
.ccg-ab-close__cta:focus-visible .ccg-ab-close__arrow {
  transform: translateX(var(--ccg-ab-close-arrow-shift));
}

/* A route the running application cannot find. Same treatment as
   `.ccg-sv-pill--missing`, for the same reason: it keeps its shape so the block
   still reads, and loses the gradient and the shadow so it does not read as a
   control that works. */
.ccg-ab-close__cta--missing {
  background: var(--ccg-page);
  color: var(--ccg-nav-missing-color);
  border: 1px solid var(--ccg-card-border);
  box-shadow: none;
  cursor: default;
}

.ccg-ab-close__trust {
  list-style: none;
  margin: var(--ccg-ab-close-trust-space) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ccg-ab-close-trust-gap);
  font-size: var(--ccg-ab-close-trust-size);
  color: var(--ccg-ab-body);
}

.ccg-ab-close__trust li {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: var(--ccg-ab-close-trust-weight);
}

.ccg-ab-close__trust-mark { flex-shrink: 0; }

.ccg-ab-region {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  margin: var(--ccg-ab-region-space) auto 0;
  max-width: var(--ccg-ab-region-width);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ab-region-size);
  font-weight: var(--ccg-ab-region-weight);
  line-height: var(--ccg-ab-region-line);
  color: var(--ccg-accent);
  text-align: center;
}

.ccg-ab-region strong {
  font-weight: var(--ccg-ab-region-strong-weight);
  color: var(--ccg-accent);
}

/* The pin is the one gold thing on a page that is otherwise entirely purple, and
   that is measured rather than a slip: `#C9A227` on the live element. */
.ccg-ab-region__pin {
  display: inline-flex;
  align-items: center;
  color: var(--ccg-ab-region-pin-color);
  flex-shrink: 0;
}

.ccg-ab-region__pin svg { display: block; }

.ccg-ab-sig {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--ccg-ab-sig-gap);
  margin-top: var(--ccg-ab-sig-space);
  font-size: var(--ccg-ab-sig-size);
  font-weight: var(--ccg-ab-sig-weight);
  letter-spacing: var(--ccg-ab-sig-tracking);
  text-transform: uppercase;
  color: var(--ccg-accent);
}

.ccg-ab-sig__line {
  display: block;
  flex: 1;
  max-width: var(--ccg-ab-sig-line-width);
  height: 1px;
  background: var(--ccg-ab-sig-line-fill);
}

/* --- the inner pages, part five: /about/kontakt/ -----------------------------
 *
 * ITS OWN FAMILY. It was the only page of this site with a form until OP#4715
 * removed it; three sections still,
 * 3183 tall, and a namespace disjoint from the five service pages, from
 * `/about/` and from `/karriere/`: it borrows the shared frame - `.ccg-sv`,
 * `.ccg-sv__port`, `.ccg-measure`, `.ccg-sv__wave` - and nothing else. Every
 * rule below was read off the live page at 1920 on 25 August 2026.
 *
 * ONE DEFECT OF THE ORIGINAL IS REPRODUCED HERE, NOT REPAIRED, and it is named
 * where it is built so that repairing it has to be a decision:
 *
 *   1. `.ccg-ko-hero__port` keeps the hero's 1200 rail at x = 0 instead of
 *      x = 360. See the note over that rule.
 *
 * A SECOND ONE WAS REPRODUCED UNTIL OP#4715 - the form plugin's square blue
 * submit control labelled in English - and left with the form.
 *
 * WHAT THIS PAGE DOES NOT HAVE, WHICH IS AS MEASURED AS WHAT IT HAS. Only
 * section 0 carries a mesh: the `::before` of both content sections computes to
 * `content: none` and neither has a background of its own. There is no particle
 * layer anywhere on the page - the six-circle field belongs to the five service
 * pages and to `/about/` - no dot field, and no wave other than the hero's.
 * There is one parallax layer, the hero photograph, at the rate the live holder
 * carries.
 *
 * THE CARD HERE IS THE FIFTH OF FIVE AND IS NOT ANY OF THE OTHER FOUR. It is
 * 478.8 x 260.81, padded `32 28.8 27.2`, radius 16, shadow
 * `rgba(27,42,74,.08) 0 12 36`, chip 56 at radius 12, title 20.8 w800. The
 * `/about/` method card it most resembles is padded `32 32 28.8` at radius 14
 * with a `rgba(27,42,74,.04) 0 8 28` shadow and a 20 title. Five properties
 * differ; `data-ccg-card="kontakt"` on the element is what lets a check read
 * them off the rendered card rather than off a class name this change invented.
 */

/* --- the hero, 820, and the mis-centring it is built to keep -----------------
 *
 * FOUR LAYERS AND THE ORDER IS THE MEASUREMENT: the photograph inside a wrapper
 * that carries the blur and the oversize, the mesh as the section's own
 * ::before, the wave, then the content. The photograph is its own element
 * inside the wrapper for the reason the service hero gives - the element
 * `[data-ccg-parallax]` writes on must have no filter of its own, which
 * test/motion.sh reads at twenty scroll positions.
 *
 * THE SECTION IS A CENTRING FLEX BOX AND NOT A PORT WITH PADDING. The live
 * section is `min-height: 820px` with `align-items: center` over a block of
 * 644.38, which is what puts the block at y = 87.81 - exactly half of the 175.62
 * that is left over. Writing it as padding would be a number nobody measured.
 */
.ccg-ko-hero {
  display: flex;
  align-items: center;
  min-height: var(--ccg-ko-hero-height);
  background: var(--ccg-ko-hero-bg);
  color: var(--ccg-hero-heading);
  isolation: isolate;
}

.ccg-ko-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  filter: var(--ccg-ko-hero-photo-filter);
  transform: scale(var(--ccg-ko-hero-photo-scale));
  transform-origin: center;
}

.ccg-ko-hero__photo {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
}

/* The mesh. Five layers, not the service hero's two: four radials and a
   vertical darkening over them, blurred 20 and drifting over 11 s. The live
   section's class list also contains `bt_bb_background_overlay_top_dark_gradient`
   and nothing on the page answers to it - measured on the section, on both of
   its pseudo-elements, on the image holder and on its wrapper, all of which came
   back with no background image. What darkens the photograph is the
   `brightness(0.4)` on the wrapper and the last layer here. */
.ccg-ko-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--ccg-ko-hero-mesh);
  filter: var(--ccg-ko-hero-mesh-blur);
  animation: var(--ccg-ko-hero-mesh-motion);
}

@keyframes koMeshDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  25%      { transform: translate(-3%, 2%) scale(1.05); }
  50%      { transform: translate(2%, -2.5%) scale(0.97); }
  75%      { transform: translate(-1.5%, -1%) scale(1.03); }
}

/* The wave overhangs the section by a pixel. Measured: it is 376 tall with its
   top at y = 445 in a section that ends at 820, so its foot is at 821. The
   shared rule sits it flush; this is the one pixel of difference. */
.ccg-ko-hero__wave { bottom: var(--ccg-ko-hero-wave-drop); }

/* THE DEFECT, AND IT IS DELIBERATE. Every other section on this site puts its
   1200 rail at x = 360 - centred in a 1920 window. This one computes to
   `width: 1200px; margin: 0`, so the rail sits at x = 0..1200 and everything
   centred inside it lands at x = 600 instead of 960. The live section is the
   only one on the site that is missing the attribute letting content sit
   outside the boxed rail, and reproducing the page reproduces the mis-centring.
   `.ccg-measure` supplies `margin-left: auto; margin-right: auto`, and inside a
   flex container an auto margin centres the item - so undoing the two lines
   below is exactly the repair, and `RAIL_EXCEPTION` in test/lib/pages_read.py
   turns red when it is made. */
.ccg-ko-hero__port {
  position: relative;
  z-index: 3;
  margin-left: 0;
  margin-right: 0;
}

.ccg-ko-hero__wrap {
  max-width: var(--ccg-ko-hero-wrap-width);
  margin: 0 auto;
  padding: var(--ccg-ko-hero-wrap-pad);
  text-align: center;
}

.ccg-ko-hero__eyebrow {
  display: inline-block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-hero-label-size);
  font-weight: var(--ccg-ko-hero-label-weight);
  letter-spacing: var(--ccg-ko-hero-label-tracking);
  color: var(--ccg-ko-hero-label-color);
  background: var(--ccg-ko-hero-label-fill);
  border: 1px solid var(--ccg-ko-hero-label-border);
  border-radius: var(--ccg-ko-hero-label-radius);
  padding: var(--ccg-ko-hero-label-padding);
  margin-bottom: var(--ccg-ko-hero-label-space);
}

/* `text-wrap: balance` appears on three elements below - this heading, the
   paragraph under it and the form card's heading - and on the same three in the
   live sheet. It changes where a line breaks and not how tall the block is, so
   leaving it out measures identically and reads differently: the live paragraph
   breaks after "Immer auf" and one without it breaks after "von echten". */

/* THE ONE H1 ON THIS PAGE, AND IT IS NOT THE DISPLAY FACE. The theme's h1 is
   Spartan; this one is Inter 900 at 60.8/62.016 tracked -2.432, read off the
   live element. The descender pad is the original theme's own heading rule and
   is 6.08 here, a tenth of 60.8 - see `--ccg-sv-heading-descender`. Without it
   the heading's box is 131.28 instead of 137.36 and the whole hero is six
   pixels short. */
.ccg-ko-hero__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-hero-title-size);
  line-height: var(--ccg-ko-hero-title-line);
  font-weight: var(--ccg-ko-hero-title-weight);
  letter-spacing: var(--ccg-ko-hero-title-tracking);
  color: var(--ccg-hero-heading);
  margin: 0 0 var(--ccg-ko-hero-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
  text-wrap: balance;
}

.ccg-ko-hero__line { display: block; }

/* The second line. It is an `<em>` in the live markup and is not rendered
   italic there: the element carries the emphasis for a reader who gets no CSS
   and the gradient carries it for one who does. `background-clip: text` with a
   transparent fill is how the live rule paints it, and the fallback colour under
   it is the white the rest of the heading already has - so a browser that does
   not clip backgrounds to text renders a white line rather than an invisible
   one. */
.ccg-ko-hero__accent {
  display: block;
  font-style: normal;
  margin-top: var(--ccg-ko-hero-accent-lift);
  background: var(--ccg-ko-hero-accent-fill);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.ccg-ko-hero__sub {
  max-width: var(--ccg-ko-hero-sub-width);
  margin: 0 auto var(--ccg-ko-hero-sub-space);
  font-size: var(--ccg-ko-hero-sub-size);
  line-height: var(--ccg-ko-hero-sub-line);
  color: var(--ccg-ko-hero-sub-color);
  text-wrap: balance;
}

.ccg-ko-hero__trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ccg-ko-hero-trust-gap);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--ccg-ko-hero-trust-size);
  color: var(--ccg-ko-hero-sub-color);
}

/* The half-em inset on each item is measured rather than chosen: the live
   `li` carries `margin-left: 7.36px`, which is half of its own 14.72 font, and
   it is what puts the first of the three at x = 306.75 rather than at 299.39. */
.ccg-ko-hero__trust-item {
  display: inline-flex;
  align-items: center;
  gap: var(--ccg-ko-hero-trust-icon-gap);
  margin-left: var(--ccg-ko-hero-trust-inset);
  font-weight: var(--ccg-ko-hero-trust-weight);
}

.ccg-ko-hero__trust-item svg {
  flex-shrink: 0;
  color: var(--ccg-ko-hero-trust-icon-color);
}

/* --- the ways to reach the firm, 672 with four, one card row more with five ---
 *
 * THE ROW WAS 980 ON THE LIVE PAGE AND IS THE FULL 1200 RAIL SINCE OP#4715, as
 * the next steps under it are. It is PULLED 64 UP OUT OF ITS OWN PORT. Both were
 * measured: the live grid is 980 wide at x = 470 inside the 1200 rail at x = 360,
 * and it carries `margin-top: -64px`, which is what lets the first two cards
 * overlap the hero's wave. The port is 128 above and 64 below rather than 128
 * and 128 - this section and the one under it share one 128 between them and
 * keep half each.
 *
 * The negative margin is the reason the section is 672 while its content is
 * 544: the margin collapses out through `.ccg-measure`, so the port's content
 * box measures 480 and 128 + 480 + 64 is the section.
 */
.ccg-ko-wege .ccg-sv__port { padding: var(--ccg-ko-wege-port); }

.ccg-ko-wege__grid {
  position: relative;
  z-index: 5;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--ccg-ko-wege-grid-gap);
  max-width: var(--ccg-ko-wege-grid-width);
  margin: var(--ccg-ko-wege-grid-lift) auto 0;
}

/* FIVE WAYS AND NOT FOUR SINCE OP#4671, and the fifth spans the row. The firm's
   second address is a fifth card, which two by two leaves one card beside an
   empty cell. The last card of an odd count takes both tracks instead - the
   layout the operator chose from three rendered ones, and a departure from the
   live page's four that the site-theme requirement on this page names. Written
   against the count rather than against WhatsApp, so a sixth card closes the
   grid again without this rule having to know. At one column, below, it is one
   more card in the list. */
.ccg-ko-wege__grid > .ccg-ko-method:last-child:nth-child(odd) { grid-column: 1 / -1; }

/* THE FIFTH CARD CONSTRUCTION. See the note at the head of this block for what
   separates it from the other four. It is an anchor rather than an article
   because all four of them lead somewhere, and `color` and `text-decoration`
   are stated because the document's own anchor rule would otherwise underline
   a whole card on hover. */
.ccg-ko-method {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--ccg-page);
  border: 1px solid var(--ccg-ko-card-border);
  border-radius: var(--ccg-ko-method-radius);
  padding: var(--ccg-ko-method-padding);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
  color: var(--ccg-heading);
  text-decoration: none;
  transition: var(--ccg-t-method);
}

.ccg-ko-method:hover, .ccg-ko-method:focus-visible {
  transform: translateY(var(--ccg-ko-method-lift));
  border-color: var(--ccg-ko-method-border-hover);
  box-shadow: var(--ccg-ko-method-shadow-hover);
  color: var(--ccg-heading);
  text-decoration: none;
}

/* The bar across the top of the card. Three pixels tall, edge to edge over the
   whole 476.8 of the padding box, and at nine tenths opacity - all four values
   are the live pseudo-element's. */
.ccg-ko-method::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--ccg-ko-method-bar-height);
  background: var(--ccg-ko-card-bar-fill);
  opacity: var(--ccg-ko-method-bar-opacity);
}

.ccg-ko-method__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ccg-ko-method-chip-size);
  height: var(--ccg-ko-method-chip-size);
  border-radius: var(--ccg-ko-method-chip-radius);
  background: var(--ccg-ko-method-chip-fill);
  color: var(--ccg-ko-method-chip-ink);
  margin-bottom: var(--ccg-ko-method-chip-space);
}

.ccg-ko-method__eyebrow {
  display: block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-method-eyebrow-size);
  font-weight: var(--ccg-ko-method-eyebrow-weight);
  letter-spacing: var(--ccg-ko-method-eyebrow-tracking);
  color: var(--ccg-ko-method-eyebrow-color);
  margin-bottom: var(--ccg-ko-method-eyebrow-space);
}

/* The live title is a `<span>` and this one is an `<h3>`, which is a departure
   and is recorded as one: four cards whose whole content is a heading, a line
   of detail and a link belong in the document outline, and the check that reads
   the five card constructions apart looks for a heading element inside each.
   No descender pad here - the live element is not a heading and does not carry
   the original theme's heading rule, so adding it would make the card 2.08
   taller than the 260.81 it was measured at. */
.ccg-ko-method__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-method-title-size);
  line-height: var(--ccg-ko-method-title-line);
  font-weight: var(--ccg-ko-method-title-weight);
  letter-spacing: var(--ccg-ko-method-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ko-method-title-space);
  padding-bottom: 0;
}

.ccg-ko-method__sub {
  margin: 0 0 var(--ccg-ko-method-sub-space);
  font-size: var(--ccg-ko-method-sub-size);
  line-height: var(--ccg-ko-method-sub-line);
  color: var(--ccg-ko-method-sub-color);
}

/* `margin-top: auto` and not a spacer: the arrow sits on the foot of the card's
   padding box whatever the two lines above it wrapped to. */
.ccg-ko-method__arrow {
  display: flex;
  margin-top: auto;
  color: var(--ccg-ko-method-arrow-ink);
}

.ccg-ko-method__arrow svg { transition: var(--ccg-t-arrow); }

.ccg-ko-method:hover .ccg-ko-method__arrow svg,
.ccg-ko-method:focus-visible .ccg-ko-method__arrow svg {
  transform: translateX(var(--ccg-ko-method-arrow-shift));
}

/* --- the next steps and the social bar, across the rail ---------------------
 *
 * THE SECTION THAT STANDS WHERE THE FORM ROW STOOD (OP#4715). The live page sets
 * a form of 658.28 beside this card of 493.72 on `1.2fr 0.9fr`; the operator
 * removed the form on 2026-10-18, and the card and the social bar under it now
 * each take the whole 1200 rail. The port is the form row's own, 64 above and
 * 128 below, and the 24 between card and bar is the gap the side column had.
 */
.ccg-ko-next .ccg-sv__port { padding: var(--ccg-ko-next-port); }

.ccg-ko-next__stack {
  display: flex;
  flex-direction: column;
  gap: var(--ccg-ko-side-gap);
}

/* The eyebrow over the information card. The form's head carried the same
   element with a 14.4 override under it, the live sheet's own; the override
   left with the form (OP#4715) and this one keeps its 16. */
.ccg-ko-eyebrow {
  display: inline-block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-eyebrow-size);
  font-weight: var(--ccg-ko-eyebrow-weight);
  letter-spacing: var(--ccg-ko-eyebrow-tracking);
  color: var(--ccg-ko-eyebrow-color);
  padding-bottom: var(--ccg-ko-eyebrow-pad);
  border-bottom: var(--ccg-ko-eyebrow-rule) solid var(--ccg-ko-eyebrow-color);
  margin-bottom: var(--ccg-ko-eyebrow-space);
}

/* NO `:hover` RULE ON THIS CARD, AND IT IS A READING RATHER THAN AN OMISSION.
   Measured 17.09.2026 at 1920 on a container built from this tree: the element
   is a `div`, it is not a link, and it carries no link and no control at all inside it, so there is nothing
   on it for a pointer to answer. A card that lights up under a pointer and then
   does nothing when it is clicked is worse than one that stays still. It does
   take the resting edge further down this file, which is about a card reading as
   a card and not about a pointer. */
.ccg-ko-info-card {
  position: relative;
  overflow: hidden;
  background: var(--ccg-page);
  border: 1px solid var(--ccg-ko-card-border);
  border-radius: var(--ccg-ko-info-radius);
  padding: var(--ccg-ko-info-padding);
  box-shadow: var(--ccg-card-rest-veil), var(--ccg-card-rest-lip);
}

.ccg-ko-info-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--ccg-ko-info-bar-height);
  background: var(--ccg-ko-card-bar-fill);
}

.ccg-ko-info-card__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-info-title-size);
  line-height: var(--ccg-ko-info-title-line);
  font-weight: var(--ccg-ko-info-title-weight);
  letter-spacing: var(--ccg-ko-info-title-tracking);
  color: var(--ccg-heading);
  margin: 0 0 var(--ccg-ko-info-title-space);
  padding-bottom: var(--ccg-sv-heading-descender);
}

/* Three tracks across the rail (OP#4715); one column at the narrow band, in the
   1024 block further down, where three would get at most 110 px each. */
.ccg-ko-steps {
  display: grid;
  grid-template-columns: var(--ccg-ko-steps-columns);
  gap: var(--ccg-ko-steps-across-gap);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* The 8 on the left is the live item's own margin and is what makes the item
   426.13 inside a 434.13 block. It is not a list marker: the list has none. */
.ccg-ko-step {
  display: flex;
  align-items: flex-start;
  gap: var(--ccg-ko-step-gap);
  margin-left: var(--ccg-ko-step-inset);
}

.ccg-ko-step__n {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ccg-ko-step-n-size);
  height: var(--ccg-ko-step-n-size);
  border-radius: 50%;
  background: var(--ccg-ko-step-n-fill);
  color: var(--ccg-ko-step-n-ink);
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-step-n-text);
  font-weight: var(--ccg-ko-step-n-weight);
}

.ccg-ko-step__text {
  display: flex;
  flex-direction: column;
  gap: var(--ccg-ko-step-text-gap);
}

.ccg-ko-step__title {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-step-title-size);
  font-weight: var(--ccg-ko-step-title-weight);
  color: var(--ccg-heading);
}

.ccg-ko-step__sub {
  font-size: var(--ccg-ko-step-sub-size);
  line-height: var(--ccg-ko-step-sub-line);
  color: var(--ccg-ko-step-sub-color);
}

/* The social row. Radius 50 rather than the card's 16, which is what makes it a
   pill 72 tall rather than a small card - the live value, and the only rounded
   block on the page other than the hero's eyebrow. */
.ccg-ko-social {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--ccg-page);
  border: 1px solid var(--ccg-ko-card-border);
  border-radius: var(--ccg-ko-social-radius);
  padding: var(--ccg-ko-social-padding);
}

.ccg-ko-social__label {
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-ko-social-label-size);
  font-weight: var(--ccg-ko-social-label-weight);
  letter-spacing: var(--ccg-ko-social-label-tracking);
  text-transform: uppercase;
  color: var(--ccg-ko-social-label-color);
}

.ccg-ko-social__links {
  display: inline-flex;
  gap: var(--ccg-ko-social-icons-gap);
}

.ccg-ko-social__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ccg-ko-social-icon-size);
  height: var(--ccg-ko-social-icon-size);
  border-radius: 50%;
  background: var(--ccg-ko-social-icon-fill);
  color: var(--ccg-ko-social-icon-ink);
  text-decoration: none;
  transition: var(--ccg-t-social);
}

.ccg-ko-social__link:hover, .ccg-ko-social__link:focus-visible {
  background: var(--ccg-ko-social-icon-fill-hover);
  color: var(--ccg-ko-social-icon-ink-hover);
  transform: translateY(var(--ccg-ko-social-icon-lift));
  text-decoration: none;
}

/* --- the blog: the overview and one article -----------------------------------
 *
 * READ ON 2026-08-31 at a window width of 1920 px from
 * https://www.consulting-concept.de/blog/ and from
 * https://www.consulting-concept.de/die-psychologie-des-verkaufs/. Every value
 * below comes from a `--ccg-bl-*` token emitted out of ccg.yaml, so a check reads
 * it out of the response rather than out of this file;
 * openspec/changes/take-over-the-blog/design.md says which page each was read on
 * and what could not be read at all.
 *
 * THE OVERVIEW IS TWO COLUMNS AND THAT IS LOAD-BEARING. 900 px of content with a
 * 30 px gutter leaves 870, and 870 is the width of every card that was measured.
 * A one-column overview would be a different page, not a simpler one.
 */
.ccg-main--flush { padding-bottom: var(--ccg-bl-holder-trail); }

.ccg-bl-head {
  position: relative;
  min-height: var(--ccg-bl-head-height);
  background: var(--ccg-bl-head-bg);
  color: var(--ccg-bl-head-title-color);
/* `overflow-x: clip` AND `overflow-y: visible`, WHICH IS THE HAIRLINE REPAIR IN
   ITS THIRD FORM. The block at the head of this file explains the defect: a
   section's vertical clip edge, antialiased where it lands between two device
   rows, lets the dark ground under the wave through for one row. On /blog it read
   188,185,180 between two rows of 255,255,255 at device scale 1.6.
   Neither of the two moves used on the home page and on /about fits here. There
   is no full-bleed section below this one to lay a pixel down from - the next
   element is `.ccg-bl-holder`, 1260 px wide in an 1890 px window - and the wave
   already overhangs by a pixel, so it cannot be given another.
   What fits is taking the vertical clip off, because THIS section has nothing
   that needs it: measured on 14.09.2026 at 1890 x 900, the only child of
   `.ccg-bl-head` whose box passes either horizontal edge of the section is the
   wave, and it passes only the bottom one, by the one pixel `.ccg-bl-head__wave`
   is deliberately given. `overflow-x: clip` keeps the horizontal clip the
   photograph needs; `clip` rather than `hidden` because `hidden` on one axis
   forces `auto` on the other and `clip` does not.
   The picture at scale 1 is not merely close: a full-page photograph of /blog at
   1890 x 1.0 before and after this rule differs in NOUGHT rows out of 9093. */
  overflow-x: clip;
  overflow-y: visible;
  isolation: isolate;
}

/* The photograph is its own element inside a wrapper, which is the arrangement
   `.ccg-sv-hero__media` already uses and gives its reason for: the wrapper
   carries the oversize, so the element `[data-ccg-parallax]` writes on has no
   filter of its own. */
.ccg-bl-head__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.ccg-bl-head__photo {
  position: absolute;
  inset: -10% 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
}

.ccg-bl-head__wave {
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: auto;
  z-index: 2;
  pointer-events: none;
}

.ccg-bl-head__port {
  position: relative;
  z-index: 3;
  padding: var(--ccg-bl-head-pad) 0;
}

.ccg-bl-head__text {
  max-width: var(--ccg-bl-head-text-width);
  padding: var(--ccg-bl-head-lead) 0 var(--ccg-bl-head-trail);
}

.ccg-bl-head__title {
  margin: 0;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-head-title-size);
  line-height: var(--ccg-bl-head-title-line);
  font-weight: var(--ccg-bl-head-title-weight);
  letter-spacing: var(--ccg-bl-head-title-tracking);
  color: var(--ccg-bl-head-title-color);
}

.ccg-bl-head__sub {
  margin: var(--ccg-bl-head-sub-gap) 0 0;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-head-sub-size);
  line-height: var(--ccg-bl-head-sub-line);
  color: var(--ccg-bl-head-title-color);
}

.ccg-bl-holder {
  width: var(--ccg-bl-holder-width);
  max-width: 100%;
  margin: 0 auto;
  padding: var(--ccg-bl-holder-top) var(--ccg-bl-content-gutter) var(--ccg-bl-holder-bottom);
  display: flex;
  align-items: flex-start;
}

.ccg-bl-content {
  flex: 0 1 var(--ccg-bl-content-width);
  min-width: 0;
  padding-right: var(--ccg-bl-content-gutter);
}

.ccg-bl-side {
  flex: 0 0 var(--ccg-bl-side-width);
  min-width: 0;
  padding-left: var(--ccg-bl-side-gutter);
}

/* A CARD IS AS TALL AS ITS TALLER COLUMN, and that is the whole of why two of the
   ten are taller than the other eight - see design.md 1.3. `align-items: center`
   is the live holder's own value, and it is why a short text block does not
   shorten the card. */
.ccg-bl-card { margin-bottom: var(--ccg-bl-card-gap); }

.ccg-bl-card__row {
  display: flex;
  align-items: center;
  margin-left: calc(-1 * var(--ccg-bl-card-bleed));
  margin-right: calc(-1 * var(--ccg-bl-card-bleed));
}

.ccg-bl-card__media {
  flex: 1 1 var(--ccg-bl-card-media-share);
  min-width: 0;
  padding: 0 var(--ccg-bl-card-bleed);
}

.ccg-bl-card__text {
  flex: 1 1 var(--ccg-bl-card-text-share);
  min-width: 0;
  padding: 0 var(--ccg-bl-card-bleed);
}

.ccg-bl-card__image-link { display: block; }

/* The file is the 3:2 rendition the live overview serves, drawn at its own
   proportions rather than cropped: 509.2 wide gives 339.5 tall, which is the
   measured height. */
.ccg-bl-card__image {
  display: block;
  width: 100%;
  height: auto;
}

.ccg-bl-card__meta {
  margin: 0 0 var(--ccg-bl-card-meta-gap);
  padding-bottom: var(--ccg-bl-card-meta-trail);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-card-meta-size);
  line-height: var(--ccg-bl-card-meta-line);
  font-weight: var(--ccg-bl-card-meta-weight);
  color: var(--ccg-bl-link-color);
}

/* THE SEPARATOR BETWEEN TWO CATEGORIES, ON THE CARD AND ON THE ARTICLE, because
   the live site draws both with one rule - `a.btArticleCategory +
   a.btArticleCategory::before` - and two rules here would be able to disagree
   about a shape that has only ever been measured once.

   IT IS NOT A MIDDOT. This rule read `content: "\00b7"` with `padding: 0 0.4em`
   until 2026-08-31, and the deviation entry in design.md said the live card's
   separator "is a middot"; both were written from the article's own row having
   been measured and the card's having been assumed to differ. Re-read at 1920 px
   on `https://www.consulting-concept.de/blog/` and on
   `https://www.consulting-concept.de/echte-erfolgsgeschichten/`, the computed
   `::before` is the same in both places, to the value: `content: ""`,
   `display: inline-block`, 4 x 4, `border-radius: 50%`, `background
   rgb(24, 24, 24)`, `opacity .7`, `vertical-align: middle`,
   `margin: -2px 10px 0px`.

   IT IS DRAWN BY THE RULE AND NOT WRITTEN INTO THE MARKUP, so the words stay
   words - which is why the spans in both templates are written with no
   whitespace between them, exactly as the live markup has none: a newline
   between two spans would add a word space on each side of the circle and widen
   every row that carries one. */
.ccg-bl-card__category + .ccg-bl-card__category::before,
.ccg-post__category + .ccg-post__category::before {
  content: "";
  display: inline-block;
  width: var(--ccg-bl-meta-dot);
  height: var(--ccg-bl-meta-dot);
  margin: var(--ccg-bl-meta-dot-rise) var(--ccg-bl-meta-dot-gap) 0;
  border-radius: 50%;
  background: var(--ccg-text);
  opacity: var(--ccg-bl-meta-dot-fade);
  vertical-align: middle;
}

.ccg-bl-card__title {
  margin: 0 0 var(--ccg-bl-card-title-gap);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-card-title-size);
  line-height: var(--ccg-bl-card-title-line);
  font-weight: var(--ccg-bl-card-title-weight);
  letter-spacing: var(--ccg-bl-card-title-tracking);
  color: var(--ccg-text);
}

.ccg-bl-card__title-link {
  color: inherit;
  text-decoration: none;
}

.ccg-bl-card__title-link:hover { color: var(--ccg-bl-link-color); }

.ccg-bl-share {
  display: flex;
  align-items: center;
  margin-top: var(--ccg-bl-card-share-gap);
  height: var(--ccg-bl-share-size);
}

.ccg-bl-share--post { justify-content: flex-end; }

.ccg-bl-share__link {
  flex: none;
  width: var(--ccg-bl-share-size);
  height: var(--ccg-bl-share-size);
  margin-right: calc(var(--ccg-bl-share-step) - var(--ccg-bl-share-size));
  border-radius: 50%;
  background: var(--ccg-bl-share-fill);
  color: var(--ccg-bl-share-ink);
  display: flex;
  align-items: center;
  justify-content: center;
}

.ccg-bl-share__glyph {
  height: var(--ccg-bl-share-glyph);
  width: auto;
  display: block;
}

.ccg-bl-content__empty { margin: 0; }

/* --- the control that moves between the overview's pages -----------------------
 *
 * READ ON 2026-09-04 at 1920 px off https://www.consulting-concept.de/blog/ and
 * https://www.consulting-concept.de/blog/page/2/, on the element the page serves.
 * Not by asking for a class name: three class names were asked for in
 * `take-over-the-blog`, none matched, and the absence was written down as a fact
 * about the site - which is why seven articles were missing for two cycles and
 * why this rule exists at all.
 *
 * THE HOLDER KEEPS ITS HEIGHT WITH ONE LINK IN IT. It is 870 x 40 on both pages
 * even though page one carries only `Older Posts` and page two only `Newer
 * Posts`, because the two sides are floated and the holder clears them. The live
 * holder does the same, with a `display: table` `::after`; `clear: both` on an
 * `::after` is the same mechanism written the way the rest of this file writes
 * it.
 *
 * THE END OF THE RUN CARRIES NOTHING. Neither page serves a greyed or an empty
 * counterpart to the control it has no target for, so neither does this - and
 * that is why there is no `--disabled` state below to be styled.
 */
.ccg-bl-paging {
  margin: var(--ccg-bl-paging-lead) 0 var(--ccg-bl-paging-trail);
  min-height: var(--ccg-bl-paging-height);
}

.ccg-bl-paging::after { content: ""; display: block; clear: both; }

.ccg-bl-paging__side {
  margin: 0;
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-bl-paging-size);
  line-height: var(--ccg-bl-paging-line);
  font-weight: var(--ccg-bl-paging-weight);
}

.ccg-bl-paging__side--newer { float: left; }
.ccg-bl-paging__side--older { float: right; }

/* THE LABEL AND THE ARROW SIT IN A ROW, and the row is reversed on the control
   that points back - which is where the live arrow sits. Measured at 1920 px on
   2026-09-04: `Older Posts` is 84.9 px of label, 5 px of gap and a 32 px arrow on
   the right, 121.9 in total; `Newer Posts` is a 32 px arrow, 5 px of gap and
   92.1 px of label, 129.1 in total. The live control is a text node with a
   floated `::after`; this is the same two boxes and the same three numbers. */
.ccg-bl-paging__link {
  display: flex;
  align-items: center;
  gap: var(--ccg-bl-paging-arrow-gap);
  color: var(--ccg-bl-link-color);
  text-decoration: none;
}

.ccg-bl-paging__side--newer .ccg-bl-paging__link { flex-direction: row-reverse; }

.ccg-bl-paging__arrow {
  display: block;
  flex: 0 0 var(--ccg-bl-paging-arrow-width);
  height: var(--ccg-bl-paging-arrow-height);
}

.ccg-bl-paging__arrow svg { display: block; }

.ccg-bl-paging__link:hover, .ccg-bl-paging__link:focus-visible { text-decoration: underline; }

/* THE LAST CARD'S OWN MARGIN, WHICH THE LIVE OVERVIEW ALSO DROPS. Measured on the
   live page: every card carries `margin-bottom: 48px` and the last one carries
   0, so the 32 px above the control is the control's own lead and nothing else.
   `last-of-type` and not `last-child`, because the control below the list is a
   `div` and every card is an `article`: the last card is the last of its type
   whether a control follows it or not. Measured before this rule: our column ran
   4069.8 px against the live 4053.7, the 16 px being the difference between a
   collapsed 48 and the live 32. */
.ccg-bl-card:last-of-type { margin-bottom: 0; }

.ccg-bl-widget { margin-bottom: var(--ccg-bl-widget-gap); }

.ccg-bl-widget__title {
  margin: 0 0 var(--ccg-bl-widget-title-gap);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-widget-title-size);
  line-height: var(--ccg-bl-widget-title-line);
  font-weight: var(--ccg-bl-widget-title-weight);
  letter-spacing: var(--ccg-bl-widget-title-tracking);
  color: var(--ccg-text);
}

/* THE SEARCH BOX IS MARKED, NOT WORKING. This installation serves no search, so
   the field is disabled and carries the same marking every other route this site
   has not taken over carries. Its geometry is the live one, so the widget under
   it sits where the live one does. */
.ccg-bl-search {
  display: flex;
  height: var(--ccg-bl-search-height);
  cursor: not-allowed;
}

.ccg-bl-search__field {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--ccg-bl-search-height);
  padding: 0 var(--ccg-bl-search-pad);
  border: 1px solid var(--ccg-bl-search-border);
  background: transparent;
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-size-body);
  color: var(--ccg-text);
}

.ccg-bl-search__button {
  flex: none;
  width: var(--ccg-bl-search-button);
  height: var(--ccg-bl-search-button);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ccg-text);
}

.ccg-bl-search__button svg { width: 16px; height: 16px; }

.ccg-bl-recent {
  list-style: none;
  margin: 0;
  padding: 0;
}

.ccg-bl-recent__item { padding: var(--ccg-bl-recent-pad) 0; }

.ccg-bl-recent__link {
  display: flex;
  align-items: center;
  min-height: calc(var(--ccg-bl-recent-thumb) + 1px);
  color: inherit;
  text-decoration: none;
}

.ccg-bl-recent__thumb {
  flex: none;
  width: var(--ccg-bl-recent-thumb);
  height: var(--ccg-bl-recent-thumb);
  margin-right: calc(var(--ccg-bl-recent-step) - var(--ccg-bl-recent-thumb));
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
}

/* Two lines and then it stops, which is what the live widget does with a title
   longer than its box. */
.ccg-bl-recent__title {
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-recent-size);
  line-height: var(--ccg-bl-recent-line);
  font-weight: var(--ccg-bl-recent-weight);
  color: var(--ccg-text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- an article --------------------------------------------------------------
 *
 * ONE COLUMN, 1200 WIDE, NO SIDEBAR AND NO BAND OVER IT. The 96 px above the
 * title is the live article's own margin; the header lies over its first 70 px
 * without being inside it, which is the arrangement every page of this site uses.
 */
.ccg-post { margin-top: var(--ccg-bl-post-top); }

/* THE CATEGORY LINE ABOVE THE TITLE, on the four articles whose front matter
   carries categories and on no others. All ten live articles were read at 1920 px
   on 2026-08-31: /echte-erfolgsgeschichten/, /die-zukunft-ist-jetzt-.../,
   /die-zukunft-des-autohandels-.../ and /warum-ein-unternehmer-.../ serve the row,
   and the other six serve no such element - so the title starts at y = 154.4 on
   those four and at y = 96 on the six.

   THE CATEGORIES ARE TEXT RATHER THAN LINKS, which is the call already made for
   the cards and for the same reason: this site serves no category archive, so a
   link here would look exactly like one that works. */
.ccg-post__meta {
  margin: 0 0 var(--ccg-bl-post-meta-gap);
  padding-bottom: var(--ccg-bl-post-meta-trail);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-post-meta-size);
  line-height: var(--ccg-bl-post-meta-line);
  font-weight: var(--ccg-bl-post-meta-weight);
  color: var(--ccg-bl-link-color);
}

/* The separator between two categories is drawn beside the card's, under
   `.ccg-bl-card__category` above, because the live site draws both with one rule
   and this file says so with one rule too. */

.ccg-post__title {
  margin: 0 0 var(--ccg-bl-post-title-gap);
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-post-title-size);
  line-height: var(--ccg-bl-post-title-line);
  font-weight: var(--ccg-bl-post-title-weight);
  letter-spacing: var(--ccg-bl-post-title-tracking);
  color: var(--ccg-text);
}

.ccg-post__media { margin-bottom: var(--ccg-bl-post-image-gap); }

/* THE PICTURE IS DRAWN AT ITS OWN SIZE WHERE IT IS NARROWER THAN THE MEASURE.
   This read `width: 100%` until 2026-09-04, which is indistinguishable from the
   rule below on the ten articles of the first cycle - every one of their files is
   1280 wide against a 1200 px measure, so both rules draw 1200. It is not
   indistinguishable on the seventeen: `/cashflow-warum-es-zeit-ist-endlich-ernst-zu-machen`
   has no rendition wider than its 800 x 629 original, and the live article draws
   that file at 800 x 629 inside the 1200 measure rather than stretching it.
   Measured there at 1920 px on 2026-09-04: x = 360, w = 800, h = 629. `width:
   100%` would have upscaled it to 1200 x 943.5, which is a picture the original
   does not serve. */
.ccg-post__image {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
}

.ccg-post__body {
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-size-body);
  line-height: var(--ccg-line-body);
  color: var(--ccg-text);
}

.ccg-post__body p { margin: 0 0 var(--ccg-p-margin-bottom); }

.ccg-post__body h5,
.ccg-post__body h6 {
  margin: 0;
  font-family: var(--ccg-font-display);
  font-weight: var(--ccg-bl-post-heading-weight);
  color: var(--ccg-text);
}

.ccg-post__body h5 {
  font-size: var(--ccg-bl-post-h5-size);
  line-height: var(--ccg-bl-post-h5-line);
}

.ccg-post__body h6 {
  font-size: var(--ccg-bl-post-h6-size);
  line-height: var(--ccg-bl-post-h6-line);
}

.ccg-post__body ol,
.ccg-post__body ul {
  margin: 0 0 var(--ccg-p-margin-bottom) var(--ccg-p-margin-bottom);
  padding-left: 0;
}

/* THE ONE COLOUR THAT DIFFERS FROM THE HOME PAGE'S READING. Links in the home
   page's body text are #181818 with no underline, which ccg.yaml records; links
   inside an article are this blue, and so are the categories on a card and the
   share marks. Both are measurements of different elements and neither
   contradicts the other. */
.ccg-post__body a {
  color: var(--ccg-bl-link-color);
  text-decoration: none;
}

.ccg-post-nav { padding: var(--ccg-bl-nav-lead) 0 calc(var(--ccg-bl-nav-height) - var(--ccg-bl-nav-lead) - var(--ccg-bl-nav-thumb)); }

.ccg-post-nav__row { display: flex; }

.ccg-post-nav__item {
  flex: 1 1 50%;
  min-width: 0;
  height: var(--ccg-bl-nav-thumb);
  display: flex;
  align-items: center;
  color: var(--ccg-text);
  text-decoration: none;
}

.ccg-post-nav__item--next {
  justify-content: flex-end;
  text-align: right;
}

.ccg-post-nav__thumb {
  flex: none;
  width: var(--ccg-bl-nav-thumb);
  height: var(--ccg-bl-nav-thumb);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
}

.ccg-post-nav__text {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 var(--ccg-bl-nav-text-gap);
}

.ccg-post-nav__dir {
  display: block;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-nav-dir-size);
  line-height: var(--ccg-bl-nav-dir-line);
  font-weight: var(--ccg-bl-nav-weight);
}

.ccg-post-nav__title {
  display: block;
  font-family: var(--ccg-font-display);
  font-size: var(--ccg-bl-nav-title-size);
  line-height: var(--ccg-bl-nav-title-line);
  font-weight: var(--ccg-bl-nav-weight);
}

.ccg-post-nav__item:hover .ccg-post-nav__title { color: var(--ccg-bl-link-color); }

/* --- the footer's second form ------------------------------------------------
 *
 * 494.4 on the nine pages, 534.4 on the blog overview and on an article: the same
 * 200 px spacing section, a content section 24 px taller and 16 px under the two.
 * On the live site those 40 px are two pairs of empty <p> elements a page builder
 * left behind - two inside the fourth column and two as children of the footer
 * itself. They are reproduced here as height rather than as empty paragraphs, for
 * the reason partials/footer.html.twig gives above its own markup.
 */
.ccg-footer--post .ccg-footer__content { padding-bottom: var(--ccg-bl-footer-extra); }

.ccg-footer--post { padding-bottom: var(--ccg-bl-footer-trail); }

/* --- the Leistung family ----------------------------------------------------
 *
 * The two pages design.md 1.1 found to be a family of their own -
 * /mergers-acquisitions and /strategische-beratung-transformationswissen-bw -
 * and the four bands they render. Namespace `ccg-lt-`; the frame classes
 * (`.ccg-sv`, `.ccg-sv__port`, `.ccg-measure`, `.ccg-sv__wave`, `.ccg-sv__dots`)
 * are the ones every inner section on this site already shares.
 *
 * WHAT IS MEASURED HERE AND WHAT IS CHOSEN, SAID ONCE SO THAT NO RULE BELOW HAS
 * TO BE READ AS MORE THAN IT IS. Measured at 1920 and recorded in design.md 1.1
 * and 2.2: the four band heights (945.8 / 1115 on M&A and 1255 on BW / 346.5 /
 * 317.8), the hero's ground rgb(25,25,25) and band 2's rgb(247,247,245), the
 * 1240 px row at x = 340 that every column geometry below falls out of, the dot
 * field's `cover 50% -331px`, the floating decoration's 533 x 634 at (216,
 * 449.8), the four card frames 270 wide at x = 980 and x = 1290, the four icon
 * cards 230 wide at x = 1000 and x = 1310, the 64 px by which the second card
 * column starts higher than the first, and the two waves' 2400 x 300 and
 * 2400 x 520. Everything else - every padding, radius, shadow, gap, type size
 * not carried by a token, and the hero overlay's gradient stops - is CHOSEN to
 * render and was not read off the live page. Each such rule says so where the
 * value is unusual enough to be mistaken for a reading.
 *
 * NO COLOUR IS WRITTEN HERE, which is the property test/composition.sh reads
 * this whole file for. Where a token already existed it is used
 * (`--ccg-hero-bg`, `--ccg-section-bg`, `--ccg-accent`, `--ccg-button`,
 * `--ccg-page`, `--ccg-card-border`, `--ccg-body-quiet`,
 * `--ccg-nav-missing-color`); the two values that had none - the hero's scrim
 * and the card's shadow - are `lt-hero-scrim` and `lt-card-shadow` in ccg.yaml's
 * `unmeasured` block, where their being chosen rather than read stays visible.
 */

/* --- the row every band lays out on -----------------------------------------
 *
 * MEASURED: every live `bt_bb_row_holder` on these two pages is 1240 px wide at
 * x = 340 - the 1200 rail at 360 with 20 px of column padding pulled out either
 * side. A quarter of 1240 is 310, and a 310 px column padded 20 has a 270 px
 * content box at x = 980 and at x = 1290, which is the measured card frame
 * exactly. So the cards below carry no width and no position of their own: the
 * row produces them.
 *
 * `align-items: flex-start` and not the `center` the service family's row uses:
 * these rows put a headline beside two columns of cards, and centring them would
 * move the cards off the tops the measurement gives.
 *
 * THIS IS NOT `.ccg-sv__row`, AND THAT IS A DECISION RATHER THAN AN OVERSIGHT.
 * The resemblance is close enough to deserve an answer: the negative gutter is
 * the same expression, the column padding is the same expression, and 66.6667 /
 * 33.3333 stand in both. What the two rules genuinely have in common is one
 * MEASUREMENT - a 20 px column gutter inside a 1240 px holder on the 1200 rail,
 * read on the service pages and read again on these two - and that measurement
 * is ALREADY shared: it is `sv-gutter: 20px` in ccg.yaml's `measured` block, and
 * both rules read it through `--ccg-sv-gutter`. Sharing the token is the seam.
 * Sharing the rule would be the fold, and read past the resemblance the
 * declarations do not agree:
 *
 *   - `align-items` is `center` there and `flex-start` here, for the reason
 *     given above. It is the one property that decides where every card in these
 *     two bands begins, so it is not a difference a shared rule could absorb.
 *   - `.ccg-sv__col--4` is not `.ccg-lt-col--4`. The service column carries
 *     `display: flex; align-items: center` beside its width; this one is a width
 *     and nothing else. Even the split the two look like they have in common is
 *     a different rule.
 *   - The service family declares exactly the two splits it uses, and its own
 *     comment says a third "would be a column nothing renders". This family
 *     renders four - 8, 6, 4 and 3. One shared rule set would therefore have to
 *     give the service family two splits nothing in it renders, or hang this
 *     family's splits off a rule the service family owns. The first contradicts
 *     the older rule where it stands; the second is the merge this change's
 *     `site-theme` delta forbids in as many words - "SHALL NOT merge two
 *     constructions that differ in their measurements however alike they look" -
 *     written about these two pages, which were taken for service pages once
 *     already on a resemblance no deeper than this one.
 *
 * `/recruiting`'s rows below reach the same answer and say so where they are
 * declared. Three families, three row rules, one gutter token.
 */
.ccg-lt-row {
  display: flex;
  align-items: flex-start;
  margin: 0 calc(-1 * var(--ccg-sv-gutter));
}

.ccg-lt-col { padding: 0 var(--ccg-sv-gutter); }
.ccg-lt-col--8 { width: 66.6667%; }
.ccg-lt-col--6 { width: 50%; }
.ccg-lt-col--4 { width: 33.3333%; }
.ccg-lt-col--3 { width: 25%; }

/* `bt_bb_padding_double` on the two icon-card columns of the Zukunft band, which
   is what puts their content at x = 1000 and x = 1310 and makes it 230 wide
   inside a 310 px column. 40 is the double of the row's own 20. */
.ccg-lt-col--pad-double { padding: 0 40px; }

/* Both waves are full-bleed images with their own aspect ratio - 2400 x 300 and
   2400 x 520 - so the height is the image's rather than the service family's
   `--ccg-wave-hero`, which is a different wave. */
.ccg-lt-wave { height: auto; }

/* --- the hero ---------------------------------------------------------------
 *
 * MEASURED: 945.8 px tall at 1920 on both pages, ground rgb(25,25,25), which is
 * `--ccg-hero-bg`; the photograph is `hero-about.jpg` (1920 x 1200) covering the
 * band from its top edge; the decoration renders 533 x 634 at (216, 449.8).
 *
 * NOT MEASURED, AND CHOSEN HERE: where the headline block sits inside the band -
 * the port centres it - and the overlay's gradient stops. The live section's
 * class is `bt_bb_background_overlay_top_dark_gradient`, which says a dark
 * gradient from the top and nothing about its stops or its opacity; nothing read
 * them.
 *
 * OVERFLOW IS VISIBLE AND EVERY OTHER `.ccg-sv` HIDES IT. The decoration is 634
 * tall from y = 449.8, so 138 px of it lie below the band - which is what the
 * live page renders, the shape overhanging the wave and the top of the band
 * below. `z-index: 3` puts the whole hero above the next section for the same
 * reason: without it the following band, which is also stacked at 2, would paint
 * over the overhang.
 */
.ccg-lt-hero {
  position: relative;
  z-index: 3;
  overflow: visible;
  background: var(--ccg-hero-bg);
  color: var(--ccg-hero-heading);
}

.ccg-lt-hero__photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 0;
}

/* CHOSEN, NOT MEASURED: the stops and the opacity. See the note above. */
.ccg-lt-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--ccg-lt-hero-scrim);
}

/* The decoration, at the position and the size it renders on the live page. It
   is 3.2 MB for 533 x 634 (design.md 2.2) and it is carried byte-identical, so
   the only thing this rule adds is where it goes. */
.ccg-lt-hero__float {
  position: absolute;
  left: 216px;
  top: 449.8px;
  width: 533px;
  height: 634px;
  z-index: 2;
  pointer-events: none;
}

/* THE MEASURED BAND HEIGHT LIVES HERE AND NOT ON THE SECTION, so there is one
   rule holding it rather than two that could drift apart: the port is the
   section's height-defining child. It centres the block rather than spacing it
   from the top, because the live section carries `top_spacing_large` and
   `bottom_spacing_large` and neither was read as a number. */
.ccg-lt-hero__port {
  padding: 0;
  min-height: 945.8px;
  display: flex;
  align-items: center;
}

/* The h1 is the theme's own display heading. The live element is an h2 and its
   size was not measured, so the theme's h1 scale applies rather than a number
   nobody read; the colour is the hero's, which is where `--ccg-hero-heading`
   already lives on the service family. */
.ccg-lt-hero__title {
  color: var(--ccg-hero-heading);
  margin: 0 0 20px;
}

.ccg-lt-hero__sub {
  margin: 0;
  max-width: 720px;
  font-size: var(--ccg-hero-subline-size);
  line-height: var(--ccg-hero-subline-line);
  color: var(--ccg-hero-heading);
}

/* --- band 2: the page's text and the four service cards ---------------------
 *
 * MEASURED: ground rgb(247,247,245) = `--ccg-section-bg`; 1115 px tall on
 * /mergers-acquisitions and 1255 on the BW page, which is content and not a rule
 * here - the BW band is the taller of the two on three bullets rather than four,
 * because its subheadline is much longer; the dot field paints `cover 50% -331px`
 * over the band; the wave is 2400 x 520.
 */
.ccg-lt-angebot { background: var(--ccg-section-bg); }

/* The live layer's own anchor. No `data-ccg-parallax` here: the shift rate the
   service family reads off `data-parallax` was not measured on these two pages,
   and a rate nobody read would be a number invented to look measured. */
.ccg-lt-angebot__dots {
  background-size: cover;
  background-position: 50% -331px;
}

.ccg-lt-angebot__title { margin: 0 0 var(--ccg-h2-margin-bottom); }

.ccg-lt-angebot__sub {
  margin: 0 0 32px;
  color: var(--ccg-body-quiet);
}

/* The bullets. The mark is drawn rather than fetched - the live one is a
   FontAwesome code point, design.md 2.3 item 10 - and it is painted in the
   theme's accent; the live widget's own `--primary-color` is #1E73BE, which is
   not this site's palette and is recorded as a departure. */
.ccg-lt-checks {
  list-style: none;
  margin: 0 0 40px;
  padding: 0;
}

.ccg-lt-checks__item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 14px;
}

.ccg-lt-checks__mark {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--ccg-accent);
  line-height: 0;
  margin-top: 3px;
}

.ccg-lt-checks__text { color: var(--ccg-text); }

/* THE BUTTON THAT LEAVES THE SITE, AND THE ONE DEFECT THIS BUILD REFUSES TO
   COPY. design.md 2.3 item 8 measured the live label as rgb(255,255,255) on
   rgba(255,255,255,0.18) over the rgb(247,247,245) band - white on white, an
   empty outlined pill in the screenshot. The live outline SHAPE is kept
   (`bt_bb_style_outline`); the two colours are the theme's own accent, so the
   label can be read. Padding and border width are chosen. */
.ccg-lt-button {
  display: inline-flex;
  align-items: center;
  padding: 14px 34px;
  border: 2px solid var(--ccg-button);
  border-radius: var(--ccg-button-radius);
  background: transparent;
  color: var(--ccg-button);
  font-weight: var(--ccg-weight-semi);
  text-decoration: none;
  /* `t-ltbtn` rather than `t-button`: that one animates transform and
     box-shadow, and what changes here on hover is the fill and the ink. It is
     in ccg.yaml's `unmeasured` block, because no hover state was read off the
     live button. */
  transition: var(--ccg-t-ltbtn);
}

.ccg-lt-button:hover, .ccg-lt-button:focus-visible {
  background: var(--ccg-button);
  color: var(--ccg-button-text);
  text-decoration: none;
}

/* THE 64 PX THE TWO CARD COLUMNS DISAGREE BY. Measured: the second column starts
   64 px higher than the first. It is written as an offset on the FIRST column so
   that nothing is pulled out of the band - a negative offset on the second would
   render the same 64 px and would push a card above the section's own top. */
.ccg-lt-cards--drop { padding-top: 64px; }

/* The card. Its width is the column's content box (270 px, measured); its
   height is what its content produces - the live frames are 351 and 381.8 and
   nothing here forces either, because forcing a height would hide the difference
   rather than reproduce it. The border, the shadow, the radius and the padding
   are chosen: the live card carries `bt_bb_border_show bt_bb_shadow_show` and no
   number was read off it. The ground is `--ccg-page` and not `--ccg-card-bg`:
  the latter is #F7F7F5, which is exactly this band's own ground, so a card
  painted with it would have no edge at all. */
.ccg-lt-card {
  background: var(--ccg-page);
  border: 1px solid var(--ccg-card-border);
  box-shadow: var(--ccg-lt-card-shadow);
  overflow: hidden;
  transition: var(--ccg-t-card);
}

/* --- AND IT ANSWERS THE POINTER, WHICH IT DID NOT UNTIL THIS CHANGE ----------
 *
 * Eight of these are served - four on /mergers-acquisitions and four on
 * /strategische-beratung-transformationswissen-bw - and each carries two links
 * out of it. Measured 17.09.2026 at 1920 on a container built from this tree,
 * with a pointer move dispatched onto the card: border rgb(230,228,222) and
 * shadow `rgba(0,0,0,.08) 0 12px 30px` at rest AND under the pointer, which is
 * to say nothing at all happened. The values it takes now are the house
 * treatment and they are in ccg.yaml, in the `unmeasured` block, with what
 * "house treatment" is taken to mean and why nothing was read off the live page.
 *
 * THE `transition` LINE ABOVE IS WHY `data-ccg-reveal` MOVED OFF THIS ELEMENT.
 * Until this change the card carried the reveal's mark itself, so
 * `[data-ccg-reveal] { transition: var(--ccg-t-reveal) }` owned its `transition`
 * property: a second declaration here would have put two different transition
 * values into the reveal's marked set, which is the state `case_reveal` in
 * test/lib/motion_read.py fails a page on. That case is asked of the home page
 * and would not have seen it - which is exactly the reason not to do it. The
 * mark now sits on `.ccg-lt-col`, the column that holds the two cards, so the
 * reveal still runs and this element's transition is its own. What changed for a
 * visitor is the granularity of the fade: two cards of a column now arrive
 * together where each used to arrive on its own. */
.ccg-lt-card:hover,
.ccg-lt-card:focus-within {
  transform: translateY(var(--ccg-lt-card-lift));
  border-color: var(--ccg-lt-card-border-hover);
  box-shadow: var(--ccg-lt-card-shadow-hover);
}

/* 40 px between the two cards of a column: the live separator between them is
   `bt_bb_separator ... bt_bb_bottom_spacing_40`, which carries the number in the
   class name. */
.ccg-lt-card + .ccg-lt-card { margin-top: 40px; }

/* 1280 x 960 is 4:3, so the picture is as tall as a quarter of its width times
   three at whatever width the column gives it. */
.ccg-lt-card__image {
  display: block;
  width: 100%;
  height: auto;
}

.ccg-lt-card__body { padding: 20px 20px 22px; }

.ccg-lt-card__title { margin: 0 0 14px; }

.ccg-lt-card__link { color: var(--ccg-heading); }
.ccg-lt-card__link--missing {
  color: var(--ccg-nav-missing-color);
  cursor: default;
}

/* MEHR ERFAHREN, and the same control at step size in band 4. The label's type
   size and tracking are chosen; what was read is that the live label is set in
   capitals in the markup itself, so nothing here transforms it. */
.ccg-lt-more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.8px;
  font-weight: var(--ccg-weight-strong);
  letter-spacing: 0.06em;
  color: var(--ccg-accent);
  text-decoration: none;
}

.ccg-lt-more__mark {
  display: inline-flex;
  line-height: 0;
}

.ccg-lt-more:hover, .ccg-lt-more:focus-visible { text-decoration: underline; }

.ccg-lt-more--missing {
  color: var(--ccg-nav-missing-color);
  cursor: default;
}
.ccg-lt-more--missing:hover { text-decoration: none; }

/* --- band 3: "Die Zukunft ist schöner..." -----------------------------------
 *
 * MEASURED: 346.5 px tall, no ground of its own - the page's white - and the
 * four icon cards 230 wide at x = 1000 and x = 1310, 134.4 and 156.8 tall.
 * CHOSEN: the port's padding. The live section carries `top_spacing_normal` and
 * no bottom spacing, and neither was read as a number; `min-height` holds the
 * measured band height when the content is shorter than it.
 */
.ccg-lt-zukunft { min-height: 346.5px; }

.ccg-lt-zukunft__port { padding: 48px 0; }

.ccg-lt-zukunft__title { margin: 0 0 var(--ccg-h2-margin-bottom); }

.ccg-lt-zukunft__sub {
  margin: 0;
  color: var(--ccg-body-quiet);
}

/* The icon card: a glyph over a linked title, no box of any kind - the live card
   has no border, no ground and no shadow. The gap and the icon colour are
   chosen; the glyphs are this theme's own (see graphics/leistung-icons.html.twig). */
.ccg-lt-icon-card { margin-bottom: 22px; }
.ccg-lt-icon-card:last-child { margin-bottom: 0; }

.ccg-lt-icon-card__icon {
  display: block;
  color: var(--ccg-accent);
  line-height: 0;
  margin-bottom: 10px;
}

.ccg-lt-icon-card__title {
  margin: 0;
  font-size: var(--ccg-h3-size);
  line-height: var(--ccg-h3-line);
}

.ccg-lt-icon-card__link { color: var(--ccg-heading); }
.ccg-lt-icon-card__link--missing {
  color: var(--ccg-nav-missing-color);
  cursor: default;
}

/* --- band 4: the three steps ------------------------------------------------
 *
 * MEASURED: 317.8 px tall, no ground of its own, three equal columns. The rule
 * above each heading is the live `bt_bb_separator bt_bb_border_style_solid`, so
 * it is a border here rather than an element that paints one line.
 * CHOSEN: the port's padding and the number's
 * type. The live section carries no spacing class at all, and `min-height` holds
 * the measured band height.
 */
.ccg-lt-schritte { min-height: 317.8px; }

.ccg-lt-schritte__port { padding: 40px 0 0; }

.ccg-lt-step {
  /* MEASURED, not chosen: the live rule is a 1 px `bt_bb_separator` above the
     block whose whole paint is `border-bottom: 1px solid rgb(204, 204, 204)`,
     read at 1920 on /mergers-acquisitions at (360, 2439.4), 373.3 wide. It is
     drawn here as the block's own top border, which puts the same line in the
     same place with one element instead of two. */
  border-top: 1px solid var(--ccg-sep-rule);
  padding-top: 20px;
}

.ccg-lt-step__head {
  display: block;
  margin: 0 0 12px;
}

/* The superheadline: `01`, `02`, `03`, above the step's own title. The live
   headline carries it as `bt_bb_headline_superheadline` in the same h4, which is
   why it is a span in the heading here and not a paragraph beside it. */
.ccg-lt-step__number {
  display: block;
  margin-bottom: 10px;
  font-family: var(--ccg-font-body);
  font-size: 13.6px;
  font-weight: var(--ccg-weight-strong);
  letter-spacing: 0.08em;
  color: var(--ccg-accent);
}

.ccg-lt-step__title {
  display: block;
  font-family: var(--ccg-font-heading);
  font-size: var(--ccg-h3-size);
  line-height: var(--ccg-h3-line);
  font-weight: var(--ccg-h3-weight);
  letter-spacing: var(--ccg-h3-tracking);
  color: var(--ccg-heading);
}

.ccg-lt-step__text {
  margin: 0 0 18px;
  font-size: 14.4px;
  line-height: 22px;
  color: var(--ccg-body-quiet);
}

/* The control on step 01, where the page gives the step one. It is the same
   construction as MEHR ERFAHREN with the mark on the other side, which is what
   the live element is: a large circle glyph followed by its label. */
.ccg-lt-more--step {
  font-size: var(--ccg-size-body);
  letter-spacing: 0;
  text-transform: none;
  gap: 10px;
}

/* The words of step 01 on the BW page, where the live element holding them is a
 * `<span>` with no target: they are text here and nothing else. The rule above is
 * deliberately not reused - it is the control, and this is the case where there
 * is none.
 *
 * MEASURED at 1920 on the live page: the phrase renders 204.2 x 24.6 at
 * (388, 2775) in the body face at 22.4px on a 24.64px line, computed colour
 * rgb(24, 24, 24) - which is `--ccg-text` byte for byte, so nothing here writes a
 * colour.
 *
 * CHOSEN: the zero margin. The live phrase sits under a `bottom_spacing_normal`
 * separator nobody read a value off, and `.ccg-lt-step__text`'s own 18 px already
 * puts it where the paragraph leaves off. */
.ccg-lt-step__label {
  margin: 0;
  font-family: var(--ccg-font-body);
  font-size: 22.4px;
  line-height: 24.64px;
  color: var(--ccg-text);
}

/* --- the four legal pages and /stellenangebote -------------------------------
 *
 * TWO FAMILIES IN ONE BLOCK BECAUSE THEY ARRIVE IN ONE CHANGE, not because they
 * share anything. `ccg-rt-` is the four legal pages - /impressum,
 * /datenschutzerklaerung, /cookie-richtlinie-eu and
 * /haftungsausschluss-disclaimer - which design.md 1.4 measured as one
 * construction served four times. `ccg-st-` is /stellenangebote, which design.md
 * 1.3 measured as a single full-bleed frame and nothing else. The two share the
 * `.ccg-sv` frame every inner section on this site shares, and nothing further.
 *
 * WHAT IS NOT HERE. No media query: the proposal keeps the mobile view out of
 * this cycle, everything below was measured at 1920 px and nothing was measured
 * anywhere else. No colour literal: every colour is a `var(--ccg-*)` the
 * document already carries, which is the rule this whole file is written under.
 */

/* --- the headline band of a legal page ---------------------------------------
 *
 * THE GROUND AND THE PHOTOGRAPH ARE TWO LAYERS. The band's own ground is the
 * near-black `--ccg-hero-bg`, measured on all three live headline bands as
 * `rgb(25, 25, 25)` with `rgb(255, 255, 255)` text - the same pair the hero
 * token already carries. The photograph sits over it as a layer of its own,
 * because a single element cannot hold both.
 */
.ccg-rt-kopf {
  background: var(--ccg-hero-bg);
  color: var(--ccg-hero-heading);
  isolation: isolate;
}

/* `welt.jpg`, 2560 x 1707, drawn `cover` and anchored at the top. Read off the
   running live page as a computed style rather than out of the response, for the
   reason design.md 2.2 gives: the page builder writes `blank.gif` into the
   markup and swaps the address in from a script. Measured on /impressum as a
   1920 x 492 layer with `background-size: cover` at `50% 0px`. */
.ccg-rt-kopf__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 0;
}

/* AND TWO OF THE FOUR BANDS ANCHOR IT IN THE MIDDLE, BECAUSE THAT IS WHAT THE
 * ORIGINAL PAINTS THERE. `50% 0` above is the value the original's own parallax
 * handler writes over its declared `50% 50%`, and it writes it on /impressum. On
 * /datenschutzerklaerung and /haftungsausschluss-disclaimer that handler never
 * runs - the live band carries no parallax attribute at all - so the original
 * keeps `50% 50%` and shows the middle of the photograph. Measured on both sides
 * on 2026-09-02 at a 1920 layout width, on /datenschutzerklaerung:
 *
 *                          live                      ours, before this rule
 *   declared + painted     50% 50%                   50% 0px
 *   parallax attribute     ABSENT                    absent
 *   band box               1920 x 460                1920 x 492
 *   welt.jpg at cover      1920 x 1280.25            1920 x 1280.3
 *   vertical slack         -820.25                   -788.3
 *   anchor                 -410.1                    0
 *
 * 410 px of photograph, on a page a visitor reads. This rule gives those two
 * bands the anchor the original paints and nothing else: no parallax handler,
 * and no change to /impressum, whose original really does settle on a nought,
 * nor to /cookie-richtlinie-eu, whose original serves no band at all. The 32 px
 * between the two band heights is a separate reading and is NOT repaired here;
 * it is recorded in the design record of `repair-what-the-checks-missed`. */
.ccg-rt-kopf--mittig .ccg-rt-kopf__media { background-position: 50% 50%; }

/* THE BAND'S HEIGHT IS BUILT OUT OF THE LIVE SPACING RATHER THAN SET.
   design.md 1.4, at 1920 px: /impressum's band is 492 tall and its h1 box is
   1200 x 60 at (360, 256). The port supplies the section's own 128 px above and
   below - `.ccg-sv__port` already does that with `--ccg-section-space` - and the
   headline block supplies the 128 px between the port and the h1 and the 48 px
   under it. 128 + 128 + 60 + 48 + 128 is 492.

   The two numbers below are the live page's own and are the only measures this
   block writes as literals: no token carries either, and ccg.yaml is not this
   change's to extend. */
.ccg-rt-kopf__inner {
  padding-top: 128px;
  padding-bottom: 48px;
}

/* `bt_bb_bottom_spacing_none` on /haftungsausschluss-disclaimer's live section,
   which is the whole of why that band measures 365.4 and the other two 492 and
   460. Nothing else about the band differs. */
.ccg-rt-kopf--ohne-fuss .ccg-sv__port { padding-bottom: 0; }

/* Where the band carries a subheadline, the air under the h1 is the
   subheadline's own line box rather than padding - so the padding goes. Only
   /haftungsausschluss-disclaimer sets one (design.md 1.4). */
.ccg-rt-kopf__inner--mit-sub { padding-bottom: 0; }

/* The h1. Its box is measured at 1200 x 60 on all three live bands; the type it
   is set in is the display face at 50px/60px, which is not the theme's own h1
   (60px/60px at weight 900), so the size is stated here and the family and the
   weight come from tokens. The colour is the band's, not the global h1's -
   `--ccg-h1-color` is the value ccg.yaml records as unmeasured, and this heading
   was measured white. */
.ccg-rt-kopf__title {
  font-size: 50px;
  line-height: 60px;
  font-weight: var(--ccg-bl-post-heading-weight);
  color: var(--ccg-hero-heading);
}

/* The subheadline. Its box was not measured, so it is set in the page's body
   type and takes whatever height that gives it; the band it sits in is
   therefore not exactly the live 365.4. That is a departure and it is recorded
   as one rather than closed with a number nobody read. */
.ccg-rt-kopf__sub {
  margin-top: 8px;
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-size-body);
  line-height: var(--ccg-line-body);
  color: var(--ccg-hero-heading);
}

/* --- the text band of a legal page -------------------------------------------
 *
 * THE PROSE IS THE PAGE BODY, so the rules below reach it by element rather than
 * by class: what the Markdown of the content file emits is what the live page
 * emits, and a class here would mean the template deciding a heading level the
 * measurement already fixed.
 *
 * THE HEADING SCALE IS PART MEASURED AND PART CHOSEN, AND WHICH IS WHICH IS
 * WRITTEN DOWN. h5 and h6 are the live theme's own, measured on the live article
 * and already in ccg.yaml as `--ccg-bl-post-h5-*` (24 / 28.8) and
 * `--ccg-bl-post-h6-*` (20 / 24) - the same two elements, the same live theme.
 * h2 is `--ccg-h2-*`, measured on this site. h3 and h4 were NOT measured
 * anywhere: no computed type was read for a heading inside any of the four text
 * bands. They are chosen to descend evenly from the h2 into the measured h5, and
 * that is the only reason they are the values they are.
 */
.ccg-rt-text {
  background: var(--ccg-page);
}

.ccg-rt-text__body {
  font-family: var(--ccg-font-body);
  font-size: var(--ccg-size-body);
  line-height: var(--ccg-line-body);
  color: var(--ccg-text);
}

.ccg-rt-text__body p { margin: 0 0 var(--ccg-p-margin-bottom); }

/* THE ELEVEN COLLAPSIBLE SERVICES OF /cookie-richtlinie-eu, and the reason there
   is no class here. The live page wraps each of the eleven services of its
   section 6 in a native `<details>` with a `<summary>` - measured on the saved
   response, `grep -c '<details'` returns 11 - so the whole of that behaviour is
   HTML the browser already implements and nothing here has to reproduce a
   plugin. The content file writes the elements; the class it would otherwise
   carry would be a class no template emits, which test/home.sh's inventory
   rejects and rightly.

   COLLAPSED IS THE MEASUREMENT. Rendered with every service open, this page is
   19424 px tall at 1920 against the live 3586. Closed, the eleven summaries are
   what the live page shows. Everything inside them is still in the document and
   still findable.

   The marker is the browser's own. The live one is the plugin's chevron, which
   this theme does not ship, and that is a departure recorded in design.md. */
.ccg-rt-text__body details {
  margin: 0 0 var(--ccg-p-margin-bottom);
  border-bottom: 1px solid var(--ccg-sep-rule);
}

.ccg-rt-text__body summary {
  cursor: pointer;
  padding: var(--ccg-p-margin-bottom) 0;
}

.ccg-rt-text__body summary h3,
.ccg-rt-text__body summary p { display: inline-block; margin: 0 8px 0 0; vertical-align: baseline; }

.ccg-rt-text__body summary p { color: var(--ccg-body-quiet); }

.ccg-rt-text__body details > :last-child { margin-bottom: var(--ccg-p-margin-bottom); }

.ccg-rt-text__body h2,
.ccg-rt-text__body h3,
.ccg-rt-text__body h4,
.ccg-rt-text__body h5,
.ccg-rt-text__body h6 {
  margin: 0 0 var(--ccg-p-margin-bottom);
  font-family: var(--ccg-font-display);
  font-weight: var(--ccg-bl-post-heading-weight);
  letter-spacing: normal;
  color: var(--ccg-text);
}

.ccg-rt-text__body h2 {
  font-size: var(--ccg-h2-size);
  line-height: var(--ccg-h2-line);
}

/* chosen, not measured - see the block comment above */
.ccg-rt-text__body h3 {
  font-size: 32px;
  line-height: 38.4px;
}

/* chosen, not measured - see the block comment above */
.ccg-rt-text__body h4 {
  font-size: 28px;
  line-height: 33.6px;
}

.ccg-rt-text__body h5 {
  font-size: var(--ccg-bl-post-h5-size);
  line-height: var(--ccg-bl-post-h5-line);
}

.ccg-rt-text__body h6 {
  font-size: var(--ccg-bl-post-h6-size);
  line-height: var(--ccg-bl-post-h6-line);
}

/* A heading arriving straight after a paragraph needs air above it that a
   paragraph following a paragraph does not. `+` rather than a margin on every
   heading, so the first heading of a page still starts at the top of the band. */
.ccg-rt-text__body p + h2,
.ccg-rt-text__body p + h3,
.ccg-rt-text__body p + h4,
.ccg-rt-text__body p + h5,
.ccg-rt-text__body p + h6,
.ccg-rt-text__body ul + h2,
.ccg-rt-text__body ul + h3,
.ccg-rt-text__body ul + h4,
.ccg-rt-text__body ul + h5,
.ccg-rt-text__body ul + h6 {
  margin-top: var(--ccg-p-margin-bottom);
}

/* The bullets of /datenschutzerklaerung's one list and /cookie-richtlinie-eu's
   one list. The same indent the article body uses, for the same reason: this is
   ordinary prose in a 1200 px column. */
.ccg-rt-text__body ol,
.ccg-rt-text__body ul {
  margin: 0 0 var(--ccg-p-margin-bottom) var(--ccg-p-margin-bottom);
  padding-left: 0;
}

.ccg-rt-text__body li { margin: 0 0 var(--ccg-p-margin-bottom); }

/* THE LINKS ARE THE BODY'S OWN COLOUR AND NOT THE ARTICLE'S BLUE. The live legal
   pages carry no link at all except inside /cookie-richtlinie-eu, whose links
   were not read for their computed colour; `--ccg-link` is the value ccg.yaml
   records for body-text links on this site, so it is the one used rather than a
   second colour nobody measured here. */
.ccg-rt-text__body a {
  color: var(--ccg-link);
  text-decoration: underline;
}

.ccg-rt-text__body > :last-child { margin-bottom: 0; }


/* === the narrow bands =========================================================
 *
 * WHAT THIS BLOCK IS AND WHY IT IS THE FIRST OF ITS KIND HERE. Everything above
 * it is the layout measured at 1920 on https://www.consulting-concept.de/ and
 * reproduced. This block is not that. The narrow view of this site was decided
 * for this site - "neu, nicht uebernehmen" - so no rule below is a reading of
 * anything, and the original's own six breakpoints were not looked at.
 * openspec/changes/design-the-mobile-view/design.md holds the decisions, the
 * measurements they were taken from, and the list of what the design decides
 * (which is what no check may assert until the draft has been agreed).
 *
 * TWO BAND EDGES. EACH IS A DECISION INSIDE A BRACKET THAT WAS MEASURED, and
 * neither is the arithmetic of its own bracket - which is what the two entries
 * below said before they were corrected on 2026-09-02:
 *
 *   1024  The horizontal header stops fitting at 777. Bisected in one-pixel
 *         steps on 2026-09-02 against this build with test/lib/pages.mjs: at a
 *         777 px window the document is 777 px wide and nothing reaches past the
 *         edge; at 776 it is still 777, and `ul.ccg-header__social` ends at
 *         776.8. That is a hard floor for the horizontal menu, so the drawer has
 *         to take over above it - the lower end of the bracket.
 *
 *         THE UPPER END IS 1260, AND IT IS NOT SUPPORT FOR 1024. `.ccg-measure`
 *         is 1200 wide with `max-width: calc(100% - 60px)`, so a fixed 1200 px
 *         construction is wider than its rail below a 1260 px window and the
 *         1200 x 600 industries stage - which is also translated 60 px sideways
 *         - below a 1320 px one. What that says is that the desktop layout
 *         itself stops fitting at 1260, above this edge: between 1025 and 1319
 *         those constructions are clipped and neither block below applies to
 *         them. This change's own sweep counted 14 boxes past the edge at 1280
 *         and 42 at 1100. That is a pre-existing defect of the wide layout, it
 *         is not repaired here, and it is recorded in proposal.md under
 *         `## Known limitations`. Quoting it as a reason for 1024 had it exactly
 *         backwards: it is the reason the edge may not be raised to 1259, not a
 *         reason it is right where it is.
 *
 *         So 777 < edge < 1260, and 1024 inside it is the design deciding. At
 *         1024 the rail is 964 and a two-across row gives each card 470 px.
 *
 *    640  A two-column row gives each column under 300 px somewhere in here, and
 *         300 px is where a card is narrower than the picture inside it wants to
 *         be. THE ARITHMETIC DOES NOT LAND ON 640 AND THE OLD WORDING IMPLIED IT
 *         DID. Each row carries its own gap, so each has its own crossing:
 *         measured at 641 on 2026-09-02, `.ccg-cards--3` gives 278.5 px per
 *         column at a 24 px gap, `.ccg-network` 274.5 at 32 and `.ccg-teasers`
 *         254.5 at 72 - so by that criterion alone the edge would be 684, 692 or
 *         732 depending on which row is asked. 640 is below all three, which is
 *         what the criterion actually establishes: at 640 every two-column row
 *         is already past it. Choosing 640 rather than 684 is the design
 *         deciding, and it is recorded as such in design.md 2.3.
 *
 * THE TWO NUMBERS ARE LITERALS AND EVERY OTHER VALUE IN THIS BLOCK IS A TOKEN.
 * That is not an exception being taken quietly: a media query cannot read a
 * custom property, measured on 2026-09-02 by serving
 * `:root { --probe-bp: 9999px } @media (max-width: var(--probe-bp)) { ... }`
 * beside the same query written with the literal - the literal applied and the
 * one reading the property did not. The probe is in design.md. So 1024 and 640
 * are the only two design numbers in this file, and they are here because the
 * language gives them nowhere else to be.
 *
 * WHAT THE DRAWER IS AND WHY IT SLIDES UPWARDS RATHER THAN SIDEWAYS. It is
 * `position: fixed` and translated off the top of the window when closed, which
 * is what keeps it out of the document's scrollable overflow: measured the same
 * day on the served page, adding a fixed 390x900 box at
 * `translateY(calc(-100% - 70px))` left `scrollWidth` at 777 and `scrollHeight`
 * at 2337, both unchanged. A panel translated sideways measured the same, and
 * upwards is still what is used - a document can never scroll above its own top,
 * so the vertical direction cannot produce the defect this cycle exists to
 * remove even on an engine that accounts for fixed boxes differently.
 *
 * AND IT IS NOT `display: none`. An element that is not displayed is not in the
 * focus order, and the whole menu would then be unreachable by keyboard at this
 * width. Translated away it keeps its box, keeps its focus stops, and
 * `:focus-within` slides it into view the moment a key reaches it - which is the
 * same mechanism a pointer already gets from `:hover` at the wide band, and is
 * what makes this work with the script switched off.
 */

/* The narrow form of the industries node's arrival. `cnNodeIn` ends on
   `translate(-50%, -50%) scale(1)`, which is the centring the absolutely placed
   node needs and is exactly wrong for one laid out in a list: for its 0.7 s the
   node would sit half its own size up and to the left of where it belongs. Same
   shape, same duration and the same six delays; the centring taken out. */
@keyframes ccgNarrowNodeIn {
  from { opacity: 0; transform: scale(0.4); }
  to   { opacity: 1; transform: none; }
}

/* And the hub's own float, for the same reason and with the same fix.
   `cnHubFloat` breathes between `scale(1)` and `scale(1.04)` and carries the
   `translate(-50%, -50%)` through both keyframes, so an infinite animation held
   the hub half its own size up and to the left of where the list puts it - for
   ever, not for 0.7 s. Photographed at 390 on 2026-09-02 it sat across the
   section's own headline. */
@keyframes ccgNarrowHubFloat {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

/* And the roadmap's track, for the same reason a third time. `rmLineDraw` ends on
   `scaleX(1)`, which is the axis a row of six phases is drawn along; at the phone
   band the phases are a column and the track runs down it, so the same draw on
   the other axis. Same shape, and the duration, the easing and the delay are the
   token's - `animation-name` is all that is overridden at the rule. */
@keyframes rmLineDrawY { to { transform: scaleY(1); } }

/* --- AND THE RESTING EDGE, WHICH IS A DEPARTURE AND NOT A REPAIR -------------
 *
 * ONE RULE AND TWO TOKENS, for nine families whose own background and whose
 * backing ground are both near-white. The reason the client gave for taking it,
 * the nine grounds it was measured against, and the half of the chosen variant
 * that is deliberately NOT taken are all written beside `card-rest-border` in
 * ccg.yaml; what is here is the landing.
 *
 * THE SECOND HALF OF THE TREATMENT IS NOT IN THIS RULE and cannot be, because a
 * shadow is not a property that can be added to: each of the nine declares its
 * own `box-shadow`, and each now reads
 * `var(--ccg-card-rest-veil), var(--ccg-card-rest-lip)` - two tokens written in
 * nine places - while this rule carries the edge.
 *
 * THE NINE CAST NINE DIFFERENT SHADOWS UNTIL 17.09.2026 and they now cast one.
 * That is the client's choice, taken after seeing it rendered on the family with
 * the heaviest own layer and on the family with the lightest, and the reasoning
 * against it - it erases a distinction `CARDS` in test/lib/pages_read.py existed
 * to hold - is recorded beside `card-rest-veil` in ccg.yaml together with the
 * nine former values, so that the cost is readable and not merely gone.
 *
 * THE ORDER MATTERS. Each of the nine sets its border with the `border`
 * shorthand in its own rule, and this rule has the same specificity, so it wins
 * by sitting after all nine in the file. Every `:hover` and `:focus-within` that
 * moves the border is a pseudo-class higher and wins over this one.
 *
 * WINNING THE CASCADE IS NOT THE SAME AS BEING SEEN TO WIN, which is what this
 * rule got wrong when it first landed. The value was .22, and .22 is exactly
 * `ka-card-border-hover` and `sv-san-card-border-hover`: the hover rules on
 * `.ccg-ka-card` and `.ccg-sv-san__card` went on winning the cascade and set the
 * border to the colour it already had, so the pointer moved the shadow and the
 * edge stood still. The value is .16 now and the rule that picks it - every
 * family keeps at least half the border movement its live counterpart makes - is
 * beside `card-rest-border` in ccg.yaml with the nine live readings it is
 * derived from. `test/lib/card_hover_read.py` compares the two values now rather
 * than asking only whether a hover rule names the property.
 *
 * `.ccg-service` AND `.ccg-lt-card` ARE NOT IN THE LIST, and both are readings
 * rather than omissions. The service card's resting border is opaque
 * `rgb(91,47,212)` and the Leistung card's is opaque `rgb(230,228,222)`; neither
 * is a ten-per-cent wash and neither reads as floating. `.ccg-job-card` sits on
 * `rgba(26,13,61,0.62)` and is not near-white at all.
 */
.ccg-card,
.ccg-teaser,
.ccg-ka-card,
.ccg-ab-method,
.ccg-ko-method,
.ccg-ko-info-card,
.ccg-sv-feature,
.ccg-sv-san__card { border-color: var(--ccg-card-rest-border); }

/* --- the band between the narrow rules and the desktop canvas ----------------
 *
 * A THIRD BREAKPOINT, AND THE FILE SAID THERE WERE TWO. It is here because
 * `lay-out-the-middle-band-and-unstretch-the-graphics` measured one thing that
 * cannot be said any other way, and it holds exactly one rule.
 *
 * `.ccg-home-zukunft__particles` is `position: absolute; inset: 0` in a section
 * whose height is `--ccg-zukunft-cell-height` plus the page's section spacing -
 * 1314 px, a token, the same at every width from 1025 up. So its box is W x 1314
 * and its proportion against the field's own 1600 : 900 gets worse as the window
 * narrows, with nothing in the layout changing at any particular width. Measured
 * on 11.09.2026: 1.217 at 1920 x 1080, 1.461 at 1599, 1.622 at 1440, 1.980 at
 * 1180, 2.279 at 1025. There is no continuous form of this fix - the two
 * renderings are two elements and one of them has to be switched off - so there
 * has to be a width, and 1599 is the top of the band this change is named for.
 *
 * 1557 AND NOT 1599, WHICH IS THE EDGE THE REST OF THIS CHANGE USES. The skew
 * crosses 1.5 at 2336 / 1.5 = 1557.3, so between 1558 and 1599 the wide
 * rendering is already inside the threshold - and the portrait one is not free.
 * It fits by `slice`, so a box wider than 780 : 1000 crops it top and bottom, and
 * the field's dots travel through the cropped part unseen: measured over one full
 * 14 s cycle, 20 samples, counting circles whose centre is inside the element's
 * box at an opacity above 0.05, the field shows a mean of 13.4 of its 14 dots
 * under the wide rendering and 8.2 under the portrait one at 1599 x 1314. Ending
 * the block at 1599 would have cost five dots across forty-two pixels of window
 * to repair a distortion that was already inside the ceiling.
 *
 * THE MARGIN AT THE EDGE IS SMALL AND IS STATED. At 1558 the wide rendering reads
 * 1.4994 against a ceiling of 1.5. No width this suite drives is near it - 1440
 * takes the portrait rendering at 1.000 and 1920 the wide one at 1.217 - but a
 * thousandth is a thousandth and it is written here rather than discovered.
 *
 * THE DESKTOP IS NOT IN THIS BLOCK. At 1558 and above the wide rendering is
 * served exactly as it was, which is what `3.4` of this change's tasks asks for.
 */
@media (max-width: 1557px) {
  .ccg-home-zukunft__particles .ccg-home-zukunft__field--wide { display: none; }
  .ccg-home-zukunft__particles .ccg-home-zukunft__field--tall { display: block; }
}

/* --- and the one switch that is not a width at all ---------------------------
 *
 * `.ccg-sv-hero__particles` is `inset: 0` in a section that is `min-height:
 * 100vh`, so wherever its content fits, the layer's box IS THE WINDOW and its
 * proportion is the window's. That is not a function of the width: measured on
 * 11.09.2026, the same 1180 px window gives the layer 1180 x 820 on an iPad in
 * landscape - a skew of 1.235 against the field's 1600 : 900 - and 1180 x 1080
 * in a desktop window narrowed to the same width, where the skew is 1.627. A
 * width query cannot tell those two apart and an aspect-ratio query is exactly
 * the question being asked.
 *
 * 6/5 IS DERIVED AND NOT CHOSEN. The wide field is drawn at 1600 : 900 = 1.7778,
 * and the written threshold for a `none` fitting is a skew of 1.5, so the wide
 * rendering is in shape while the box is at least 1.7778 / 1.5 = 1.1852 wide to
 * tall. 6/5 is the nearest round ratio above that. Measured after this rule:
 * 1920 x 1080 (1.778) keeps the wide rendering at a skew of 1.000, 1440 x 1080
 * (1.333) keeps it at 1.333, 1180 x 820 (1.439) keeps it at 1.235, and
 * 1180 x 1080 (1.093) takes the portrait one at 1.000.
 *
 * WHERE IT IS A PROXY AND NOT THE MEASUREMENT, said rather than rounded up. It
 * is the WINDOW's proportion and the rule wants the ELEMENT's, and the two part
 * company wherever the hero's content is taller than 100vh - which is what the
 * 1024 block below is for. A window wider than 1024 and short enough for the
 * hero to overflow it would fall between the two and is not covered; the
 * narrowest window measured where the content still fits is 1025 x 800. */
@media (max-aspect-ratio: 6 / 5) {
  .ccg-sv-hero__particles .ccg-sv-particles--wide { display: none; }
  .ccg-sv-hero__particles .ccg-sv-particles--tall { display: block; }
}

@media (max-width: 1024px) {
  /* The /about emblem's footline, one of three band values; see ccg.yaml. At
     641 - the bottom of this band - the element is 267.2 px wide against 400
     units, so 15 units paint 10.02 px against a floor of 9.28.

     ON THE ELEMENT AND NOT ON `:root`, and that is this change's answer to
     having added two idioms for one question. It reassigned the token here
     while giving the dashboard's portrait value labels a rule scoped to the
     drawing they belong to, so one graphic changed a size by re-pointing a
     document-wide token and the other by naming its own element. `:root` in
     this file means a SCALE - the h1 and hero tokens two blocks down are read
     by many elements and are meant to move together - and one label's size is
     not one. Both graphics name their own element now, each reading its own
     token; design.md 2.6 carries the choice and the reason. */
  .ccg-ab-emblem__foot { font-size: var(--ccg-ab-emblem-foot-size-lap); }

  /* --- the header ---------------------------------------------------------- */

  /* Shown here and `display: none` above, which is also what keeps it out of the
     focus order at the wide band - so the wide keyboard sweep walks exactly the
     stops it walked before this cycle. */
  .ccg-header__toggle {
    display: inline-flex;
    order: 1;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--ccg-nav-toggle-size);
    height: var(--ccg-nav-toggle-size);
    margin-left: var(--ccg-nav-toggle-gap);
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--ccg-nav-color);
    cursor: pointer;
  }

  /* `order` and not a different place in the markup. The control belongs after
     the mark and the two social glyphs to a reader looking at the bar, and
     before the menu it opens to a reader walking it with a key - and `order`
     moves the first without touching the second, because sequential navigation
     follows the markup and not the visual order. */
  .ccg-header__toggle-bars {
    display: flex;
    flex-direction: column;
    gap: var(--ccg-nav-toggle-bar-gap);
  }

  .ccg-header__toggle-bar {
    display: block;
    width: var(--ccg-nav-toggle-bar-width);
    height: var(--ccg-nav-toggle-bar-height);
    border-radius: var(--ccg-nav-toggle-bar-radius);
    background: currentColor;
  }

  /* The two states the header has, taken by the toggle as well. Written as the
     navigation's own two rules are written, at the same specificities, so the
     bars are never a colour the lettering beside them is not. */
  .ccg--hero .ccg-header__toggle { color: var(--ccg-nav-hero-color); }
  .ccg-header[data-ccg-sticky="on"] .ccg-header__toggle { color: var(--ccg-nav-color); }

  /* --- the drawer ---------------------------------------------------------- */
  .ccg-nav {
    position: fixed;
    top: var(--ccg-header-height);
    left: 0;
    right: 0;
    max-height: calc(100vh - var(--ccg-header-height));
    overflow-y: auto;
    /* A drawer taller than the window scrolls inside itself, and the scroll stops
       at its own end rather than carrying on into the document behind it. One
       declaration and no state: see the paragraph at the rule below for why the
       document is not locked instead. */
    overscroll-behavior: contain;
    padding: var(--ccg-nav-drawer-pad);
    background: var(--ccg-nav-drawer-bg);
    box-shadow: var(--ccg-nav-drawer-shadow);
    transform: translateY(calc(-100% - var(--ccg-header-height)));
    transition: var(--ccg-nav-drawer-motion);
    z-index: 10001;
  }

  /* The sticky bar is two pixels taller than the transparent one, and the drawer
     hangs off its bottom edge in both states. */
  .ccg-header[data-ccg-sticky="on"] .ccg-nav {
    top: var(--ccg-header-height-sticky);
    max-height: calc(100vh - var(--ccg-header-height-sticky));
    transform: translateY(calc(-100% - var(--ccg-header-height-sticky)));
  }

  /* THE TWO WAYS IT OPENS, AND BOTH ARE HERE RATHER THAN ONE HERE AND ONE IN THE
     SCRIPT. The first is the attribute js/ccg.js writes when the control is
     pressed; the second is a key arriving in the drawer, which needs no script at
     all. Both are written at 0-3-0 so that neither loses to the sticky rule above
     - the earlier form of the second was `.ccg-nav:focus-within`, 0-2-0, which
     would have left a keyboard unable to open the drawer on a scrolled page. */
  .ccg-header[data-ccg-nav-open="true"] .ccg-nav,
  .ccg-header .ccg-nav:focus-within { transform: none; }

  /* AND THE BAR GOES WITH IT, WHICH IS THE HALF THAT WAS MISSING. The drawer is
     `position: fixed`; the bar above it is `position: absolute` until
     js/ccg.js's sticky observer fires, and ccg.yaml puts that at 302 px of
     scroll. Between 1 px and 301 px the two therefore parted company. Measured
     at 390 x 844 on 2026-09-02 with a real tap on the control and
     `scrollTo(0, 200)`:

         header y = -200   drawer y = 70   toggle y = -187

     - the logo, both social glyphs and the control that opened the drawer were
     all off the top of the window while the panel stayed pinned under a 70 px
     strip of the hero. There was no close control on the screen at all, which is
     the one thing a disclosure may never do.

     The bar and the drawer are one component while it is open, so the bar takes
     the drawer's positioning for as long as it is. `position` is all that
     changes: `.ccg-header` is out of flow in both states, so nothing on the page
     moves and no measurement of the layout changes.

     THE DOCUMENT IS NOT LOCKED, and that is a decision rather than an omission.
     A lock is `overflow: hidden` on an element this rule cannot reach, so it
     needs a second piece of state that a script owns and has to unwind - and
     with the bar pinned there is nothing left for it to repair: the control is
     on the screen at every scroll position, measured below. What scrolling
     behind an open drawer costs is the page moving under it, and
     `overscroll-behavior: contain` on the drawer above is what keeps a scroll
     that began inside the panel from becoming that. */
  .ccg-header[data-ccg-nav-open="true"] { position: fixed; }

  /* --- the menu inside it -------------------------------------------------- */
  .ccg-nav__list {
    flex-direction: column;
    align-items: stretch;
  }

  .ccg-nav__list > .ccg-nav__item + .ccg-nav__item {
    border-top: 1px solid var(--ccg-nav-drawer-rule);
  }

  .ccg-nav__link {
    padding: var(--ccg-nav-drawer-link-pad);
    white-space: normal;
  }

  .ccg-nav__link--l2, .ccg-nav__link--l3 { padding: var(--ccg-nav-drawer-sub-pad); }

  /* A sub-menu is a part of the list rather than a panel over it. `position:
     static` makes the three placement rules above - `top: 100%`, `left: -100%`,
     `left: 100%` - inert without any of them having to be restated. */
  .ccg-nav__sublist {
    position: static;
    width: auto;
    padding: 0 0 0 var(--ccg-nav-drawer-sub-indent);
    background: transparent;
  }

  /* THE THIRD WAY A SUB-MENU OPENS, WHICH IS THE ONE A FINGER HAS. This
     attribute is what js/ccg.js writes on a tap; `:focus-within` is what a key
     produces. The two CSS rules it joins are two blocks up in this file and are
     untouched.

     WHAT THIS COMMENT SAID BEFORE, AND WHY IT WAS WRONG: that `:hover` is not
     available at this width. This is a width query and says nothing at all about
     the pointer - a mouse in a 900 px window hovers exactly as it does in a
     1920 px one, and the hover disclosure two blocks up went on matching here.
     Withdrawing it is the `@media (hover: none)` block at the end of this file,
     which asks the pointer instead. */
  .ccg-nav__item--has-children[data-ccg-open="true"] > .ccg-nav__sublist { display: flex; }

  /* --- the mark under the current entry, which was desktop arithmetic -------
   *
   * `left: 20px; right: 20px` and `transform: scale(0.5)` about a centred origin
   * draw a bar half the width of what it is on. On a 240 px panel entry that is
   * the word; on a drawer entry that spans the window it is not. Measured at 390
   * on 2026-09-02: the painted bar ran x = 107.5 to x = 282.5 while `Home` ended
   * at x = 70.6 - 37 px of clear space between a word and its own mark, which
   * reads as a rule between two rows rather than as a mark on one. Here it is a
   * short bar at the entry's own text edge: the inset is the drawer link's own
   * left padding, so it starts where the word starts, and the scale is taken off
   * because a fixed width is not a share of anything. */
  .ccg-nav__link--l1::after {
    left: var(--ccg-narrow-nav-mark-inset);
    right: auto;
    width: var(--ccg-narrow-nav-mark-width);
    transform: none;
  }

  /* The drawer's ground is white in both header states, so the lettering is the
     navy in both. Without this the hero rule two hundred lines up paints every
     top-level entry white on white for the whole length of the home page. */
  .ccg--hero .ccg-nav__link--l1 { color: var(--ccg-nav-color); }
  .ccg--hero .ccg-nav__link--l1.ccg-nav__link--missing { color: var(--ccg-nav-missing-color); }
  .ccg--hero .ccg-nav__item--current > .ccg-nav__link--l1::after { background: var(--ccg-nav-mark-color); }

  /* --- the hero's own scale, which belongs to this edge and not to 640 ------
   *
   * WHITE ON WHITE, MEASURED. The hero's display sizes and its 186 px text
   * indent were read at 1920 against a 1200 px rail. Below this edge the rail is
   * at most 964 and the indent takes 186 of it, so the block wraps and - being
   * vertically centred - grows upwards into the constellation's photograph node.
   * `test/lib/contrast.mjs`, run sequentially on 2026-09-02 while the scale
   * still started at 640:
   *
   *    390    0 below 4.5, worst 4.69
   *    641    2 below 4.5, worst 1.00   #FFFFFF setup line on a #FFFFFF photo
   *    700    2 below 4.5, worst 1.48
   *   1920    0 below 4.5, worst 4.76
   *
   * At 641 the 60 px setup line laid out 395 px wide, wrapped into four lines
   * and put its first line at y 292 across the node at y 233 to 437. The hero is
   * the one construction whose type was measured against the rail rather than
   * against the window, so it takes its narrow form at the edge where the rail
   * stops being the one it was measured in - the same edge the header changes at
   * - rather than 384 px lower down. The five section headings, the vertical
   * rhythm and the general h1/h2 scale stay at 640: none of them sits over a
   * picture, and none of them was measured against anything but its own line.
   */
  :root {
    --ccg-h1-setup-size: var(--ccg-narrow-h1-setup-size);
    --ccg-h1-setup-line: var(--ccg-narrow-h1-setup-line);
    --ccg-h1-setup-tracking: var(--ccg-narrow-h1-setup-tracking);
    --ccg-h1-punch-size: var(--ccg-narrow-h1-punch-size);
    --ccg-h1-punch-line: var(--ccg-narrow-h1-punch-line);
    --ccg-h1-punch-tracking: var(--ccg-narrow-h1-punch-tracking);
    --ccg-h1-punch-space: var(--ccg-narrow-h1-punch-space);
    --ccg-hero-subline-size: var(--ccg-narrow-hero-subline-size);
    --ccg-hero-subline-line: var(--ccg-narrow-hero-subline-line);
    --ccg-hero-text-indent: var(--ccg-narrow-hero-text-indent);
  }

  /* AND THE DISPLAY LINE IS NOT HYPHENATED. `hyphens: auto` is set on `body`
     below, and the argument written beside it is about German compounds in body
     copy that would otherwise make a page wider than its window. A display
     headline is neither: at 641 the rule set `Unterneh-/men.` across two lines
     of the hero. `overflow-wrap: break-word` stays behind it, so a word that
     genuinely cannot fit still breaks. */
  .ccg-home-hero__title {
    -webkit-hyphens: manual;
    hyphens: manual;
  }

  /* AND EVERY OTHER HEADING KEEPS THE DICTIONARY AND LOSES THE SHORT BREAKS.
     The exception above was written for one element and the same rule was
     hurting five more. Counted at 390 on 07.09.2026 over `h1..h6` of the home
     page, five headings broke mid-word: `Fuenf Funktionen fuer Ih-/ren Erfolg.`,
     `Gestalten Sie die Un-/ternehmenszukunft mit uns!`, `Aktuelle Facharti-/kel
     und Neuigkeiten`, `Neuerungen im Gesell-/schaftsregister` and `Die Vorteile
     der Digitalisi-/erung von Belegen`. Two of those leave two letters standing
     before the hyphen.

     `hyphens: manual` IS NOT THE REPAIR AND WAS MEASURED BEFORE BEING REJECTED.
     `Unternehmenszukunft` lays out 346.9 px wide at this band's h2 in a 330 px
     rail, so it has to break somewhere; with the dictionary switched off the
     `overflow-wrap: break-word` below takes over and sets `Unternehmenszukunf/t
     mit uns!`, which is worse than what it replaced. So the dictionary stays and
     the breaks it may take are bounded instead: at least twelve letters in the
     word, six before the hyphen and five after. Measured with this rule at 390:
     five mid-word breaks become three - `Unternehmenszu-/kunft` (forced),
     `Gesell-/schaftsregister` and `Digitali-/sierung`, all three correct German
     syllable breaks - and `document.documentElement.scrollWidth` stays 390,
     which is this band's own acceptance measure. A browser that does not
     implement the property falls back to the behaviour of the line above it, so
     nothing regresses where it is unsupported.

     AND THE MEASUREMENT WAS ONE PAGE WHILE THE RULE IS ALL THIRTY-SIX. This
     selector is inside the lap block on `h1..h6`, so it applies to every route
     this site serves at every width up to 1024. What was counted, before and
     after, is the home page's own headings. The other thirty-five routes are
     unmeasured for it. That is recorded rather than closed, because closing it
     means a check that counts mid-word breaks and there is no such check. */
  h1, h2, h3, h4, h5, h6 {
    -webkit-hyphenate-limit-chars: var(--ccg-narrow-hyphen-limits);
    hyphenate-limit-chars: var(--ccg-narrow-hyphen-limits);
  }

  /* --- the six waves ------------------------------------------------------
   *
   * A height read at 1920 is a share of the window and not a number of pixels,
   * and treating it as a number is what put the hero's headline and the whole of
   * the careers section under a white silhouette at 390. The share is in
   * ccg.yaml with the division that produced it.
   */
  :root {
    --ccg-wave-hero: var(--ccg-narrow-wave-hero);
    --ccg-wave-loesungen: var(--ccg-narrow-wave-loesungen);
    --ccg-wave-zukunft-top: var(--ccg-narrow-wave-zukunft-top);
    --ccg-wave-zukunft: var(--ccg-narrow-wave-zukunft);
    --ccg-wave-news: var(--ccg-narrow-wave-news);
    --ccg-sv-band-wave-height: var(--ccg-narrow-sv-band-wave-height);
    --ccg-branchen-hub-size: var(--ccg-narrow-hub-size);
  }

  /* A stack of columns needs the space a row of them does not. */
  .ccg-footer__column { padding-bottom: var(--ccg-narrow-footer-column-gap); }

  /* --- what a word may not do ----------------------------------------------
   *
   * A German compound is longer than an English one and this site is full of
   * them. Re-measured at 390 on 2026-09-02 by serving the three rules of this
   * block back off again on all 28 routes: **nine** were wider than the window
   * with the header already repaired, and **seven** of the nine were one
   * unbreakable word - six of the ten articles, worst `h1.ccg-post__title` on
   * /wesentliche-neuerungen... at a document width of 519, and /blog at 422 with
   * `a.ccg-bl-card__title-link` reaching 421.7. The other two are the
   * illustration below. This comment said eight and six, and design.md said nine
   * with /blog counted for its flex row; the flex row is a real defect of that
   * page - it leaves the article column 30 px wide - but it is not what made the
   * document wider. `break-word` breaks only a word that would otherwise
   * overflow, so nothing that fits is touched.
   *
   * AND `hyphens: auto` IN FRONT OF IT, because `break-word` alone breaks a word
   * wherever the line happens to end: photographed at 768 on 2026-09-02 it set
   * `Strategieberatun / g` and `Gruendungsberatu / ng` across two service cards.
   * The document declares `lang="de"` - partials/base.html.twig emits it - so the
   * engine has the dictionary to break at a syllable and to print the hyphen.
   * `break-word` stays behind it, for the word no dictionary covers. */
  body {
    -webkit-hyphens: auto;
    hyphens: auto;
    overflow-wrap: break-word;
  }

  /* --- what a picture may not do ------------------------------------------- */
  /* A floor rather than a rule: `img` is 0-0-1, so every sized image in this
     file keeps its own width and only one that was never given one is held. */
  img { max-width: 100%; }

  /* --- the rows, with fewer columns than they were composed for ------------
   *
   * TWO COLUMNS FOR ALL OF THEM, AND THE SERVICES ROW USED TO BE THREE. Measured
   * at 641 - the narrowest window this block still applies to - three columns
   * gave each service card 177.7 px, against the 223.4 px the card was composed
   * at and against the 254 to 278 px every other row gives its columns at the
   * same width. The card carries a 176 px watermark digit behind it, so at
   * 177.7 the digit was the whole card: `test/lib/contrast.mjs` at 641 sampled
   * the card's own 1 px purple border under it and read #5B2FD4 on #5B2FD4, one
   * of its two ratios below 4.5 at that width. Two columns give 278.5 px at 641
   * and 470 px at 1024, and the digit is inside the card at both. */
  .ccg-cards--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--ccg-narrow-row-gap); }
  .ccg-services { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--ccg-narrow-row-gap); }
  .ccg-teasers  { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--ccg-narrow-row-gap); }
  .ccg-footer__column { flex: 1 1 50%; max-width: 50%; }

  /* --- the two headings that are centred by being wider than their rail ---- */
  /* `left: 50%; width: max-content; transform: translateX(-50%)` centres a
     heading that must not wrap. Re-measured at 390 on 2026-09-02 by putting the
     three declarations back on the served element: the line lays out 439.1 px
     wide at x = -24.5 in a 330 px rail, against x = 30 and 330 px with this
     rule. Both the 830 px width and the x = -220 written here before, and the
     x = -192 design.md carried, were of a state nobody re-read. */
  .ccg-home__title--services,
  .ccg-home-news__title {
    left: auto;
    width: auto;
    max-width: 100%;
    transform: none;
  }

  /* --- the line under the call to action ----------------------------------
   *
   * It is a flex row of a pin, a sentence and three `strong` elements, and flex
   * makes each of those an item of its own. On a 330 px rail the row shrank the
   * sentence to a four-word-tall column with `Freiburg`, `Dresden` and
   * `bundesweit` standing beside it at three different heights - photographed at
   * 390 on 2026-09-02. As a block it is one sentence again and the pin is a word
   * in it. */
  .ccg-home__locality { display: block; }
  .ccg-home__pin { vertical-align: text-bottom; margin-right: var(--ccg-narrow-pin-gap); }

  /* --- the careers section, whose grid is 800px + 400px at every width ----- */
  .ccg-home-zukunft__inner {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  /* THE TEXT STARTS BELOW THE WAVE, AND 24 PX IS THE WIDE LAYOUT'S OWN NUMBER.
     The section's top wave is a share of the window (`--ccg-wave-zukunft-top`,
     22.083vw) while this padding was a fixed 48 px, so above a certain window
     the wave reaches past the padding and the `#D6C2FF` eyebrow is painted on
     the wave's near-white. Measured on 2026-09-02 with the padding at 48: the
     eyebrow's own top and the wave's bottom meet exactly at 798 px (both at
     document y 5669) and the wave is ahead of it above that - eyebrow 5620
     against a wave bottom of 5643 at 900, and 5555 against 5605 at 1024.
     `test/lib/contrast.mjs` read `07 Karriere` at 1.50 at 900 and at 1024.

     THE ADDEND IS NOT A NEW DECISION. Above this band the same two values are
     `padding-top: 448px` against `--ccg-wave-zukunft-top: 424px`, so the wide
     layout already says: the padding is the wave plus 24. Written against the
     token it holds at every width the token is a share at, and it puts the
     eyebrow a constant 152 px below the wave's bottom - which is what 448
     against 424 measures at 1920 as well.

     BELOW 640 THE FIXED VALUE COMES BACK, restated in that block. The wave is
     86 px at 390 and the eyebrow already clears it by 26 px there, and the
     390 px layout is the one being shown for agreement: with this rule scoped
     to the lap band, 390 reads padding 48, section height 772 and document
     height 12521 both before and after. Same two-layer shape as the ten hero
     tokens above. */
  .ccg-home-zukunft__main { padding-top: calc(var(--ccg-wave-zukunft-top) + 24px); }
  .ccg-jobs { margin: var(--ccg-narrow-zukunft-jobs-space) 0 0; }

  /* --- three constructions outside the home page that reach past the window -
   *
   * WHY THEY ARE HERE AT ALL, when the other twenty pages are the next cycle's.
   * These are not designs of those pages; they are the rules of 2.2 applied
   * where the acceptance measure of this cycle - no document wider than its
   * window - would otherwise stay red. Each of them is one line and each is the
   * same rule the home page's own rows take: a row that was composed for two
   * columns becomes one, and a decoration placed against a stage is clipped by
   * that stage. What those pages look like once they are one column is the next
   * cycle's, and nothing here decides it.
   *
   * Measured at 390 on 2026-09-02, after the header and the word-breaking:
   *
   *   /mergers-acquisitions and /strategische-...-bw   606 px, all of it
   *     `img.ccg-lt-hero__float`, an illustration placed at `left: 216px;
   *     width: 533px` against a hero that was 1200 wide when it was placed.
   *     Clipped by its own section, which is what `section#ccg-hero` and
   *     `section#ccg-loesungen` already do with their decorations at every width.
   *
   *   /blog   422 px, which is the card title above and not this. `.ccg-bl-holder`
   *     is a flex row of a 900 px column and an aside; at 390 the aside took the
   *     width and left the article column 30 px, inside which every card row was
   *     32 px wide. One column, and the card's own picture-beside-text row with
   *     it. Measured with the word-breaking above switched off, /blog reads 422
   *     with this rule in force either way - so this repairs what a visitor can
   *     read on that page rather than the document's width.
   */
  /* --- and one negative bleed that was measured against a narrower card ----
   *
   * The watermark digit sits at `right: -8.8px`, which puts 8.8 px of it past
   * the edge of a card that was 223 px wide when the offset was read. On a
   * 330 px card the digit is 176 px wide and the overhang lands on the card's
   * own 1 px purple border - and test/lib/contrast.mjs, run at 390 on
   * 2026-09-02, sampled that border pixel and reported #5B2FD4 text on a
   * #5B2FD4 ground at 1.00, its only reading below 4.5 out of 141. The digit is
   * decorative and the reading was an artefact of where the sample landed, but a
   * bleed measured against one card width is not a bleed at another, so the
   * digit is brought inside the card at the narrow bands. */
  .ccg-service__watermark { right: 0; }

  /* --- the news teaser's picture, whose box stopped matching the file --------
   *
   * ALL SIX FILES ARE 768 x 432 AND THE TEXT IS INSIDE THE PICTURE. The box is
   * `width: 100%` by a fixed `--ccg-news-image-height` of 200 px, painted
   * `cover`, and 200 px was composed against the 360.7 px column the three-across
   * row gives at 1920 - an aspect of 1.80 against the file's 1.778, so almost
   * nothing is cropped there. This block gives the row two columns and the one
   * below gives it one, and the height did not follow: measured on 07.09.2026,
   * the box is 280 x 200 at 390 and 252.5 x 200 at 641, and `cover` pays the
   * difference out of the width - 75.6 px of the drawn picture at 390 (21.3%,
   * 37.8 px off each side) and 103.1 px at 641 (29.0%). On a picture whose
   * content is lettering that is words: `e Vorteile der / igitalisierung von /
   * elegen`, `PSYCHOLO / VERKAUFE`, `RTIEBS- / HULUNGEN`.
   *
   * So the box keeps the file's own shape here rather than a height composed for
   * a column count it no longer has. Measured with this rule: 280 x 157.5 at 390,
   * 252.5 x 142 at 641 and 444 x 249.8 at 1024, and nought cropped on either axis
   * at all three on all six teasers. 1920 is untouched - it is above this block. */
  .ccg-teaser__image {
    height: auto;
    aspect-ratio: var(--ccg-narrow-news-image-aspect);
  }

  /* --- the hero's constellation, which is a different composition here ------
   *
   * The wide arrangement is one SVG with `viewBox="0 0 1600 900"` and
   * `preserveAspectRatio="xMidYMid slice"`, and `slice` scales to cover, so the
   * element's own shape decides how much of the composition survives. Measured
   * on 07.09.2026 against this build, reading the SVG's layout box rather than
   * its painted one - the painted box carries the -4deg tilt above and is not
   * what the fitting is done against:
   *
   *     window     layout box    element aspect   viewBox aspect
   *     1920x1080  1920 x 1080   1.78             1.78
   *     1024x844   1024 x  844   1.21             1.78
   *      641x844    641 x  844   0.76             1.78
   *      390x844    390 x  844   0.46             1.78
   *
   * At 390 that is a fivefold enlargement of which a vertical strip survives:
   * one of the three image nodes, cut by the right edge, with the lines running
   * to nodes outside the window. The narrow arrangement is a portrait field of
   * its own with `meet`, so the whole of it is inside the element's box at every
   * one of those shapes. Which of the two is served is decided here, because
   * the attribute cannot be - see the rule beside the wide element for the
   * measurement.
   */
  .ccg-home-hero__constellation--wide { display: none; }
  .ccg-home-hero__constellation--narrow { display: block; }

  /* AND THE BOX IS NOT THE WHOLE WINDOW HERE, which is the one thing `meet`
     alone does not settle. `meet` fits the field into the box, so the box's own
     height is what decides the scale wherever the width would allow more - and
     over `height: 100vh` that is every width in this band. Measured at 641 on
     07.09.2026 with the box at 100vh: the field painted 577 x 788 ending at
     y 823.6, and the headline block starts at 542, so the two lower image nodes
     lay across `Sie fuehren Ihr Unternehmen.` and `Wir fuehren Ihre Zahlen.`.
     Photographed before this rule was written.

     THE HEIGHT IS THE WINDOW MINUS WHAT THE TEXT NEEDS, and not a number. The
     text block is bottom-anchored in a section of `100vh + 128px`, so what it
     occupies of the window does not move with the window: measured at 390, the
     headline's top is at 510 in an 844 px window and the section ends at 908,
     which is 334 px of window below the headline's top. The token is that 334
     plus the space the graphic keeps clear of the headline, and it holds at any
     window height for that reason - at 844 the field paints 362 x 464 and at
     800 it paints 328 x 420, both of them the same distance above the
     headline.

     THAT LAST SENTENCE WAS TRUE OF ONE WINDOW AND WAS WRITTEN AS THOUGH IT HELD
     AT ANY HEIGHT, and the correction is the `max()` below rather than a new
     number. `334px of window below the headline's top` was read at 390x844
     alone. Re-measured on 07.09.2026 at two short windows: at 844x390 the
     headline's top is at 88 - 302px of window below it - and at 740x320 it is at
     18, the same 302. The share is a function of the WIDTH, because the h1 wraps
     to two lines at 390 and one at 641 and above; it is not a function of the
     height, which is the thing `calc(100vh - 380px)` subtracts from. So at a
     window shorter than 380 the subtraction goes negative, and just above it it
     goes to almost nothing: measured, 10px at 844x390 - where the badge painted
     5.6 x 5.6px - and 0px at 740x320. A phone held sideways is inside this band
     (844 <= 1024) and lost the graphic entirely.

     THE FLOOR IS READ OFF THIS RULE RATHER THAN CHOSEN AGAINST IT. 220 is what
     `calc(100vh - 380px)` itself yields at a 600px window, and 600 is the
     shortest window measured on 07.09.2026 at which the subtraction still
     produces a graphic that is whole and clear of the headline: at 900x600 the
     box is 220, the image nodes are 44px across and the field ends 77.5px above
     the headline. So the rule reads `never less than the smallest working box
     this subtraction already produces`.

     AND IT MOVES NOTHING AT ANY WINDOW 600px TALL OR TALLER. Measured after the
     change at 390x844 (box 464, nodes 92.8, clearance 45), 390x800 (420),
     390x750 (370, clearance 45.2), 390x700 (320, clearance 45.3) and 900x600
     (220, clearance 77.5): the reserve wins at all five. A floor of 464 - the
     height the agreed 390x844 window produces - was measured first and rejected
     for exactly this. It would have bound at every window shorter than 844, and
     at 390x750, an ordinary phone viewport once the browser's own chrome is off,
     it turned 46px of clearance into a 49px overlap. The gate asked that 390x844
     not move; a floor that leaves 390x844 alone and breaks 390x750 answers the
     letter of that and not the point of it.

     WHAT IT COSTS, AND IT IS RECORDED RATHER THAN HIDDEN. In a landscape window
     the graphic overlaps the headline - 132.5px at 844x390 and 202.3px at
     740x320 - because the headline's top is at 88 and at 18 there and no graphic
     of any size would clear it. The graphic is `pointer-events: none` at
     z-index 2 and `.ccg-home-hero__inner` is at z-index 5, so the text paints
     over the graphic rather than under it. And at 220 the badge's logo renders
     17.6px tall against the 38px this theme serves the same file at in the
     header at this band: on a phone held sideways the wordmark is present and is
     not legible at reading size. That is a limit of this composition at that
     window and it is written down rather than closed. Photographed at 844x390
     and 740x320 into ~/docker/projects/.ccg-screens/2026-09-07-op4220/. */
  .ccg-home-hero__constellation--narrow {
    height: max(var(--ccg-narrow-hero-graphic-floor),
                calc(100vh - var(--ccg-narrow-hero-graphic-reserve)));
  }

  /* AND WHERE THE RESERVE'S PREMISE IS VOID, THE GRAPHIC TAKES THE SPACE THE
     REFERENCE NARROW WINDOW GIVES IT. The reserve exists to keep the graphic
     ABOVE the headline. That is possible only where there is space above the
     headline, and in a window wider than it is tall there is none: measured on
     07.09.2026, the headline's top is at 88 at 844x390 and at 18 at 740x320, so
     a graphic that cleared it would be 88px and 18px tall with its image nodes
     at 17px and 3px. Nothing is bought by keeping the graphic small there, and
     the 220px floor above buys 44px nodes and a badge logo 17.6px tall - present
     and not legible.

     So in a short landscape window the graphic is given 464px, which is the
     height the agreed narrow window produces (844 - 380) and the size the whole
     composition was signed off at: image nodes 92.8px, badge logo 94.5 x 37.1px
     against the header's own 38px at this band. The text paints over it -
     z-index 5 against this element's 2, and `pointer-events: none` - and it was
     photographed at both windows before this rule was written rather than
     reasoned about.

     THE TWO CONDITIONS, AND WHICH OF THEM IS MEASURED. `orientation: landscape`
     is the premise above and is measured: at 900x600, which is landscape and
     inside this band, the headline's top is at 298 and the subtraction still
     leaves 220px clear of it, so the premise HOLDS there and this rule must not
     reach it.

     THE BOUND IS 430 AND IT WAS 500, AND THE 500 WAS MEASURABLY WRONG. 500 was
     chosen rather than read - it sat between the two measured cases, 390 where
     the subtraction gives 10 and 600 where it gives 220 - and the gate then
     measured the range it had been given. At 800x500 the headline's top is 198px
     down and at 640x480 it is 242px down, so the premise `there is nothing above
     the headline` is false over most of 431..500; and because this rule hands
     those windows a 464px field, the bold line crosses the left photo node at
     800x500 and the setup line runs across both lower nodes at 640x480. One pixel
     taller - 800x501 - the same page is clean. Before this rule existed those
     windows drew a 100-120px graphic that cleared the text, so the chosen side of
     the old boundary was the harmful one.

     430 is read rather than chosen: it is the tallest landscape viewport a
     current phone reports (932x430), which is the device class the reserve's
     premise was written about. 800x500 and 640x480 now fall to the `max()` floor
     and behave as they do at 501.

     AND WHAT THIS RULE DOES AT 430 AND BELOW IS HIDE THE GRAPHIC, NOT SIZE IT.
     Giving those windows the signed-off 464px field was tried first and
     photographed: at 844x390 the headline's top is 88px down, so a 464px field
     runs straight through it - `Sie fuehren Ihr Unternehmen.` crosses the upper
     photo node and the sub-line crosses the badge and both lower nodes. The text
     paints on top and is still there, but it is white type over three
     photographs, and that is worse than the 10px sliver this whole reserve was
     opened to repair. There is no size that fits: the premise `the graphic sits
     above the headline` needs vertical room a 390px-tall window does not have at
     any scale.

     SO IT IS A DEVIATION AND IT IS DECLARED AS ONE. On a phone held sideways this
     hero is the photograph, the headline and the wave, and no constellation. The
     section is still delivered - what is dropped is one decorative layer of it,
     for the reason `site-theme` requires a dropped thing to be dropped for. It is
     recorded in the change's design record beside the measurement that produced
     it.

     What is NOT closed: the floor itself only clears the headline down to about
     522px of window at 641-1024 wide and about 554 at 390, so a short PORTRAIT
     window - an Android phone in split screen at 390x520, which this rule never
     reaches - still overlaps by ~34px. It is recorded rather than repaired, and
     no width the suite drives can see it because the harness fixes the viewport
     height at 1080. */
  @media (orientation: landscape) and (max-height: 430px) {
    .ccg-home-hero__constellation--narrow { display: none; }
  }

  /* --- the service card's photograph is centred from here down --------------
   *
   * A DEVIATION FROM THE WIDE LAYOUT, RECORDED AS ONE in
   * openspec/changes/repair-the-mobile-home-page/design.md. `right: 20.8px` on a
   * 223 px card puts the 128 px photograph 27 px right of the card's centre,
   * which reads as a deliberate overhang. Measured at 1024 on 07.09.2026 the
   * same rule on the 470 px card this band gives it put the photograph's centre
   * at 414.2 against the card's own 265 - 149 px off centre - and at that
   * distance it reads as a mistake rather than as a placement. There is no
   * collision at this band and this is not repairing one; it is the same
   * lopsidedness the 640 band repairs, at the width where it is worse.
   *
   * `margin-inline: auto` between `left: 0` and `right: 0` AND NOT A TRANSFORM.
   * `.ccg-service:hover .ccg-service__photo` sets `transform: scale(...)
   * rotate(...)`, so a centring written as `translateX(-50%)` would be replaced
   * by the hover rule the moment a pointer touched the card and the photograph
   * would jump half its own width. The margin and the transform are different
   * properties and do not fight. */
  .ccg-service__photo { left: 0; right: 0; margin-inline: auto; }

  .ccg-lt-hero { overflow-x: clip; }

  .ccg-bl-holder { display: block; }
  .ccg-bl-content { padding-right: 0; }
  .ccg-bl-card__row { display: block; }

  /* --- the industries constellation, which becomes a list ------------------
   *
   * NOTHING IS DROPPED AND ONE THING CHANGES ITS TRIGGER. The stage is 1200 x
   * 600 with six nodes at percentage coordinates and a curve drawn between each
   * of them and the hub. The coordinates and the curves are of that stage, so
   * neither survives a 330 px rail; the six industries, their labels and the
   * sentence each of them carries all do, and they are what the section is.
   *
   * THE SENTENCE IS THE PART THAT WOULD HAVE BEEN LOST QUIETLY. At the wide band
   * it is a tooltip that appears on hover, and a finger has no hover - so left
   * alone it would have been six paragraphs of text a phone can never reach.
   * Here it is always shown, under the label it belongs to.
   *
   * WHAT IS DROPPED IS THE DRAWING: the six curves, the six ambient arcs, the
   * twelve drifting dots and the halo, all of which are `svg.ccg-network__lines`
   * and all of which connect points that no longer exist. That is a deviation
   * and it is named as one in design.md and in proposal.md.
   */
  .ccg-network {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ccg-narrow-node-gap);
    justify-items: center;
    width: auto;
    /* AND THE 2:1 GOES WITH THE HEIGHT. The stage carries `aspect-ratio: 1200/600`
       unconditionally since `lay-out-the-middle-band-and-unstretch-the-graphics`,
       so `height: auto` on its own no longer means what it meant: the ratio would
       set the height from the width and hold a stacked grid of six nodes to half
       its own width. Measured at 390 with the ratio left in place: the stage
       computed 330 x 165 against the 1109.2 its content needs. */
    aspect-ratio: auto;
    height: auto;
    transform: none;
  }

  .ccg-network__lines { display: none; }

  .ccg-network__hub {
    position: relative;
    grid-column: 1 / -1;
    top: auto;
    left: auto;
    width: var(--ccg-narrow-hub-size);
    height: var(--ccg-narrow-hub-size);
    transform: none;
    animation-name: ccgNarrowHubFloat;
  }

  /* 0-2-0, which is what beats the six `.ccg-branche--N` rules that carry the
     coordinates and the sizes. */
  .ccg-network .ccg-branche {
    position: static;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: auto;
    height: auto;
    transform: none;
    animation-name: ccgNarrowNodeIn;
    text-align: center;
  }

  .ccg-network .ccg-branche__circle {
    flex: none;
    width: var(--ccg-narrow-node-size);
    height: var(--ccg-narrow-node-size);
  }

  .ccg-network .ccg-branche__label {
    position: static;
    margin-top: var(--ccg-narrow-row-gap);
    transform: none;
    white-space: normal;
  }

  .ccg-network .ccg-branche__tooltip {
    position: static;
    width: auto;
    max-width: 100%;
    margin-top: var(--ccg-narrow-row-gap);
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    text-align: center;
  }

  /* The pointer that pointed at a circle the panel no longer floats beside. */
  .ccg-network .ccg-branche__tooltip::before { display: none; }

  /* AND THE PANEL'S OWN HEADING, WHICH IS THE LABEL A SECOND TIME. The markup
     emits `node.label` twice - once as the label under the circle and once as
     the panel's `h4` - and at the wide band that costs nothing because the panel
     is hidden until a pointer opens it. This block opens the panel permanently,
     for the reason given above it, and the name then stands twice on every one
     of the six nodes. Measured at 390 on 07.09.2026: 6 of 6 nodes rendered both
     elements with a height above nought and identical text - Automobil,
     Handwerk, Gastronomie, Handel, `High Tech | IT`, E-Commerce - the second
     42.6 px under the first; at 1920 the panel computes `visibility: hidden` and
     0 of 6 do.

     The heading is what goes, not the label: dropping the label instead would
     move the name off the circle it belongs to and into the panel, which is a
     larger departure from the wide composition than this section already makes.
     Measured with this rule at 390: 0 of 6, and `.ccg-network` 1863.8 px against
     2030.8. */
  .ccg-network .ccg-branche__tooltip-title { display: none; }

  /* And the hover state of a panel that is already open. 0-3-0, so it wins
     against the six `.ccg-branche--N:hover` rules that would otherwise slide an
     always-visible panel sideways under a laptop's pointer. */
  .ccg-network .ccg-branche:hover .ccg-branche__tooltip { transform: none; }

  /* ==========================================================================
   * THE INNER FAMILIES: /recruiting AND THE FIVE SERVICE PAGES
   *
   * Everything from here to the end of this block was added by
   * `lay-out-the-inner-families-for-a-phone`. The narrow layout above it was
   * built for the home page and finished there; these seven routes were served
   * the wide construction at every width, and what that produced is in that
   * change's design.md with the boxes. The short form: five service heroes laid
   * their first line ABOVE the top of the document, a 1068 px process chapter
   * kept 352 px of padding on a 352 px box so its body computed nought and set
   * one letter per line, and six horizontal rows never lost a column.
   *
   * FOUR RULES AND NOT SEVEN PAGES. Each rule below is written once against the
   * component and holds on every route that carries it; where a rule is scoped
   * to one family it is because the component belongs to one family, and it says
   * so.
   *
   * AND `/about` IS EXCLUDED BY NAME, WHICH THIS COMMENT ONCE CLAIMED WAS FREE.
   * It said: "`/about` shares `.ccg-sv__row` with the service hero and is out of
   * this change's scope, which is why the hero rules name `.ccg-sv-hero`." That
   * was false, and measurably so. `partials/inner/about-hero.html.twig` opens
   * `<section class="ccg-sv ccg-sv-hero ccg-ab-hero">`, so naming `.ccg-sv-hero`
   * INCLUDED it: measured at 390 on 09.09.2026 with the rules unscoped, the
   * /about hero's label moved from y 16.7 to y 135, its punch line from 57.6 px
   * in a 206.7 px column to 38 px across the 330 px rail, and its emblem from a
   * 133.3 x 166.7 box beside the text to a 390 x 487.5 one under it. Every one
   * of those is an improvement and NONE of them was photographed, judged against
   * this change's acceptance bar, or written into its record.
   *
   * SO THE EIGHT RULES CARRY `:not(.ccg-ab-hero)` AND /about KEEPS WHAT IT HAD.
 * (Six when this comment was written; two were added later and the count was
 * not re-read until the gate of 14.09.2026 counted them.)
   * The reason is not that the hero looks worse under them - it looks better,
   * and the numbers above are in this change's design.md so the next cycle can
   * start from them. It is that the rest of that page is not laid out for a
   * phone at all: measured the same day at 390, `.ccg-ab-tl__entry` sets its
   * prose in ~100 px columns and `.ccg-ab-mission` keeps two ~110 px columns,
   * and `.ccg-ab-mission__compass` lays an SVG letter at x -70.2..-32.2. A
   * repaired hero over seven unrepaired sections is a page in two styles, and
   * the floor `test/lib/pages_read.py` would then have to give /about could not
   * be met without repairing the compass too. /about is named under `## Out of
   * scope` in this cycle's proposal and it stays there, whole.
   *
   * WHY MOST OF IT IS AT THIS BAND AND NOT AT 640. Measured on 09.09.2026 by
   * walking every element that carries its own text on all seven routes: at 1024
   * only /recruiting paints text outside the window (13 elements); at 641 five
   * service pages do as well (the industries row's third column, and the
   * roadmap's). So the recruiting rules and everything whose arithmetic breaks
   * before 641 are here, and only what needs the 330 px rail is at 640.
   * ========================================================================== */

  /* --- 1. the inner hero reserves the header's height ----------------------
   *
   * `.ccg-sv-hero .ccg-sv__port` centres its content in `min-height: 100vh` with
   * no padding at all. That is correct while the content is shorter than the
   * window and wrong the moment it is not: a centred flex item overflows BOTH
   * ends. Measured at 390x844 on 09.09.2026 the hero's content is 967.4 px in an
   * 844 px window, so `.ccg-sv-hero__label` - the first line of the page - sat at
   * y = -33.8 on all five service pages and painted through the CCG mark.
   *
   * THE HOME PAGE'S ANSWER IS THE ONE FOLLOWED. `.ccg-home-hero` never centres:
   * it is `align-items: flex-end` in `min-height: calc(100vh + space)` with the
   * text anchored to the foot, so nothing it holds can rise into the header.
   * This is the same move for a hero whose content starts at the top instead -
   * anchor it, and reserve the header above it - rather than a second mechanism.
   *
   * THE RESERVE IS THE HEADER PLUS ONE UNIT OF THE BAND'S OWN SECTION SPACE, and
   * both are tokens this file already reads. It is not a number chosen for this
   * rule: at this band that is 70 + 128 = 198. At 640 `--ccg-section-space` is 64
   * and the reserve is 134.
   *
   * THIS COMMENT ALSO SAID THE 198 WAS CORROBORATED BY THE WIDE LAYOUT - "the
   * clearance the WIDE layout gives the same label ... is 198.2 ... to within a
   * fifth of a pixel" - and the gate withdrew that. The wide hero centres its
   * content in `min-height: 100vh` and lifts the text by `-4vh`, so the clearance
   * is `(100vh - 459.2)/2 - 4vh`: 198.2 at the 1080-tall window the harness
   * drives, 115.4 at 900, 89.6 at 844. It is a function of the viewport's height
   * and not of the wide layout's spacing, so the agreement at 1080 is a
   * coincidence and not a derivation. The reserve stands on its two tokens.
   *
   * AND THE LIFT COMES OFF. `--ccg-sv-hero-text-lift: -4vh` raises the text
   * block off the centre line of a centred layout; there is no centre line here,
   * and at 844 px of window the lift is -33.8 px - which is the whole of the
   * offset measured above. `transform` and not the token, because the token is a
   * design value for the wide band and nothing else reads it. */
  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv__port {
    align-items: flex-start;
    padding: calc(var(--ccg-header-height) + var(--ccg-section-space)) 0 var(--ccg-section-space);
  }

  /* `transform: none` and not `translateY(0)`, and on /about it would have been
     neither: `.ccg-ab-hero` sets `--ccg-sv-hero-text-lift` from
     `--ccg-ab-hero-text-lift`, which ccg.yaml:1581 declares as `0px`. So the
     round trip through this rule on that page was a no-op with a real cost -
     a rule reaching a route nobody had looked at. Measured rather than argued:
     the token is 0, the lift it withdraws is 0, and the guard above is there
     for the five rules beside it, not for this one. */
  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv-hero__text { transform: none; }

  /* --- 2. the service hero's row becomes one column ------------------------
   *
   * 66.6667 and 33.3333 of the rail, text beside graphic. Measured at 1024 the
   * graphic box is 471.4 wide at x = 610.9 against a text column ending at 659,
   * so the two overlap by 48 px and the graphic ends 58 px past the window; at
   * 641 the overlap is 10 px and the overhang 20. Neither shows in a check on
   * the document's width - `.ccg-sv` computes `overflow: hidden` - and neither
   * carries text, so the inventory in rule 4 does not see them either. They were
   * found by looking, which is what this change's acceptance bar is for.
   *
   * TEXT FIRST AND GRAPHIC UNDER IT, which is the document order. The home
   * page's narrow hero puts its graphic above the headline because the wide
   * composition has it there; here the wide composition has it beside, and
   * stacking in source order is what puts the page's own headline directly under
   * the header on all five routes, the way every other family on this site
   * opens.
   *
   * The measures are left alone: `--ccg-sv-hero-text-width` (720) and
   * `--ccg-sv-hero-sub-width` (580) still hold the line length at this band and
   * are simply wider than the rail at 640. */
  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv__row {
    flex-direction: column;
    align-items: stretch;
  }

  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv__col--8,
  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv__col--4 { width: 100%; }

  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv__col--4 {
    justify-content: center;
    margin-top: var(--ccg-section-space);
  }

  /* --- 2a. AND THE GRAPHIC BLEEDS TO THE WINDOW HERE AND NOT AT 640 ---------
   *
   * THIS RULE WAS DECLARED AT 640 AND THAT WAS A DEFECT, corrected here on
   * 09.09.2026 with the band it belongs to measured rather than assumed.
   * `.ccg-sv-hero__anim` is `width: 100%; max-width: 640px` scaled by
   * `--ccg-sv-hero-anim-scale` (1.6). At 1920 the column under it is 400 px, so
   * the graphic draws 640 inside a 1920 px section and touches no edge, and
   * before this change at 641 it drew 267.2 px at x 393.9 - 20.1 px past the
   * right edge of the window and nothing off the left.
   *
   * Rule 2 above gives that column the whole rail from 1024 down, and 1.6 of the
   * rail is wider than the window across the whole of this band. Measured on
   * /externer-cfo the day the repair was written, with the bleed still at 640:
   *
   *     641   layout box 581 -> painted 929.6 at x -144.3   144.3 px cut EACH side
   *     1024  layout box 640 -> painted 1024  at x 0        nothing cut, by luck:
   *                                                         `max-width: 640` x 1.6
   *                                                         is exactly 1024
   *
   * So the worst reading in this band was 7.2 times the 20.1 px the same page
   * had BEFORE the change, and it sat in the one range no case in this
   * repository drives. That is what a repair argued only from the 390
   * measurement buys.
   *
   * THE REPAIR IS THE ONE `repair-the-mobile-home-page` RECORDS FOR A COMPOSED
   * GRAPHIC IN A BOX IT DOES NOT FIT: change the box, not the fitting. The
   * graphic is as wide as the rail plus its two gutters - which is the window,
   * at every width in this band, because `--ccg-measure-gutter` is 30 and the
   * measure's own inset is 10 at 390, 641 and 1024 alike - and it is drawn at
   * 1:1, so nothing of it lies outside anything. `xMidYMid meet` on the svg is
   * untouched, so what the graphic draws is unchanged; what changes is that the
   * element now RESERVES the space it paints instead of overhanging it. */
  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv-hero__anim {
    width: calc(100% + 2 * var(--ccg-measure-gutter));
    max-width: none;
    margin-inline: calc(-1 * var(--ccg-measure-gutter));
    transform: none;
  }

  /* --- 3. the horizontal rows, one rule per row ----------------------------
   *
   * THE SAME ANSWER THE HOME PAGE'S OWN ROWS WERE GIVEN in
   * `repair-the-mobile-home-page`: a row loses columns at this band and keeps
   * one at 640. `.ccg-cards--3`, `.ccg-services`, `.ccg-teasers` and
   * `.ccg-network` are all written that way twenty lines above; these are the
   * same rule for the rows the inner families carry, and they are two columns
   * here for the same reason those are - a card composed for a 346.7 px column
   * is not a card at 100.
   *
   * `minmax(0, 1fr)` AND NOT `1fr`, and that is the defect rather than a habit.
   * `1fr` floors each track at the item's min-content, so the industries row
   * measured 166.8 px per column inside a 330 px grid at 390 - three columns and
   * two 64 px gaps in 298 px of room - and painted columns two and three at
   * x 300..420 and x 531..652. The zero minimum is what makes the track able to
   * be as narrow as the rail. */
  .ccg-sv-feature-grid,
  .ccg-sv-cons__grid,
  .ccg-sv-cases,
  .ccg-sv-san__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* --- and the constellation behind that grid is WITHDRAWN ------------------
   *
   * A DECLARED DEPARTURE, not a simplification. `svg.ccg-sv-cons__lines` draws
   * seven dashed curves and sends seven dots travelling along them, and every one
   * of the fourteen is drawn between the centres of a THREE-COLUMN grid: the
   * paths run through x = 200, 600 and 1000 of a 1200-unit viewBox and stop at
   * y = 175 and 525. The line above makes that grid two columns, and the rule in
   * the 640 block makes it one. The curves then connect nothing.
   *
   * MEASURED, on 11.09.2026 against the working tree, the cards' own centres in
   * the viewBox's units: 189.3 / 600 / 1010.7 at 1920 and at 1440 and 188.6 /
   * 600 / 1011.4 at 1180 - three columns, and the curves land on them. At 768 the
   * grid is two and the centres are 303.4 / 896.6 on three rows; at 640 and 390
   * it is one and all six cards stand on 600. There is no scaling of a
   * three-column drawing that makes it a two- or one-column one.
   *
   * AND IT IS NOT ONLY WRONG, IT IS VISIBLE. The element is `inset: 0` in a
   * container whose height is the grid's, so at 390 it is 330 x 1784.8 against a
   * 1200 x 700 viewBox - a skew of 9.27 - and its `preserveAspectRatio="none"`
   * paints each travelling dot, radius 4.5 to 5, at 2.75 px across and 25.50 px
   * down: a purple stripe standing by itself beside the first card on all five
   * service pages.
   *
   * SAME ANSWER AND SAME ARGUMENT AS `svg.ccg-network__lines` on the home page
   * and `.ccg-rc-step__line` above: a line drawn between two things that are no
   * longer where it was drawn to is removed rather than rescaled. Nothing that
   * carries a word goes with it - the element is `aria-hidden` and decorative,
   * and all six cards are served. */
  .ccg-sv-cons__lines { display: none; }

  /* The service hero's particle field takes its portrait rendering here, for the
     reason written where the wide one is declared: below this width the hero
     stacks and the layer's box goes from 1025 x 800 to 1024 x 2152.5 while
     keeping its width, which paints a dot of radius 2 at 0.98 x 5.33 px. */
  .ccg-sv-hero__particles .ccg-sv-particles--wide { display: none; }
  .ccg-sv-hero__particles .ccg-sv-particles--tall { display: block; }

  /* --- /about's spectrum takes its narrow drawing here ---------------------
   *
   * THE BAND IS THIS ONE AND NOT THE PHONE'S, AND THE READING IS WHY. The wide
   * field is 1500 units across and its labels are declared at 10, 12, 20, 22 and
   * 38 of them, so what a reader sees is the declared size times the field's
   * scale. Measured 16.09.2026 on a container from this tree, `.ccg-ab-spec__svg`
   * with the 12-unit label inside it AND WITH THE 10-UNIT ONE, which is the
   * smallest the field carries and therefore the one the floor is decided on:
   *
   *     393   svg  393 x 157.2   scale 0.262    12 ->  3.14    10 ->  2.62 px
   *     640   svg  640 x 256     scale 0.427    12 ->  5.12    10 ->  4.27 px
   *     641   svg  581 x 232.4   scale 0.387    12 ->  4.65    10 ->  3.87 px
   *    1024   svg  964 x 385.6   scale 0.643    12 ->  7.71    10 ->  6.43 px
   *    1180   svg 1120 x 448     scale 0.747    12 ->  8.96    10 ->  7.47 px
   *    1440   svg 1200 x 480     scale 0.800    12 ->  9.60    10 ->  8.00 px
   *
   * THE SECOND COLUMN IS THE ONE THAT DECIDES, and this comment carried only the
   * first for one round. `DESTINATION` is declared at 10 units, so the field's
   * minimum is 8.00 px at 1440 and stays there at 1920 and 2560, where the rail
   * is capped and the scale does not grow - which is exactly what GRAPHIC_TYPE in
   * test/lib/pages_read.py pins for `ccg-ab-spec__svg--wide` and what the
   * change's limitation 3 records. Read off the 12-unit column alone the field
   * looks as though it clears the floor from 1440 up; it does not clear it at any
   * width this site is used at.
   *
   * The floor those are read against is `--ccg-sv-mtx-badge-size`, 9.28px, which
   * is the smallest type this theme sets for any element at any width - measured
   * the same day by reading the computed `font-size` of every element carrying
   * its own text on all thirty-six routes at 393, where the minimum is 9.28 and
   * `.ccg-sv-mtx__badge` is what carries it. So the wide field falls short across
   * the whole of this band AND the phone's, and a switch at 640 would have left
   * 641..1024 reading between 3.87 and 6.43 px. What the switch at 1024 buys is
   * the phone band, where the narrow drawing reads 10.72 at 390; from 1025 up the
   * wide field is short by between 1.28 and 5.41 px, and that is recorded in the
   * change's known limitations rather than closed here, because closing it would
   * take a third width literal in this file.
   *
   * AND THE BOX IS CAPPED AT THE FIELD'S OWN UNIT COUNT.
   * `--ccg-ab-spec-narrow-measure` is 400px, which is the 400 units the drawing
   * is composed in: above it one unit would paint more than one pixel and a phone
   * composition would be magnified into a poster 2198 px tall at 1024 - the box
   * is 964 there and 964 / 400 x 912 is 2198. Below it the cap does nothing,
   * which is every width the phone band covers. */
  /* --- and /lohnabrechnung's dashboard takes its portrait drawing here ------
   *
   * THE BAND IS THE SAME ONE THE SPECTRUM SWITCHES AT, and the readings say it
   * is the right one for this graphic too. The wide field is 600 units across
   * and its smallest labels are declared at 10 of them, so what a reader sees is
   * 10 times the field's scale. Measured 17.09.2026 on a container built from
   * this tree, the element's own box against its 600 units:
   *
   *     390   svg 276.81 x 129.17   scale 0.4614   10 ->  4.61 px
   *     640   svg 526.81 x 245.84   scale 0.8780   10 ->  8.78 px
   *     641   svg 267.05 x 286.36   scale 0.4451   10 ->  4.45 px
   *     768   svg 341.14 x 310.36   scale 0.5686   10 ->  5.69 px
   *    1024   svg 490.47 x 285.98   scale 0.8174   10 ->  8.17 px
   *    1025   svg 491.05 x 285.98   scale 0.8184   10 ->  8.18 px
   *    1180   svg 581.47 x 307.98   scale 0.9691   10 ->  9.69 px
   *    1440   svg 628.14 x 307.98   scale 1.0469   10 -> 10.47 px
   *
   * so the wide field clears the 9.28 px floor from 1180 up and at no width
   * below it. The portrait drawing serves this band and reads 10.01 px at its
   * own worst width, which is 641.
   *
   * WHAT IS STILL SHORT AND IS NOT CLOSED HERE. Between 1025 and the width at
   * which the wide field's scale reaches 0.928 the smallest label paints between
   * 8.18 and 9.28 px - up to 1.1 px under the floor, over a band no width this
   * suite drives falls in. Closing it would take a third width literal in this
   * file, which the two beside `--ccg-ab-spec-narrow-measure` argue against, or
   * a smallest label of 12 units, which would put this graphic's unit caption
   * within a unit of its category label. It is written down rather than closed,
   * the way the spectrum's own 1025..1024-and-up shortfall is. */
  .ccg-sv-dash .ccg-sv-dash__svg--wide { display: none; }
  .ccg-sv-dash .ccg-sv-dash__svg--tall { display: block; }

  .ccg-ab-spec .ccg-ab-spec__svg--wide { display: none; }
  .ccg-ab-spec .ccg-ab-spec__svg--narrow {
    display: block;
    width: 100%;
    max-width: var(--ccg-ab-spec-narrow-measure);
    margin-inline: auto;
  }

  /* The industries row's own gaps are 56 and 64, composed for a 1200 px stage
     with a drawn constellation behind it. `--ccg-narrow-row-gap` is what every
     other row of this site takes once it has fewer columns than it was composed
     for, and it is 24. */
  .ccg-sv-cons__grid { gap: var(--ccg-narrow-row-gap); }

  /* The roadmap keeps its track at this band, so it keeps a row: six phases in
     one line give each 145 px at 1024 and 74 at 641, and 74 is where the phase
     title stacks. Three across is two rows of three under one drawn line, which
     is the most of the wide arrangement that survives here. */
  .ccg-sv-road__phases { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* --- 4. the process chapters get their body back -------------------------
   *
   * `--ccg-sv-chapter-pad` is `64px 267px 64px 85.4375px` and the two horizontal
   * values are the margins the live page leaves beside a 580 px body in a 1068 px
   * chapter. They are not shares, they are pixels, so they do not shrink with the
   * box: measured at 390 the chapter is 352.4 px wide and 352.4 px of it is
   * padding. `.ccg-sv-process` measures 11280 px tall at 390 against 1186 for
   * `#ccg-branchen` directly above it, which is three paragraphs set one letter
   * to a line.
   *
   * THE BODY KEEPS ITS MEASURE AND IS CENTRED, so this is not "the text now runs
   * the width of the window": `--ccg-sv-chapter-body-width` (580) still holds,
   * and `margin-inline: auto` puts it under the heading the chapters container
   * already centres. At 1024 the body is 580 in a 932 px chapter, at 641 it is
   * 549, at 390 it is 330. */
  .ccg-sv-chapter,
  .ccg-sv-chapter:nth-child(even) { padding: var(--ccg-narrow-sv-chapter-pad); }

  .ccg-sv-chapter__body { margin-inline: auto; }

  /* AND THE TWO DECORATIONS GO, WHICH IS A DEVIATION AND IS DECLARED AS ONE.
     Both live in the margin the wide chapter has beside its body: measured at
     1920, the body ends at x = 1091.4 and the watermark digit runs 1178.7..1440.6,
     87.3 px clear of it, and the decoration sits at `right: 18%` of the same
     margin. Once the body is centred and takes the measure there is no margin
     left, and `right: 5%` of a 932 px chapter puts the 261.9 px digit at
     623.5..885.5 against a body at 176..756 - 132 px of it behind the paragraph.
     A 256 px digit behind body text is not the composition the wide band draws;
     it is a different one.
     The precedent is the home page's `svg.ccg-network__lines`, dropped in
     `design-the-mobile-view` for the same reason in the same words: a decoration
     placed against coordinates that no longer exist. Shrinking rather than
     dropping was measured first - the digit keeps its share of the chapter, 24.52
     per cent, which is 80.9 px of type at 390 - and rejected because the share
     it keeps is a share of a margin the narrow chapter does not have. */
  .ccg-sv-chapter__watermark,
  .ccg-sv-chapter__decor { display: none; }

  /* --- 5. the Leistung family's row ----------------------------------------
   *
   * `.ccg-lt-row` is a flex row with per-column percentage widths and no wrap, so
   * a 25 per cent column is 145 px at 641 and 82 at 390. Measured at 390 on
   * /mergers-acquisitions: `.ccg-lt-card__link` computes 12.7..17.8 px wide at
   * 19.52 px of type, which is `Strategische Beratung` set one letter to a line
   * over twenty lines. The proposal's table records one control outside the
   * window on this route and that is all its probe could see - the row itself is
   * inside the window and unreadable, which is the defect the inventory in rule 4
   * does not catch and a person does.
   *
   * TWO ACROSS FOR THE QUARTERS AND THE THIRDS, ONE FOR THE HALVES, which is the
   * same step the grids above take. The eighths are already the wide column of a
   * 8/4 split and have nothing beside them worth keeping at this width. */
  /* --- the Leistung hero's floating shape, as shares of its own band ---------
   *
   * `left: 216px; top: 449.8px; width: 533px; height: 634px` inside a band whose
   * height is a fixed 945.8 and whose width is the window. Those are the live
   * page's own numbers at 1920, where the shape lies over the photograph's left
   * half and hangs 138 px past the band on purpose - the previous cycle's own
   * comment records that overhang as the arrangement to reproduce.
   *
   * At 390 the same pixels put it at x 216..749, over the right half of the
   * window and 359 px outside it, and the 138 px of overhang lands on the heading
   * of the section below: photographed at 390 on 09.09.2026,
   * `Mergers & Acquisitions` in the Angebot band printed over the shape.
   *
   * Every value below is the live pixel divided by the band it was measured
   * against - 216/1920, 449.8/945.8, 533/1920 - so the shape keeps its place in
   * the composition rather than its place on a 1920 px screen. The height is
   * `auto` and not 634/945.8, because a share of the height and a share of the
   * width are two different scalings and the second one would flatten the shape;
   * `auto` against the file's own 533 x 634 keeps it whole. Nothing is dropped
   * and the overhang goes, which is the one part of the arrangement that cannot
   * survive: it is 138 px of a 945.8 px band, and 138 px of a shape that is now
   * 108 px wide would be most of it. */
  .ccg-lt-hero__float {
    left: 11.250%;   /* 216 / 1920 */
    top: 47.558%;    /* 449.8 / 945.8 */
    width: 27.760%;  /* 533 / 1920 */
    height: auto;
  }

  .ccg-lt-row { flex-wrap: wrap; row-gap: var(--ccg-narrow-row-gap); }
  .ccg-lt-col--8, .ccg-lt-col--6 { width: 100%; }
  .ccg-lt-col--4, .ccg-lt-col--3 { width: 50%; }

  /* THE FOUR CARDS ARE ONE LIST HERE AND THEY ARE SPACED ONCE. At 1920 they are
     two columns of two: 40 px between the two inside a column, and the first
     column dropped 64 px so the two columns stagger. Stacked, those are three
     different gaps in one vertical list - photographed at 390 on 09.09.2026,
     cards two and three touched with nothing between them while card four had 40.
     `--ccg-narrow-row-gap` is the value this file already gives a row that has
     fewer columns than it was composed for, and it is the same 24 the industries
     row above takes. The stagger goes with the second column it staggered
     against. */
  .ccg-lt-card + .ccg-lt-card { margin-top: var(--ccg-narrow-row-gap); }
  .ccg-lt-cards--drop { padding-top: 0; }



  /* The live page's empty one-column separator between the photograph and the
     text. It separates two things that are no longer side by side. */




  /* The two banded rows carry no column padding, because at 1920 a photograph in
     one of them runs to the edge of the window. Stacked, the photograph still
     does and the text may not: without this the prose starts at x = 0. */


  /* The hero's row is 762 tall because the photograph in it is. Stacked, that is
     762 px of empty column under a headline. */

  /* The two rows of three steps, which lose a column here like every other row.
     `flex: 1 1 0` floors each item at its own min-content, and in the large row
     that is a 144 px mark inside `padding: 0 40px` - so three of them need 672 px
     however narrow the row is. Measured at 641 before this rule: three steps at
     x 30, 254 and 478, each 224 wide, the third's title running to x = 662, which
     is 21 px past the window. `0 1 50%` is the flex form of the two-column grid
     the rows above take, and it puts the third step at half width under the first
     rather than stretching it across. */
  /* The wrap and the row gap are unconditional since
     `lay-out-the-middle-band-and-unstretch-the-graphics`; what is this band's own
     is the second half - two columns exactly, rather than as many as fit. */

  /* AND THE CONNECTOR BETWEEN THEM GOES WITH THE WRAP, WHICH IS A DEVIATION. It
     is a 95x8 and a 155x8 gradient bar drawn from `calc(50% + 46px)` of one step
     towards the next, so it is a horizontal line between two items that are no
     longer side by side - the argument the home page's `svg.ccg-network__lines`
     was dropped on, and the same shape of thing. Nothing that carries a word is
     dropped: the mark, the super-line and the title of all three steps are
     served.
     IT WAS DECLARED AT 640 UNTIL 09.09.2026 AND THE WRAP ABOVE IS AT 1024, so
     between those two bands the bar was served against a row it no longer
     described. Measured at 800 on /recruiting before this line moved: the three
     steps wrap 2 + 1, step 2's connector runs x 631..726 into the empty half of
     the row, and in the large row step 5's runs x 731..886 - 86 px PAST the
     800 px window. Neither carries text, so `case_outside` reports the route
     clean at every width; `case_width` reports it clean too, because
     `.ccg-rc__port` computes `overflow-x: clip`. Found by looking at the band
     the suite does not drive. */

  /* --- 7. the four photographs of /recruiting ------------------------------
   *
   * Every one of them is a fixed pixel box: 742x762, 860x875, 696x629, 860x884.
   * THE BOX IS RESHAPED AND THE PICTURE IS NOT CROPPED, which is the answer
   * `repair-the-mobile-home-page` gave the news teasers - there a 16:9 file in a
   * box of fixed height lost 21.3 per cent of its width to `cover`, and the
   * repair was to give the box the file's own shape rather than to change the
   * fitting. Here the figure takes the width it is given and keeps its ratio, so
   * nothing is cropped on either axis at any width.
   *
   * AND THE DECORATIONS COME WITH IT. Each is placed in pixels against the
   * figure's own 742 or 860, so at 330 they would land outside the picture they
   * decorate. Every value below is that pixel divided by the figure's - 457/742,
   * 492/762 - so the composition is the same one at every width. They are
   * measurements re-expressed, not new placements. */
  /* THE SHARES ARE NO LONGER DECLARED HERE. Every rule this block used to carry
     for these four figures - the percentage width, the aspect ratio, the image's
     share of its figure and the four sets of decorations - now stands
     unconditionally, because `lay-out-the-middle-band-and-unstretch-the-graphics`
     needed the same thing between 1025 and 1599. They were identical values, so
     nothing at this band changes; what is left here is the one thing that differs.

     THE CAPS COME OFF, and without this line they would not. Unconditionally each
     figure is `width: 100%` under a `max-width` of its own measured box, which is
     what stops it overflowing a narrower column. Stacked, the column IS the
     window, and a 1024 px window would put a 742 px hero photograph in it where
     the rule here has always stretched it to the full width. Re-measured at 1024
     with the cap left in place: the hero figure computed 742 x 762 against this
     block's 1024 x 1051.5. The cap is lifted so this band keeps the box it had. */

  /* THE THREE MARKS BESIDE `Recruiting neu gedacht` ARE DROPPED, AND THAT IS A
     DEVIATION. `.ccg-rc-neu__aside` is a nought-sized anchor at the text column's
     origin and its three decorations hang from -148 to -252 px to the left of it,
     in the rail's own left margin. At this band the text column IS the rail and
     there is no margin to hang in; all three would lie off the left edge of the
     window, which is where two of them already were before this rule. Same
     argument as the chapter's watermark above and the home page's
     `svg.ccg-network__lines` before it. */

  /* --- 5. the last four families' rows, the same rule as the seven above ----
   *
   * `lay-out-the-inner-families-for-a-phone` gave every horizontal row of the
   * inner families one answer - fewer columns here, one at 640, written as
   * `repeat(2, minmax(0, 1fr))` and `minmax(0, 1fr)` - and recorded why: a card
   * composed for a 346.7 px column is not a card at 100. These are the same rule
   * for the rows `/karriere`, `/about` and `/about/kontakt` carry, and the zero
   * minimum is here for the same reason it is there. `1fr` floors a track at the
   * item's own min-content, which is what put /karriere's last two cards outside
   * the window rather than merely narrow: re-measured at 390 on 10.09.2026
   * against HEAD's stylesheet served from the running container, the four tracks
   * computed 147.4 / 118.3 / 118.5 / 141.4 - each its own min-content and none
   * of them a share of anything - so the row measured 587.9 inside a 330 px rail
   * and ended at x 617.9. The third and the fourth card therefore stood at
   * x 337.3 and x 476.6, and their title and their paragraph laid at
   * x 360.7..432.4 and x 500..594.5, which are the four elements `case_outside`
   * reports on that route.
   *
   * THAT TABLE READ `4 tracks of 71.7` UNTIL THE GATE OF 10.09.2026, and 71.7
   * was never a track: it is the width of the third card's title, the first of
   * the four elements reported outside the window, which is what a track width
   * gets confused with. It could not have been one - four tracks of 71.7 with
   * three 20.8 px gaps measure 349.2 and end at x 379.2, inside the window, and
   * the same sentence reported elements at x 594.5. A `1fr` set that DID fit the
   * rail would compute (330 - 62.4) / 4 = 66.9. The replacement is the browser's
   * own `grid-template-columns` for that element, read back with the wide
   * reading beside it; the other row in this table was re-read the same way and
   * was already right.
   *
   * Measured at 390 on 10.09.2026 before this rule, with the wide reading beside
   * it:
   *
   *     .ccg-ka-grid          147.4 + 118.3 + 118.5 + 141.4   (4 x 284.4 at 1920)
   *     .ccg-ab-method-grid   3 tracks of  95.1               (385.1 at 1920)
   */
  .ccg-ka-grid,
  .ccg-ab-method-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* /about/kontakt's three next steps, a column at this band (OP#4715). Across
     the rail they are three tracks; at 390 the rail is 330 and three tracks
     would get at most 110 px each before any gap, beside a 36 px numeral. The
     column is the arrangement they had on every width beside the form, with the
     gap it had. Recorded as departure 10 of the narrow-band list. */
  .ccg-ko-steps {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ccg-ko-steps-gap);
  }
}

@media (max-width: 640px) {
  /* And the phone's value, on the element for the reason the lap band's twin
     states. At 390 the element is 133.3 px wide against 400 units, so 30 units
     paint 10.00 px against a floor of 9.28. */
  .ccg-ab-emblem__foot { font-size: var(--ccg-ab-emblem-foot-size-phone); }

  /* --- the type scale and the vertical rhythm ------------------------------
   *
   * REDECLARED AS TOKENS RATHER THAN AS RULES, which is what keeps this file
   * free of design values. Every rule that reads `--ccg-h1-punch-size` picks the
   * narrow value up without being touched, and a check can still read the whole
   * scale out of the served page's `:root` the way it reads the wide one.
   */
  :root {
    --ccg-h1-size: var(--ccg-narrow-h1-size);
    --ccg-h1-line: var(--ccg-narrow-h1-line);
    --ccg-h2-size: var(--ccg-narrow-h2-size);
    --ccg-h2-line: var(--ccg-narrow-h2-line);

    /* THE HERO'S FOOT STAYS AT THIS EDGE WHILE ITS TYPE MOVED UP TO 1024, and
       the two are one measurement apart. `.ccg-home-hero` takes
       `padding-bottom: calc(2 * this)`, and the wave at its foot is
       `height: var(--ccg-wave-hero)`, a share of the window - so the subline
       clears the wave only while the wave is shorter than twice this value.
       At 64 that is 128 px, which the share crosses at a 654 px window:
       measured on 2026-09-02 with this token in the lap band, the white subline
       lay over the wave's own white at 700 (ratio 1.07) and at 900 (1.07), and
       was clear at 641 and 390. At the wide value the same arithmetic clears
       every window down to 1307. So it belongs to the band where the wave is
       short, and that is this one. */
    --ccg-hero-bottom-space: var(--ccg-narrow-hero-bottom-space);

    /* The five section headings of the home page, one size between them. The
       reason is in ccg.yaml beside the value. */
    --ccg-warum-head-size: var(--ccg-narrow-head-size);
    --ccg-warum-head-line: var(--ccg-narrow-head-line);
    --ccg-warum-head-tracking: var(--ccg-narrow-head-tracking);
    --ccg-svc-head-size: var(--ccg-narrow-head-size);
    --ccg-svc-head-line: var(--ccg-narrow-head-line);
    --ccg-svc-head-tracking: var(--ccg-narrow-head-tracking);
    --ccg-branchen-head-size: var(--ccg-narrow-head-size);
    --ccg-branchen-head-line: var(--ccg-narrow-head-line);
    --ccg-branchen-head-tracking: var(--ccg-narrow-head-tracking);
    --ccg-zukunft-head-size: var(--ccg-narrow-head-size);
    --ccg-zukunft-head-line: var(--ccg-narrow-head-line);
    --ccg-zukunft-head-tracking: var(--ccg-narrow-head-tracking);
    --ccg-news-head-size: var(--ccg-narrow-head-size);
    --ccg-news-head-line: var(--ccg-narrow-head-line);
    --ccg-news-head-tracking: var(--ccg-narrow-head-tracking);

    --ccg-section-space: var(--ccg-narrow-section-space);
    --ccg-section-space-news: var(--ccg-narrow-section-space-news);
    /* THE THREE VALUES THE SERVICE CARD IS RE-LAID-OUT WITH, and the first of
       them is what the collision was. `--ccg-svc-card-space-top` is the padding
       that clears the photograph, which hangs at `top` and is `--ccg-svc-photo-
       size` tall. At the wide band those are 20.8 and 128, so 148.8 has to be
       cleared and 153.6 clears it. At this band the top space was 40 while the
       photograph kept its 128, and measured at 390 on 07.09.2026 on the 330 px
       card: photograph 2685.3..2813.3, number 2774.8..2798.8, title
       2807.6..2830.6 - the number inside the photograph's band and the title
       5.7 px under its lower edge.
       Here the three are one arithmetic: 28 above the photograph, 188 of
       photograph, 28 from the photograph to the number, and 28 + 188 + 28 = 244.
       The card is not short of room - at 390 it is 330 px wide against 223 on
       the desktop - so the photograph grows rather than the spacing shrinking.
       188 is the share the photograph has of the desktop card, 128 of 223,
       carried onto 330. */
    --ccg-svc-card-space-top: var(--ccg-narrow-svc-card-space-top);
    --ccg-svc-photo-size: var(--ccg-narrow-svc-photo-size);

    /* And the digit behind the card, which was sized against the 223 px card and
       is 176 px tall. AN EARLIER VERSION OF THIS COMMENT SAID 176 PUT THE DIGIT'S
       LOWER EDGE 70 PX BELOW THE CARD ON THE TALLER CARD THIS BLOCK MAKES. It does
       not: the overhang is `bottom: -70.4px` at line 1264, declared once and
       unconditional, so it is the same at every width and on a card of any height.
       The size decides only how much of the digit stands above the cut - 105.6 of
       176, 61.6 of 132 - and this value is here because a 176 px digit competes
       with the sub-line and the link on a 330 px card, not because it moves an
       edge it cannot reach. */
    --ccg-svc-watermark-size: var(--ccg-narrow-svc-watermark-size);
    --ccg-footer-spacer-height: var(--ccg-narrow-footer-spacer-height);

    --ccg-header-logo-height: var(--ccg-narrow-header-logo-height);
    --ccg-header-logo-sticky-height: var(--ccg-narrow-header-logo-sticky-height);
  }

  /* --- one column ---------------------------------------------------------- */
  .ccg-cards--3 { grid-template-columns: minmax(0, 1fr); }
  .ccg-services { grid-template-columns: minmax(0, 1fr); }
  .ccg-teasers  { grid-template-columns: minmax(0, 1fr); }
  .ccg-network  { grid-template-columns: minmax(0, 1fr); }
  .ccg-footer__column { flex: 1 1 100%; max-width: 100%; }

  /* The second layer of the careers padding - see the lap band's rule for the
     measurement. Here the wave is short enough that the fixed value clears it
     on its own (86 px of wave against an eyebrow 26 px below it at 390), and
     this is the width the design is being shown at, so it keeps the value it
     was drawn with. */
  .ccg-home-zukunft__main { padding-top: var(--ccg-narrow-zukunft-main-space); }

  /* --- the service card, laid out for the width it actually gets ------------
   *
   * Five arrangements were built as trial rules over the running page,
   * measured, photographed and shown; this is the one that was chosen, and the
   * table of the five is in
   * openspec/changes/repair-the-mobile-home-page/design.md.
   *
   * The photograph is centred with the margin and not with a transform, for the
   * reason the lap band's own rule gives: the hover rule sets `transform` on
   * this element and would replace a centring written as one. `top` is restated
   * here because the space above the photograph is one of the three values the
   * card is re-laid-out with above, and it is the same 28 the padding counts. */
  .ccg-service__photo {
    top: var(--ccg-narrow-svc-photo-space);
    left: 0;
    right: 0;
    margin-inline: auto;
  }

  /* THE PICTOGRAM IS DROPPED AT THIS BAND, and it is a deviation recorded in
     design.md rather than a simplification. It is the one part of the card that
     is not centred - `margin: 0 0 22.4px` against the card's own
     `text-align: center` - so beside a centred photograph it is the only thing
     left hanging on the card's left edge, and the arrangement the trial showed
     with it kept there was the one the choice rejected. */
  .ccg-service__icon { display: none; }

  /* The space between the sub-line and the call to action. The card carries
     `min-height: 360px`, which was sized for a five-column row; once the card is
     taller than that the 25.6 px this margin holds is no longer the last thing
     between them, because `.ccg-service__link` takes `margin-top: auto`. Measured
     at 390 before this rule: 88 px of nothing between the two. */
  .ccg-service__kicker { margin-bottom: var(--ccg-narrow-svc-kicker-space); }

  /* ==========================================================================
   * THE INNER FAMILIES AT THE PHONE BAND
   *
   * The band above carries everything whose arithmetic breaks before 641. What
   * is here needs the 330 px rail specifically: the type scale, the last column
   * of every row, and the two constructions that have to be rearranged rather
   * than narrowed - the comparison matrix and the roadmap.
   * ========================================================================== */

  /* --- the type scale, as tokens, exactly as the block above declares it ----
   *
   * ONE HEADING SIZE FOR THE WHOLE SITE AT THIS BAND, WHICH IS THE HOME PAGE'S
   * OWN DECISION EXTENDED RATHER THAN A SECOND ONE. `repair-the-mobile-home-page`
   * gave the home page's five section headings one size because "at 330px of
   * rail the difference between them is not legible as a hierarchy", and their
   * wide sizes were 40, 51.2 and 41.6 three times. `--ccg-sv-head-title-size` IS
   * 41.6 and `--ccg-sv-band-title-size` is 41.6: the same number arriving at the
   * same rail. `--ccg-rc-h2-size` is 44 and `--ccg-sv-san-title-size` 38.4, which
   * is the same span the home page's own five covered. So all of them take
   * `--ccg-narrow-head-size` and the site has one section heading at this band.
   *
   * AND THE HERO'S H1 TAKES THE HOME HERO'S. `--ccg-h1-setup-size` and
   * `--ccg-h1-punch-size` become 26 and 38 here, and the service hero and the
   * recruiting hero are the same construction - a label, a setup line, a punch
   * line and a sub-line over a photograph - at 35.2/57.6 and 64. Measured at 390
   * before this: `Strategischer Finanzpartner. Auf Zeit.` set at 57.6 px in a
   * 206.7 px column over eight lines, breaking `Strate/gischer/Finan/zpart/ner.`
   * mid-word twice. `Strategischer` at 38 px sets in 272 px, inside the 330 rail.
   *
   * WHY THIS IS AT 640 AND THE HOME HERO'S IS AT 1024. The home page moved its
   * hero scale up a band because `contrast.mjs` measured white type on white
   * photograph at 641 - the hero is the one construction there whose type was
   * measured against the rail rather than the window. These heroes do not have
   * that problem at 641, because rule 2 of the band above has already given the
   * text the whole rail there: 581 px at 641 against the 374 it had.
   *
   * BOTH OF /recruiting's SUB-LINES TAKE THE HERO'S NARROW VALUE, and the second
   * one is here because of what it looked like without it. `--ccg-rc-sub2-size`
   * is 23 px and it is the prose under every h2 on that page; with the heading at
   * 28 and the paragraph at 23 the two are one size apart and the hierarchy stops
   * reading - photographed at 390 on 09.09.2026, `Recruiting neu gedacht` at 28
   * over three paragraphs at 23, and in the Finden band a 23 px sub-line standing
   * over 16 px paragraphs of the same block. 17 is the one narrow sub-line value
   * this theme has. THE TOKEN IS NAMED FOR THE HERO AND IS USED OUTSIDE ONE, which
   * is said rather than tidied: a second token holding 17 px would be the copy
   * nobody updates, and that trade is the same one the heading size above makes. */
  /* THE SERVICE HERO'S SIX ARE DECLARED ON THE HERO AND NOT ON `:root`, WHICH
     IS THE ONE PLACE THIS BLOCK DEPARTS FROM THE MECHANISM ABOVE and it departs
     for the reason the section header gives: `/about` renders through
     `.ccg-sv-hero` and is out of this cycle's scope, and a token on `:root`
     reaches it. Everything else here stays on `:root`, because no element on
     /about reads any of it - measured 09.09.2026, that page's other seven
     sections carry `ccg-sv`, `ccg-sv__port` and `ccg-sv__wave` and no other
     `ccg-sv-*` class at all.
     WHAT THAT COSTS IS ONE SENTENCE OF design.md 2.2, which said the scale is
     redeclared "inside `:root` ... so a check can still read the whole scale out
     of the served page". Six of the twenty-two are now read off
     `.ccg-sv-hero:not(.ccg-ab-hero)` instead of off the root element. They are
     still custom properties on a served element rather than `font-size` on a
     rule, which is the part that mattered, and they still inherit to
     `.ccg-sv-hero__setup`, `__punch`, `__sub` and `__title` - which is what
     makes the tracking below reach a declaration it cannot outrank. */
  .ccg-sv-hero:not(.ccg-ab-hero) {
    --ccg-sv-hero-setup-size: var(--ccg-narrow-h1-setup-size);
    --ccg-sv-hero-punch-size: var(--ccg-narrow-h1-punch-size);
    --ccg-sv-hero-punch-line: var(--ccg-narrow-h1-punch-line);
    --ccg-sv-hero-sub-size: var(--ccg-narrow-hero-subline-size);
    --ccg-sv-hero-sub-line: var(--ccg-narrow-hero-subline-line);

    /* THE TRACKING GOES WITH THE SIZE, AND AS A TOKEN RATHER THAN AS A RULE -
       which is the second time this block had to be written that way. It was
       first written as `.ccg-sv-hero__title { letter-spacing: ... }` and measured
       afterwards: the punch line computed -1.44px still, because
       `.ccg-sv-hero__punch` sets the same property itself at the same specificity
       and later in the file. Redeclaring the token reaches both, which is the
       whole reason this file's scale is tokens.
       -1.44 is 2.5 per cent of the 57.6 it was composed against and 3.8 per cent
       of 38; -3 is 4.7 per cent of 64 and 7.9 per cent of 38.
       `--ccg-narrow-h1-punch-tracking` is the value the home page's own h1 takes
       at this band, and it is -0.9, which is 2.4 per cent of 38. */
    --ccg-sv-hero-h1-tracking: var(--ccg-narrow-h1-punch-tracking);
  }

  :root {
    /* /recruiting's h1 is not the service hero's element and is not reached by
       the rule above; its own hero carries no `.ccg-ab-*` twin, so it stays a
       root token. */

    --ccg-sv-head-title-size: var(--ccg-narrow-head-size);
    --ccg-sv-head-title-line: var(--ccg-narrow-head-line);
    --ccg-sv-head-title-tracking: var(--ccg-narrow-head-tracking);
    --ccg-sv-band-title-size: var(--ccg-narrow-head-size);
    --ccg-sv-band-title-line: var(--ccg-narrow-head-line);
    --ccg-sv-san-title-size: var(--ccg-narrow-head-size);
    --ccg-sv-san-title-line: var(--ccg-narrow-head-line);


    --ccg-sv-chapter-pad: var(--ccg-narrow-sv-chapter-pad);
    --ccg-sv-mtx-card-padding: var(--ccg-narrow-sv-mtx-card-padding);
    --ccg-sv-mtx-cell-min: var(--ccg-narrow-sv-mtx-cell-min);
  }


  /* And the setup line gets a leading. `.ccg-sv-hero__setup` declares
     `line-height: 1`, which never shows at the wide band because the line fits:
     measured at 1920 the element is 35.2 px tall over 35.2 px of type, one line.
     At 390 it wraps - `Mehr als ein Business-Plan.` sets in two - and 1.0 stands
     them 26 px apart with nothing between the descender of one and the cap of the
     next. `--ccg-narrow-h1-setup-line` is the leading the narrow scale carries for
     this size and is what the home page's own setup line takes here. */
  .ccg-sv-hero:not(.ccg-ab-hero) .ccg-sv-hero__setup { line-height: var(--ccg-narrow-h1-setup-line); }

  /* --- /about's hero keeps its punch line in two lines ----------------------
   *
   * AND IT IS THE ONE HERO THIS BAND HAD LEFT ALONE. Every rule above carries
   * `:not(.ccg-ab-hero)`, so /about's hero keeps the wide two-column row at
   * every width: measured 16.09.2026 on a container from this tree, its
   * `.ccg-sv__col--8` is 248.66 px wide at a 393 px window with 20 px of padding
   * on each side, leaving 208.66 for the text.
   *
   * WHAT THAT COSTS, READ RATHER THAN ARGUED. `hero_punch` is
   * `Wir leben Um-set-zung.` with two soft hyphens, and at 57.6 px the run
   * `Umset-` measures 221.8 px - 194.2 for `Umset` plus 27.6 for the hyphen the
   * break paints. 221.8 does not fit 208.66, so the browser takes the earlier
   * soft hyphen and the word sets in THREE lines, `Um-` / `set-` / `zung.`, for
   * a punch line of five lines in all. The readings, on this build:
   *
   *     393   column 248.66   text 208.66   3 lines of `Umsetzung.`  (5 in all)
   *     402   column 254.66   text 214.66   3 lines                  (5 in all)
   *     430   column 273.33   text 233.33   2 lines                  (4 in all)
   *     440   column 280      text 240      2 lines                  (4 in all)
   *
   * So the threshold sits between 402 and 430, which puts the two smallest
   * iPhones - 393 and 402 - on the wrong side of it and the two largest on the
   * right side. Three renderings were put to the client and this one was chosen.
   *
   * THE RIGHT PADDING AND NOT THE LEFT, AND NOT THE TYPE SIZE. The left padding
   * is what stands the text off the rail's edge and is shared with the label,
   * the sub-line and the pill, so it stays. The right padding separates this
   * column from `.ccg-sv__col--4`, and that column carries 20 px of left padding
   * of its own, so the gap between the two is 20 either way. 208.66 + 20 is
   * 228.66, which carries 221.8 with 6.86 px to spare.
   *
   * IT IS SCOPED TO THIS SECTION AND THE FIVE SERVICE PAGES SHARE THE CLASS.
   * `.ccg-sv__col--8` is emitted by `partials/inner/hero.html.twig` as well, on
   * /single-service, /lohnabrechnung, /externer-cfo, /strategieberatung and
   * /existenzgruender-beratung. `.ccg-ab-hero` is on /about's section alone, so
   * those five are not reached: measured before and after, all five read
   * x 10, width 373, padding 20px / 20px at 393 and x 340, width 826.66,
   * padding 20px / 20px at 1920. */
  .ccg-ab-hero .ccg-sv__col--8 { padding-right: 0; }

  /* THE HERO'S GRAPHIC BLED TO THE WINDOW FROM HERE UNTIL 09.09.2026 and the
     rule now sits in the 1024 block, at rule 2a, with the 641 measurement that
     moved it. What is kept here is the reading that put it here in the first
     place, because it is still true and is still the worst single crop this
     change removes: at 390 rule 2 gives the graphic the whole rail - 330 - and
     330 x 1.6 is 528 in a 390 px window, 69 px off each side, cut by
     `.ccg-sv`'s own `overflow: hidden` and photographed with the outermost orbit
     ring sliced on both sides. What was wrong was not the repair but its band:
     the same arithmetic runs from 1024 down, and at 641 it read 144.3 px each
     side rather than 69. */

  /* --- one column ----------------------------------------------------------
   *
   * The last step of the row rule in the band above, and it is written exactly
   * as `.ccg-cards--3` and `.ccg-services` are written twenty lines up. */
  .ccg-sv-feature-grid,
  .ccg-sv-cons__grid,
  .ccg-sv-cases,
  .ccg-sv-san__grid,
  .ccg-sv-dash { grid-template-columns: minmax(0, 1fr); }

  .ccg-lt-col--4, .ccg-lt-col--3 { width: 100%; }

  /* The chart's own head and foot, which are two-item rows inside the chart card.
     `.ccg-sv-dash__meta` carries `white-space: nowrap`, so beside it the title got
     whatever was left: measured at 390 before this rule the title box was 43.3 px
     wide against 15.2 px of type, `Lohn-/Auf-/wand:/In-/Hou-/se vs./mit/CCG` over
     eight lines. Stacked, each takes the card's width. */
  .ccg-sv-dash__head,
  .ccg-sv-dash__foot {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ccg-sv-dash-head-gap);
  }

  /* The three steps of /recruiting, in both their sizes. `flex: 1 1 0` on a row
     of three gives each 110 px at 390; the large row's own `padding: 0 40px`
     leaves 30 of that for a 17 px title. */

  /* THE CONNECTOR BETWEEN THE STEPS IS DROPPED FROM 1024 DOWN, at the rule
     beside the wrap that orphans it. It was declared here until 09.09.2026 and
     that was a band too low: the wrap is at 1024 and the connector was served
     from 1024 to 641, pointing at nothing. The measurement is with the rule. */

  /* --- the comparison matrix, rearranged rather than narrowed --------------
   *
   * `minmax(170px, 1.5fr) repeat(4, 1fr)` cannot be made to fit: the 170 is the
   * function name's own column and the four beside it are the roles being
   * compared. Measured at 390 the grid computed 170 + 53 + 55.2 + 50.9 + 55 in
   * 257.6 px of card, so `Festangest. CFO` sat at x 344.3..395.2 and the CCG
   * column at 395.2..450.2 - two of the four roles wholly outside the window,
   * which is a comparison a visitor cannot make.
   *
   * SO THE LABEL TAKES A LINE OF ITS OWN AND THE FOUR ROLES KEEP THEIR COLUMNS.
   * The forty-five cells are flat siblings in one grid and the label of each row
   * is its first cell, so `grid-column: 1 / -1` on that one cell is the whole
   * rearrangement: the label spans the four tracks and the four values fall under
   * it, in the same order, under the same four headings. Nothing is dropped, no
   * cell moves in the document, and the head row - which is a label cell followed
   * by four role names - gets the same treatment for free.
   *
   * WHY NOT A ROW THAT SCROLLS SIDEWAYS. It would satisfy every check this
   * repository has, including the one this change adds, only because the check
   * would have to be written to ignore it; and a comparison whose columns cannot
   * be seen at once is not a comparison. This keeps all four under one another.
   *
   * The rule under the label is dropped and the rule under the values kept, so
   * each function is one band rather than two. */
  .ccg-sv-mtx__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  .ccg-sv-mtx__cell--label {
    grid-column: 1 / -1;
    border-bottom: none;
    padding-bottom: 0;
  }

  .ccg-sv-mtx__cell--head.ccg-sv-mtx__cell--label { border-bottom: none; }

  /* The rule that clears the border under the last row needs nothing here and a
     copy of it was written and removed. `.ccg-sv-mtx__grid > :nth-last-child(-n+5)`
     is the last five cells, which is exactly one function - its label and its four
     values - at both arrangements, because the cell count per row did not change:
     what changed is which track the label occupies. */

  /* --- the roadmap, turned on its side -------------------------------------
   *
   * Six phases on a drawn horizontal track. Three across still gives 98 px a
   * phase at 390 - measured before this rule, phases 04, 05 and 06 lay at
   * x 414.6..686.6, entirely outside the window - and one across leaves the track
   * running left to right across a column that runs top to bottom.
   *
   * SO THE PHASE BECOMES A ROW AND THE TRACK BECOMES A COLUMN. Dot on the left,
   * card beside it, and the track down the line the dots sit on. That is the same
   * roadmap: same six phases, same numbers, same order, same drawn line arriving
   * the same way.
   *
   * THE ANIMATION IS KEPT AND ONLY ITS AXIS CHANGES, which is the device
   * `ccgNarrowNodeIn` and `ccgNarrowHubFloat` were built with at the top of this
   * section: a narrow twin of the keyframe, and `animation-name` alone overridden
   * so the duration, the easing and the delay stay the token's. `rmLineDraw` ends
   * on `scaleX(1)` from a `transform-origin: left center`; this one ends on
   * `scaleY(1)` from the top. Nothing about the timing is restated here, which is
   * what keeps `test/motion.sh`'s reading of the token the only reading there is.
   *
   * AND THE TRACK IS DRAWN BY THE PHASES AND NOT BY ONE ABSOLUTE BOX, WHICH IS
   * THE SECOND VERSION OF THIS RULE. The first one kept `.ccg-sv-road__track` and
   * turned it on its side - `top: var(--ccg-sv-road-track-top); bottom: 0` in
   * `.ccg-sv-road`. `bottom: 0` is the section's foot and not the last dot, and
   * the wide track it was copied from is symmetric: `left` and `right` are both
   * `--ccg-sv-road-track-inset` (8.3333%), which is half of one of six columns,
   * so the wide line runs from the FIRST dot's centre to the LAST dot's centre
   * and stops. Measured at 390 on /existenzgruender-beratung with that rule in
   * force: the track ran y 3738.7..4745.8 while the sixth dot's centre was at
   * 4601.9 and its lower edge at 4628.9 - 143.9 px past the centre, 116.9 px
   * past the dot, ending in the left gutter beside the last card at nothing.
   * (A review lens read that overhang as about 126 px; 143.9 and 116.9 are the
   * two numbers the boxes give, depending on which edge of the dot is meant.)
   *
   * THERE IS NO CONSTANT THAT FIXES IT, which is why the element changes rather
   * than its offsets. The distance from the last dot's centre to the foot of the
   * section is the last phase's own height less half a dot, and a phase is as
   * tall as its card's prose; any pixel or percentage written here would be read
   * off one card at one width. So each phase draws the segment it owns - from
   * its own dot's centre, across its own height, over the grid's gap, to the
   * next dot's centre - and the last phase draws none. That is exact at every
   * height and every content length, and it is the same six phases on the same
   * one line.
   *
   * `left` IS HALF THE DOT, so the line passes through the centre of a dot that
   * is now the row's first item rather than the column's; `top` is half the dot
   * again, which is that dot's centre measured from the phase's own top, because
   * the phase is `align-items: flex-start` and the dot is its first item.
   *
   * THE MOTION IS THE TOKEN'S AND THE DRAW IS NOW SIX SEGMENTS RATHER THAN ONE
   * SWEEP, WHICH IS DECLARED AS A DEVIATION rather than passed off as the same
   * animation. `--ccg-sv-road-line-motion` is read whole, so the duration, the
   * easing and the delay are unchanged and `test/motion.sh`'s reading of the
   * token stays the only reading there is; `animation-name` alone is overridden
   * to `rmLineDrawY`, the way `ccgNarrowNodeIn` and `ccgNarrowHubFloat` are
   * handled at the top of this section. What differs from the wide band is that
   * six segments grow downward together over the token's 2.4s instead of one bar
   * growing across, because six absolutely-positioned boxes cannot share one
   * `scaleY`. Nothing is dropped, nothing is added, and the line arrives in the
   * same 2.4s. */
  .ccg-sv-road__phases {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ccg-narrow-row-gap);
  }

  .ccg-sv-road__phase {
    flex-direction: row;
    align-items: flex-start;
    text-align: left;
    gap: var(--ccg-narrow-row-gap);
    position: relative;
  }

  .ccg-sv-road__dot-wrap { flex: none; margin-bottom: 0; }
  .ccg-sv-road__card { flex: 1 1 auto; }

  /* The one box that was the whole track. It is a horizontal bar with a
     horizontal draw and there is no row for it to lie along here. */
  .ccg-sv-road__track { display: none; }

  .ccg-sv-road__phase::before {
    content: "";
    position: absolute;
    top: calc(var(--ccg-sv-road-dot-size) / 2);
    bottom: calc(-1 * (var(--ccg-narrow-row-gap) + var(--ccg-sv-road-dot-size) / 2));
    left: calc(var(--ccg-sv-road-dot-size) / 2);
    /* Centred on the dot with a margin and not with `translateX(-50%)`, because
       `rmLineDrawY` ends on a bare `transform: scaleY(1)`: a translate written
       into the same property would be dropped at the end of the draw and the
       line would step half its width sideways on the last frame. */
    margin-left: calc(-0.5 * var(--ccg-sv-road-track-height));
    width: var(--ccg-sv-road-track-height);
    border-radius: 2px;
    background: var(--ccg-sv-road-track-fill);
    transform: scaleY(0);
    transform-origin: center top;
    animation: var(--ccg-sv-road-line-motion);
    animation-name: rmLineDrawY;
    pointer-events: none;
    z-index: -1;
  }

  /* The sixth phase owns no interval, and that is what ends the line on the
     last dot instead of 143.9 px below it. */
  .ccg-sv-road__phase:last-child::before { content: none; }

  /* --- the last four families' type scale ----------------------------------
   *
   * NO VALUE IS CHOSEN HERE. Every one is a token this theme already carries for
   * the phone band, applied to a construction that is the same construction -
   * which is the mechanism `repair-the-mobile-home-page` established and
   * `lay-out-the-inner-families-for-a-phone` extended to the seven inner routes:
   * the scale is redeclared as tokens inside `:root`, never as `font-size` on a
   * rule, so a check can still read the whole scale out of the served page.
   *
   * | what                        | wide | narrow                            |
   * |-----------------------------|------|-----------------------------------|
   * | --ccg-ka-band-title-size    | 48   | narrow-h1-punch-size (38)         |
   * | --ccg-ko-hero-title-size    | 60.8 | narrow-h1-punch-size (38)         |
   * | --ccg-bl-head-title-size    | 64   | narrow-h1-punch-size (38)         |
   * | --ccg-bl-post-title-size    | 44   | narrow-h1-size (32)               |
   * | --ccg-ka-head-title-size    | 36.8 | narrow-head-size (28)             |
   * | --ccg-ka-cta-title-size     | 33.6 | narrow-head-size (28)             |
   * | --ccg-ab-h2-size            | 54.4 | narrow-head-size (28)             |
   * | --ccg-ab-spec-title-size    | 48   | narrow-head-size (28)             |
   * | --ccg-ab-close-title-size   | 64   | narrow-head-size (28)             |
   * | --ccg-bl-card-title-size    | 34   | narrow-h2-size (26)               |
   * | --ccg-bl-head-sub-size      | 24   | narrow-hero-subline-size (17)     |
   *
   * THE FOUR ROWS THAT TAKE THE PUNCH SIZE ARE FOUR HERO H1s over a coloured
   * band or a photograph, which is the construction `--ccg-sv-hero-punch-size`
   * and `--ccg-rc-h1-size` already take it for. The one that reads worst without
   * it is `/blog`: measured at 390 on 10.09.2026, `Aktuelle Fachartikel und
   * Neuigkeiten` set at 64 px in a 330 px rail as `Fachartik / el und /
   * Neuigkeit / en` - two words broken mid-word with no hyphen. At 38 it sets as
   * `Aktuelle / Fachartikel und / Neuigkeiten`.
   *
   * AN ARTICLE'S OWN H1 TAKES `--ccg-narrow-h1-size` AND NOT THE PUNCH SIZE, and
   * the difference is what the element is. `.ccg-post__title` is the document's
   * heading in a white body, not a punch line over a photograph, and 32/36 is
   * the scale this band already declares for `h1` twenty lines above. At 44 the
   * longest of the seventeen titles - `Die Zukunft ist jetzt: Wie die digitale
   * Buchhaltung die Automobilbranche revolutioniert` - broke `Automo-bilbranche`
   * across two lines at 390.
   *
   * THE SEVEN ROWS THAT TAKE `narrow-head-size` ARE SECTION HEADINGS, and this
   * band has had one size for a section heading since
   * `repair-the-mobile-home-page` gave the home page's five one, with the reason
   * recorded there: at 330 px of rail the difference between them is not legible
   * as a hierarchy. Their wide sizes here are 33.6, 36.8, 41.6, 48, 54.4 and 64,
   * which is the same spread that argument was made about.
   *
   * `--ccg-bl-card-title-size` TAKES THE BAND'S OWN H2 AND NOT THE HEADING SIZE.
   * The element is an `<h2>` inside a list item rather than a section's heading,
   * and 34 would otherwise leave a card's title larger than the h1 of the
   * article it links to - 34 against 32 - on the same site at the same width.
   *
   * THE TRACKINGS GO WITH THE SIZES ON NINE OF THE TWELVE ROWS, for the reason
   * `lay-out-the-inner-families-for-a-phone` had to write twice: a tracking
   * composed against 48 or 64 px of type is a different fraction of 38, and
   * redeclaring the token reaches every rule that reads it.
   *
   * THE OTHER THREE ROWS ARE TWO BLOG TITLES AND ONE ROW WITH NO SUCH TOKEN, and
   * until the gate of 10.09.2026 this paragraph claimed the whole table.
   * `--ccg-bl-head-sub-size` is the third: `ccg.yaml` declares `bl-head-sub-size`
   * and `bl-head-sub-line` and no tracking beside them, so that row has none to
   * carry or to move. `--ccg-bl-card-title-tracking`
   * and `--ccg-bl-post-title-tracking` are both `-1px` in `ccg.yaml` - composed
   * against 34 and 44 - and neither is redeclared in the `:root` below, so both
   * carry into 26 and 32. The interval is what separates them from the nine:
   * those drop by 20 to 36 px and carry wide trackings of -0.736 to -2.56 px,
   * which is the spread the sentence above is about, while these two drop by 8
   * and by 12 and both start from the same -1. WHAT IS ASSERTED IS THE COUNT AND
   * NOT THE JUDGEMENT: nine rows redeclare a tracking, two keep theirs and one
   * has none. Whether -1px reads as well at 26 and 32 as at 34 and 44 was not
   * measured, so it is not claimed - the two rows are named here so that a
   * reader can look at them rather than take a blanket sentence for a sweep.
   *
   * THE LINE COMPANIONS ARE NOT A SECOND DECISION. Each takes the narrow scale's
   * own leading for the size beside it, by the same argument as its size.
   */
  :root {
    --ccg-ka-band-title-size: var(--ccg-narrow-h1-punch-size);
    --ccg-ka-band-title-line: var(--ccg-narrow-h1-punch-line);
    --ccg-ka-band-title-tracking: var(--ccg-narrow-h1-punch-tracking);
    --ccg-ka-head-title-size: var(--ccg-narrow-head-size);
    --ccg-ka-head-title-line: var(--ccg-narrow-head-line);
    --ccg-ka-head-title-tracking: var(--ccg-narrow-head-tracking);
    --ccg-ka-cta-title-size: var(--ccg-narrow-head-size);
    --ccg-ka-cta-title-line: var(--ccg-narrow-head-line);
    --ccg-ka-cta-title-tracking: var(--ccg-narrow-head-tracking);

    /* `--ccg-ab-h2-size` is the h2 of three of /about's eight sections - counted
       on 10.09.2026 with `grep -n 'ccg-ab-h2-size' css/ccg.css`, which finds
       exactly three consumers: `.ccg-ab-tl__title`, `.ccg-ab-mission__title` and
       `.ccg-ab-scene__title`, the timeline's, the mission's and the methods'
       headline. It read `five` until the gate of that day, naming the quote and
       the closing block with them; neither reads it. The quote section renders
       no heading element at all - `about-quote.html.twig` serves a `blockquote`
       between a mark and an attribution - and the closing block's
       `<h2 class="ccg-ab-close__title">` reads `--ccg-ab-close-title-size`,
       which is its own row three lines below. The conclusion the sentence draws
       is unchanged, because it is about which rules the token reaches: it does
       NOT reach that page's hero,
       which renders `.ccg-sv-hero__punch` off `--ccg-sv-hero-punch-size` and is
       excluded from this band by `:not(.ccg-ab-hero)` above. Measured at 390
       with these rules in place: the hero's setup is 35.2 px in a 206.7 px
       column, its punch 57.6 px and its label at y 16.7 - identical to the same
       page under HEAD, which is what the exclusion is for. */
    --ccg-ab-h2-size: var(--ccg-narrow-head-size);
    --ccg-ab-h2-line: var(--ccg-narrow-head-line);
    --ccg-ab-h2-tracking: var(--ccg-narrow-head-tracking);
    --ccg-ab-spec-title-size: var(--ccg-narrow-head-size);
    --ccg-ab-spec-title-line: var(--ccg-narrow-head-line);
    --ccg-ab-spec-title-tracking: var(--ccg-narrow-head-tracking);
    --ccg-ab-close-title-size: var(--ccg-narrow-head-size);
    --ccg-ab-close-title-line: var(--ccg-narrow-head-line);
    --ccg-ab-close-title-tracking: var(--ccg-narrow-head-tracking);

    --ccg-ko-hero-title-size: var(--ccg-narrow-h1-punch-size);
    --ccg-ko-hero-title-line: var(--ccg-narrow-h1-punch-line);
    --ccg-ko-hero-title-tracking: var(--ccg-narrow-h1-punch-tracking);

    --ccg-bl-head-title-size: var(--ccg-narrow-h1-punch-size);
    --ccg-bl-head-title-line: var(--ccg-narrow-h1-punch-line);
    --ccg-bl-head-title-tracking: var(--ccg-narrow-h1-punch-tracking);
    --ccg-bl-head-sub-size: var(--ccg-narrow-hero-subline-size);
    --ccg-bl-head-sub-line: var(--ccg-narrow-hero-subline-line);
    --ccg-bl-card-title-size: var(--ccg-narrow-h2-size);
    --ccg-bl-card-title-line: var(--ccg-narrow-h2-line);
    --ccg-bl-post-title-size: var(--ccg-narrow-h1-size);
    --ccg-bl-post-title-line: var(--ccg-narrow-h1-line);
  }

  /* --- the legal pages' heading ladder, which this band inverted ------------
   *
   * FIVE RULES AND NOT FIVE TOKENS, WHICH IS THE ONE PLACE THIS BLOCK DEPARTS
   * FROM ITS OWN MECHANISM. Everything above is a token redeclaration, because
   * every wide value it overrides is already a token. These five are not:
   * `.ccg-rt-text__body h2` reads the shared `--ccg-h2-size`, h5 and h6 read
   * `--ccg-bl-post-h5-size` and `--ccg-bl-post-h6-size`, and h3 and h4 are
   * literals in this file marked `chosen, not measured`. Redeclaring
   * `--ccg-h2-size` would move the home page's h2 with them, and inventing a
   * `--ccg-rt-h2-size` whose wide value is a copy of `--ccg-h2-size` would be
   * the copy nobody updates. The VALUES are still tokens, so the ladder is
   * readable off `:root` on the served page; what is not readable off `:root` is
   * that these five rules are the ones applying it.
   *
   * WHAT WAS WRONG. The wide ladder is 36 / 32 / 28 / 24 / 20 and descends. At
   * this band `--ccg-h2-size` becomes `--ccg-narrow-h2-size` (26) and the other
   * four do not move, so it reads 26 / 32 / 28 / 24 / 20 - the outline's top
   * level smaller than the two under it. Measured at 390 on 10.09.2026 on
   * /cookie-richtlinie-eu, the one of the four legal pages that runs h2 down to
   * h5: `1. Einführung` 26 px over `5.1 Technische oder funktionelle Cookies`
   * 32 px over `Verwendung` 28 px. The same three at 1920 are 36 / 32 / 28.
   *
   * Where the five narrow values come from is argued in ccg.yaml beside them. */
  .ccg-rt-text__body h2 {
    font-size: var(--ccg-narrow-rt-h2-size);
    line-height: var(--ccg-narrow-rt-h2-line);
  }

  .ccg-rt-text__body h3 {
    font-size: var(--ccg-narrow-rt-h3-size);
    line-height: var(--ccg-narrow-rt-h3-line);
  }

  .ccg-rt-text__body h4 {
    font-size: var(--ccg-narrow-rt-h4-size);
    line-height: var(--ccg-narrow-rt-h4-line);
  }

  .ccg-rt-text__body h5 {
    font-size: var(--ccg-narrow-rt-h5-size);
    line-height: var(--ccg-narrow-rt-h5-line);
  }

  .ccg-rt-text__body h6 {
    font-size: var(--ccg-narrow-rt-h6-size);
    line-height: var(--ccg-narrow-rt-h6-line);
  }

  /* --- the last four families' rows, one column ----------------------------
   *
   * The 640 half of rule 5 in the block above. Measured at 390 on 10.09.2026,
   * with the wide reading beside each:
   *
   *     .ccg-ka-grid           147.4 + 118.3 + 118.5 + 141.4  (4 x 284.4 at 1920)
   *     .ccg-ab-method-grid    3 tracks of  95.1              (385.1 at 1920)
   *     .ccg-ko-wege__grid     2 tracks of 153.8              (478.8 at 1920)
   *     .ccg-ko-contact__grid  106.7 + 175.3                  (658.3 + 493.7 at 1920)
   *
   * THE FOURTH ROW IS GONE (OP#4715): the form it set beside the three steps was
   * removed, and its two paragraphs below are kept as the reading that put it
   * here rather than as a rule this file still carries.
   *
   * THE FIRST ROW READ `4 tracks of 71.7` UNTIL THE GATE OF 10.09.2026 and the
   * argument for the correction is at the same table in the 1024 block above.
   * The other three rows each close on the rail exactly with their own gaps -
   * 3 x 95.1 + 2 x 22.4, 2 x 153.8 + 22.4, and 106.7 + 175.3 + 48, all 330 - and
   * all four were re-read off the browser on the same day against the same
   * stylesheet.
   *
   * THE THIRD OF THOSE IS THE DEFECT THIS CHANGE WAS SCOPED BY and no check can
   * see it: nothing on `/about/kontakt` lies outside the window and
   * `scrollWidth` answers 390, while `info@consulting-concept.de` sets in a
   * 153.8 px card as `info@co / nsulting- / concept. / de`.
   *
   * THE FOURTH IS WORSE AND WAS NOT IN THE PROPOSAL. `.ccg-ko-contact__grid` is
   * `1.2fr 0.9fr` - the form and the three steps beside it - and at 390 the form
   * card computed 106.7 px wide. Photographed the same day: `Erzählen Sie uns
   * von Ihrem Vorhaben.` set one letter to a line down 900 px of column, the
   * paragraph under it set `kei / n` and `son / der / n`, and every input was
   * about 30 px wide with the submit button overhanging the card.
   */
  .ccg-ka-grid,
  .ccg-ab-method-grid,
  .ccg-ko-wege__grid { grid-template-columns: minmax(0, 1fr); }

  /* /about's mission body is two text columns rather than a grid, and it is the
     same defect: 386 px each at 1920 and 141 px each at 390. One column. */
  .ccg-ab-mission__body { column-count: 1; }

  /* --- /about's timeline becomes a column with its rail down one side -------
   *
   * REARRANGED RATHER THAN NARROWED, which is what the roadmap's six phases got
   * in the previous change and for the same reason: the rail runs down the
   * middle of a track whose two halves are the two sides of an alternation, and
   * at this width neither half is a column anything can be set in. Measured at
   * 390 on 10.09.2026, `.ccg-ab-tl__card` is 125 px wide against 460 at 1920,
   * and the five headlines set `Wachstum` with its full stop alone on the next
   * line and `Fünf Disziplinen` with `, ein Team.` alone on the next.
   *
   * Same five entries in the same order, the same years, the same markers on the
   * same rail, the same 0.15 s staggered arrival and the same pulsing halo on
   * the last one. What changes is that the rail stands at the left edge of the
   * list rather than at its centre and every card sits to the right of it.
   *
   * EVERY NUMBER HERE IS ARITHMETIC OVER TWO TOKENS THIS FILE ALREADY CARRIES.
   * The marker is `--ccg-ab-tl-marker-size` (38) centred on the rail, so half of
   * it is where the rail has to stand for the marker to touch the list's left
   * edge and no more; and the card keeps `--ccg-ab-tl-card-inset` (40) of
   * clearance from the rail, which is exactly the clearance it keeps from the
   * centre line at 1920. 19 + 40 = 59 of a 330 px list, so the card is 271.
   *
   * BOTH KEEP THEIR `translateX(-50%)`, AND THE RULE THAT DROPPED IT ON ONE OF
   * THEM PUT THE RAIL BESIDE THE MARKERS' CENTRE RATHER THAN THROUGH IT. The
   * base rules give the rail `left: 50%; transform: translateX(-50%)` and the
   * marker `left: 50%; transform: translate(-50%, -50%)`, so at 1920 the two are
   * concentric. This block moved both to `left: 19px`; while it also wrote
   * `transform: none` on the rail, the marker kept its own translate and the two
   * no longer shared a centre. Measured at 390 on 10.09.2026 with that rule in
   * place: the list stands at x 30, the five markers each at x 30..68 with their
   * centre at x 49, and the 3 px rail at x 49..52 with its centre at x 50.5 -
   * off by 1.5 px, which is half the rail's own width and is visible at the
   * discs, the rail meeting them right of centre rather than through it. Without
   * the override the rail is at x 47.5..50.5, centre 49, which is the markers'
   * centre exactly. The gate found this by reading the two rules against each
   * other; the pictures are at `.ccg-screens/2026-09-10-op4270/gate/`. */
  .ccg-ab-tl__list::before {
    left: calc(var(--ccg-ab-tl-marker-size) / 2);
  }

  .ccg-ab-tl__marker { left: calc(var(--ccg-ab-tl-marker-size) / 2); }

  /* 110 was the height a card of half the track needed; a card of the whole of
     it is as tall as its own prose. */
  .ccg-ab-tl__entry { min-height: 0; }

  .ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__card,
  .ccg-ab-tl__entry:nth-child(even) .ccg-ab-tl__card {
    width: auto;
    flex: 1 1 auto;
    margin-left: calc(var(--ccg-ab-tl-marker-size) / 2 + var(--ccg-ab-tl-card-inset));
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
    text-align: left;
  }

  /* The year and the badge are inline-blocks in a column flex and have to be
     told which end to sit at. On an alternating track that end was the one the
     card was on; here every card is on the same side. */
  .ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__year,
  .ccg-ab-tl__entry:nth-child(odd) .ccg-ab-tl__badge { align-self: flex-start; }

  /* The decoration hangs 4 per cent in from the side the card is NOT on, which
     alternates with the card. With every card on the right, an even entry's
     decoration is behind the rail and the marker rather than behind the prose it
     belongs to. One side for all five, and it is the card's. Nothing is dropped:
     all five 240 x 240 graphics are still laid out and still animate. */
  .ccg-ab-tl__entry:nth-child(even) .ccg-ab-tl__decor {
    left: auto;
    right: var(--ccg-ab-tl-decor-inset);
  }

  /* --- /about's compass rose stays inside the window -----------------------
   *
   * THE ONE ELEMENT `case_outside` REPORTS ON THIS ROUTE. `.ccg-ab-mission__
   * compass` is 640 x 640 hung at `right: -120px` of the mission block, so 120
   * px of it lies past that block's right edge. At 1920 the block is 1100 wide
   * inside a 1920 px window and the rose runs x 990..1630, well inside it. At
   * 390 the block is 330 and the same declaration puts the rose at x -160..480 -
   * off BOTH edges - with the `W` of its rose, an `svg > g > text`, at
   * x -70.2..-32.2. That letter is what the check reports and it is the only
   * element outside the window on this page.
   *
   * THE ROSE IS SIZED TO THE BLOCK RATHER THAN SHRUNK BY A FACTOR, so there is
   * no number here: it is as wide as the mission block, square, and anchored to
   * that block's right edge, which is where the wide composition anchors it.
   * At 390 that is x 30..360. The composition changes and it is a departure: at
   * 1920 the rose enters from the right and is cut by the section's own
   * `overflow: hidden`; here the whole of it is visible behind the prose at the
   * same 12 per cent opacity. The alternative was to drop it, and it carries the
   * four cardinal letters. */
  .ccg-ab-mission__compass {
    right: 0;
    width: 100%;
    height: auto;
    aspect-ratio: 1;
  }

  .ccg-ab-mission__compass svg { height: auto; aspect-ratio: 1; }

  /* --- /about's spectrum is not widened to the window here any more ---------
   *
   * A RULE WAS REMOVED HERE AND ITS REMOVAL IS A DEPARTURE, recorded in
   * design.md. It read
   *
   *     .ccg-ab-spec__svg { width: calc(100% + 2 * var(--ccg-measure-gutter));
   *                         margin-inline: calc(-1 * var(--ccg-measure-gutter)); }
   *
   * and it gave the wide field the window instead of the rail, on the argument
   * that a graphic in a box it does not fit gets a bigger box. Its own comment
   * said what the bigger box did not buy: the twenty labels went from about 4.7
   * and 6.0 px to about 4.7 and 7.1, which is still below anything readable.
   *
   * Since `lay-out-the-spectrum-for-a-phone` the element that rule names is not
   * drawn at this band at all - `.ccg-ab-spec__svg--wide` is `display: none`
   * from 1024 down - and the narrow drawing beside it was out of its reach too,
   * which is the honest reason for the deletion and not the one first written
   * here. The rule's selector is `.ccg-ab-spec__svg`, one class; the lap band
   * above declares the narrow box as `.ccg-ab-spec .ccg-ab-spec__svg--narrow`,
   * two - so `width` and `margin-inline` were both decided there whatever this
   * block said, and specificity does not care which block comes later.
   *
   * MEASURED RATHER THAN ARGUED, because a specificity claim is exactly the kind
   * that reads as obvious and is wrong half the time. On 16.09.2026, on the
   * served /about at a 393 px window, the narrow element's box was read, this
   * rule was re-added verbatim through an injected stylesheet, and the box was
   * read again: x 30, width 318, margin 0 px on both sides, unchanged to the
   * pixel. (318 and not 333 because the browser that took the reading carries a
   * 15 px scrollbar; the number that moves here is the one that did not move.)
   *
   * So what is removed is a dead rule and not a harmful one. An earlier draft of
   * this comment said it asked for 433 px in a 373 px rail and left the field at
   * x -20 with 13 px of empty rail beside it; none of that happened. 373 is the
   * width of the hero row on that page, a different element, and the rule never
   * applied to this one. The narrow drawing sits on the rail, which is where
   * every card on this page sits, and its box is declared once in the lap band
   * above. */

  /* --- /about's region line is one sentence and is laid out as one ----------
   *
   * `.ccg-ab-region` is a flex row over a pin, three text runs and two `<strong>`
   * elements. At 1920 the six items are one line, 680 x 20.6. At 390 each
   * anonymous run becomes a column of its own: measured on 10.09.2026 the block
   * was 330 x 82.4 with `Freiburg`, `Dresden`, `&` and `bundesweit` each set in a
   * column of 30 to 60 px, `Persönliche Betreuung für Unternehmen in` in another,
   * and the sentence unreadable in any order. It is one sentence, so at this band
   * it is one line box; the pin is `inline-flex` and flows with the words. */
  .ccg-ab-region { display: block; }

  /* AND THE PIN HAS TO BE TOLD WHERE THE LINE IS, which is what this one chosen
     value is for. On a flex row `align-items: center` placed it; in a line box
     it aligns on the baseline, and an `inline-flex` whose only child is a
     `display: block` svg takes its own bottom margin edge as that baseline - so
     the whole 15 px disc sits above the text's baseline rather than beside its
     letters. Measured at 390 on 10.09.2026, the pin against the centre of the
     text run it stands in front of - `Persönliche Betreuung für Unternehmen in`,
     an 18 px ink box - with the region's own 14.72 px type, so 0.2em is 2.944:

         vertical-align: baseline   pin centre 2.50 px ABOVE the run's centre
         vertical-align: middle     0.98 px below
         vertical-align: -0.2em     0.44 px below

     `middle` is the keyword that means this and it lands further out than the
     value, because it centres on the x-height's middle rather than on the run's
     ink box. -0.2em was chosen and this is the measurement it was chosen by; it
     had none beside it until the gate of 10.09.2026. */
  .ccg-ab-region__pin { vertical-align: -0.2em; }

  /* --- an article's previous/next pair becomes a column ---------------------
   *
   * `.ccg-post-nav__row` is two `flex: 1 1 50%` items each 96 px tall - the
   * thumbnail's height - with the title beside the thumbnail. At 390 each item
   * is 165 px, the text column inside it 37, and the titles overflow the 96 px
   * they are given: measured on 10.09.2026, `previous` set one letter to a line
   * in a 37 px column 160.3 px tall and `next` in one 503.9 px tall, starting
   * 194 px ABOVE the row and painting through the share marks above it.
   *
   * Stacked, each item is as tall as its own title and the two keep the
   * mirroring the wide layout gives them - the previous article's thumbnail on
   * the left, the next article's on the right - which is the only thing in the
   * pair that says which direction is which. */
  .ccg-post-nav__row {
    flex-direction: column;
    gap: var(--ccg-narrow-row-gap);
  }

  .ccg-post-nav__item { height: auto; }

  /* The placeholder that holds the empty half of the row when an article has no
     older or no newer neighbour. Side by side it is what keeps the other half at
     its own end; stacked it is a 96 px hole in a list of one. */
  .ccg-post-nav__item--empty { display: none; }

  /* --- the blog head stops imposing a height measured at 1920 ---------------
   *
   * `--ccg-bl-head-height` is a `min-height` and at 1920 it is exactly what the
   * band's own content produces: 128 + 128 above the title, 343 of title and
   * sub-line, 192 + 128 below - 919, against a token of 919.2. At 390 the same
   * text is 210 px tall, so the token is 133 px of photograph the content does
   * not ask for, and it became 453 once the title took the narrow punch size.
   * Withdrawn here, the band is 786 and the air under the sub-line is 320 px -
   * the same 320 the wide band leaves. The lead and the trail are untouched. */
  .ccg-bl-head { min-height: 0; }
}

/* --- what a visitor with no hover gets, at every width ------------------------
 *
 * THE GESTURE BELONGS TO THE INPUT AND NOT TO THE WINDOW, which is what this
 * block is for and what the width query above got wrong. The two-tap contract -
 * one tap opens a sub-menu, a second follows its link - was gated on the width
 * band alone, so between 641 and 1024 it swallowed the first click of a mouse:
 * measured at 900 on 2026-09-02 with real mouse events over the DevTools input
 * domain, the first click on `Ueber uns` set the attribute and did not navigate
 * and the second reached /about/. The panel was already open from `:hover`, so
 * that first click produced no visible change at all. A 1920 screen with the
 * window snapped to half is 960.
 *
 * AND `:hover` IS LIVE AT EVERY WIDTH, which the lap band's own comment denied.
 * The comment beside the tap rule above said `:hover` is not available at this
 * width; it is a width query and says nothing about the pointer, so the hover
 * disclosure kept matching everywhere. Whether a touch device leaves `:hover`
 * standing after a tap was not measured when that was written; it is now, at 390
 * with touch emulated over the DevTools protocol - the tapped item read
 * `:hover` true after the finger had gone, on both taps. A panel held open by
 * nothing but a stale hover is a panel no state in this theme describes, which
 * is why the rule is withdrawn here rather than argued about.
 *
 * SO THE CONDITION IS `(hover: none)` AND IT CARRIES NO WIDTH. A touch device
 * wider than the lap band gets the gesture too, which is what the spec delta's
 * `Touch opens what hover opens` says without a width condition and what was
 * true only up to 1024 before this block: on an iPad in landscape the four
 * routes under `Ueber uns` and `Karriere` had no gesture that reached them.
 * js/ccg.js asks the same media feature and nothing else; the width half of the
 * question it still asks the stylesheet, through the control's own `display`.
 *
 * NOTHING HERE APPLIES TO A POINTER. Every browser this repository measures with
 * reports `(hover: hover)`, so this block is inert in all of them unless touch
 * emulation is switched on - which is how it was measured:
 * `Emulation.setTouchEmulationEnabled` makes `(hover: none)` and
 * `(pointer: coarse)` true over the same protocol the rest of the suite drives.
 */
@media (hover: none) {
  /* The hover disclosure is withdrawn and the two that a finger and a key can
     actually produce are restated after it, so that an entry which is both
     hovered and open is open. Same 0-3-0 as the rule it replaces; later in the
     cascade is what decides it. */
  .ccg-nav__item--has-children:hover > .ccg-nav__sublist { display: none; }

  .ccg-nav__item--has-children:focus-within > .ccg-nav__sublist,
  .ccg-nav__item--has-children[data-ccg-open="true"] > .ccg-nav__sublist { display: flex; }
}

/* --- less motion, if that is what the visitor asked for ----------------------
 *
 * THE ONE DELIBERATE DEPARTURE FROM THE LIVE SITE IN THIS THEME. Measured rather
 * than assumed: all twelve of its external stylesheets and all nine inline style
 * blocks were fetched and searched, and the two occurrences of
 * `prefers-reduced-motion` between them are a contact form's spinner and a rule
 * nested inside `@media (max-width: 900px)`, which cannot apply at the width this
 * migration is measured at.
 *
 * THE MOTION IS REMOVED, NOT SHORTENED. `transition: none` rather than the
 * hundredth of a second the live site's own unreachable rule uses. And the
 * element that was going to be revealed is shown rather than left at zero.
 *
 * THE ANIMATIONS GO WITH THE TRANSITIONS, and they did not have to before: until
 * this cycle the theme had none. The composition brings sixteen keyframe
 * animations, eleven of them infinite - the mesh drift, the node shimmer, the hub
 * float, two ring rotations, the dot pulse, the badge pulse, the stripe. A page
 * that switches off its transitions and goes on pulsing is not a page with less
 * motion on it.
 *
 * THE SELECTORS ARE REPEATED RATHER THAN SWEPT UP BY `*` ALONE: `*` has
 * specificity zero and every rule above it is more specific, so the sweep would
 * lose to each of them and remove nothing. `!important` would win, and this file
 * has none; the honest alternative is to name the same selectors again, later in
 * the cascade, where they win by order.
 */
@media (prefers-reduced-motion: reduce) {
  [data-ccg-motion="on"] [data-ccg-reveal],
  [data-ccg-motion="on"] [data-ccg-reveal="in"] { opacity: 1; }

  [data-ccg-reveal],
  /* THE DRAWER, WHICH IS THE FIRST THING IN THIS LIST THAT ONLY EXISTS BELOW A
     BAND EDGE. `.ccg-nav` had no transition of its own until the narrow bands
     gave it one, and the `*` sweep at the end of this list cannot take it away:
     the paragraph above this block says why - `*` is specificity zero and
     `.ccg-nav` is 0-1-0. Measured on 2026-09-02 by emulating
     prefers-reduced-motion at 390 before this line existed, the drawer still
     computed the whole of --ccg-nav-drawer-motion rather than none. It still
     opens and still closes; it just arrives.

     THE VALUE IS NOT QUOTED HERE AND THAT IS NOT STYLE. test/motion.sh's
     `every transition value comes from a token` case strips comments with
     `sed 's|/\*.*||'`, which is single-line, so a duration and a curve written
     out in prose inside a block comment like this one are read as a rule and
     turn the case red - measured on 2026-09-02, when an earlier wording of this
     paragraph did exactly that. The case is right about the file and wrong about
     the line; it is left alone and the prose names the token instead. */
  .ccg-nav,
  .ccg-nav__list,
  .ccg-nav__sublist,
  .ccg-nav__link,
  /* The current-branch bar. It declares the same token as the link it sits in,
     so it needs the same line here; a pseudo-element is not swept up by the
     selector of its originating element. */
  .ccg-nav__link--l1::after,
  .ccg-header,
  .ccg-nav__sublist .ccg-nav__item,
  .ccg-card,
  .ccg-service,
  .ccg-service__photo,
  .ccg-service__icon,
  .ccg-teaser,
  .ccg-teaser__title,
  .ccg-button,
  /* The one selector `build-the-last-eight-pages` adds that is still served. It
     declares a `--ccg-t-*` transition above, and a selector that is not repeated
     here keeps its transition under `prefers-reduced-motion: reduce` - which is
     the whole of what this block exists to prevent.

     IT ADDED FOUR, AND THREE WERE /recruiting's: `.ccg-rc-cta`,
     `.ccg-rc-cta-outline` and `.ccg-rc-lnk::after`. That page's service was
     withdrawn with OP#4618 and its rules with OP#4653, so the three name
     nothing. Their transitions went with them, which is why removing them from
     this list takes no motion out of anyone's reach. */
  .ccg-lt-button,
  .ccg-archive-button,
  .ccg-ghost-button,
  .ccg-arrow,
  .ccg-branche__circle,
  .ccg-branche__image,
  .ccg-branche__label,
  .ccg-branche__ring,
  .ccg-branche__tooltip,
  .ccg-job-card,

  /* THE INNER PAGES HAD TO BE NAMED HERE TOO, AND WERE NOT UNTIL NOW. The sweep
     at the end of this list is `*`, which has specificity zero and loses to
     every one of the class rules that declare these transitions - which is the
     whole reason the home page's twenty selectors are written out above. When
     four new namespaces arrived they inherited the argument and not the list,
     so a visitor who asked for less motion still got every hover on the five
     service pages, on /about/, on /about/kontakt/ and on /karriere/. Measured
     rather than reasoned: the selectors below are every rule between
     `--- the inner pages ---` and this block that states a `transition`, read
     out of the stylesheet rather than remembered. */
  .ccg-sv-pill,
  .ccg-sv-pill::before,
  .ccg-sv-pill__arrow,
  .ccg-sv-feature,
  .ccg-sv-feature__image,
  .ccg-sv-cons__card,
  .ccg-sv-cons__card::after,
  .ccg-sv-cons__photo,
  .ccg-sv-cons__title,
  .ccg-sv-chapter,
  .ccg-sv-chapter__pill,
  .ccg-sv-chapter__title,
  .ccg-sv-chapter__watermark,
  .ccg-sv-dash__chart,
  .ccg-sv-dash__pair,
  .ccg-sv-dash__bar,
  .ccg-sv-dash__shine,
  .ccg-sv-dash__glow,
  .ccg-sv-dash__tip,
  .ccg-sv-dash__stat,
  .ccg-sv-mtx__card,
  .ccg-sv-case,
  .ccg-sv-san__card,
  .ccg-sv-road__card,
  .ccg-sv-road__dot,
  .ccg-ab-tl__marker,
  .ccg-ab-tl__decor,
  .ccg-ab-method,
  .ccg-ab-close__cta,
  .ccg-ab-close__arrow,
  .ccg-ko-method,
  .ccg-ko-method__arrow svg,
  .ccg-ko-social__link,
  .ccg-ka-card,
  .ccg-ka-btn,
  .ccg-ka-btn__arrow,

  /* AND THE LEISTUNG CARD, WHICH THE PARAGRAPH ABOVE PROMISED AND DID NOT
     DELIVER. `show-the-card-edges-and-the-graphic-type` gave `.ccg-lt-card` a
     `transition` of its own - it had none until then - and did not add it here,
     so a visitor of /mergers-acquisitions or
     /strategische-beratung-transformationswissen-bw who had asked the system for
     less motion still got the whole of --ccg-t-card: 0.4 s of transform,
     box-shadow and border-color under the pointer. The `*` sweep at the end of
     this list does not reach it, for the reason stated twice above: `*` is
     specificity 0-0-0 and `.ccg-lt-card` is 0-1-0.

     THE CLAIM THE PARAGRAPH ABOVE MAKES WAS RE-CHECKED RATHER THAN TRUSTED, and
     the way it was checked is repeatable: every rule between the
     `--- the inner pages ---` marker and this block was parsed out of this file,
     comments stripped, and every selector stating a `transition` other than
     `none` compared against the list above. One was missing and it is the line
     below. A claim about completeness that was false once can be false again, so
     the next change to give a rule in that span a transition is asked to run the
     same sweep rather than to read this note. */
  .ccg-lt-card,

  /* The footer, for the third time and for the same reason the two notes above
     give: the sweep is `*` at specificity zero and loses to the class rules that
     declare these. Read out of the stylesheet rather than remembered - these are
     every rule in the footer block that states a `transition`. */
  .ccg-footer__link,
  .ccg-footer__social-link,
  *, *::before, *::after { transition: none; }

  .ccg-home-hero::before,
  .ccg-home-hero__punch::after,
  .ccg-network__halo,
  .ccg-network__lead,
  .ccg-network__line,
  .ccg-network__amb,
  .ccg-network__dot,
  .ccg-network__hub,
  .ccg-network__hub-ring,
  .ccg-network__hub-ring::after,
  .ccg-branche,
  .ccg-branche__circle,
  .ccg-branche:hover .ccg-branche__ring,
  .ccg-home-zukunft::before,
  .ccg-job-card::before,
  .ccg-job-card__dot,

  /* The same omission, in the same list, for the same reason. Two of these are
     the ones a visitor would notice first: `.ccg-sv-hero::before` and
     `.ccg-ko-hero::before` are the drifting mesh behind two heroes, and at
     0-1-1 they beat `*::before`'s 0-0-1 - so the page that was asked to stop
     moving went on breathing. /karriere/ appears nowhere below, and that is
     measured too: not one element on it carries an animation name other than
     `none`. */
  .ccg-sv-hero::before,
  .ccg-sv-hero__punch::after,
  .ccg-sv-hero__anim .ccg-sv-shimmer,
  .ccg-sv-cons__card,
  .ccg-sv-cons__line,
  .ccg-sv-cons__ring::before,
  .ccg-sv-cons__ring::after,
  .ccg-sv-dash__bar,
  .ccg-sv-dash__shine,
  .ccg-sv-dash__glow,
  .ccg-sv-dash__pair--feat .ccg-sv-dash__glow,
  .ccg-sv-dash__val,
  .ccg-sv-dash__shadow,
  .ccg-sv-dash__callout,
  .ccg-sv-dash__live-dot,
  .ccg-sv-mtx__cell--ccg .ccg-sv-mtx__dot--full,
  .ccg-sv-case__line--before,
  .ccg-sv-case__line--after,
  .ccg-sv-case__mark,
  .ccg-sv-case__mid,
  .ccg-sv-case__end,
  .ccg-sv-case__traj-lbl--mid,
  .ccg-sv-road__line,
  .ccg-sv-road__phase,
  .ccg-sv-road__phase::before,
  .ccg-ab-tl__entry,
  .ccg-ab-tl__entry:last-child .ccg-ab-tl__marker,
  .ccg-ab-emblem__ring,
  .ccg-ab-emblem__cardinals polygon,
  .ccg-ab-seed__leaf-a,
  .ccg-ab-seed__leaf-b,
  .ccg-ab-seed__sparks circle,
  .ccg-ab-bars rect,
  .ccg-ab-donut__ring,
  .ccg-ab-donut__slice,
  .ccg-ab-pentagon__frame,
  .ccg-ab-pentagon__nodes > g,
  .ccg-ab-dach__shape,
  .ccg-ab-dach__pulse,
  .ccg-ab-mission__compass .ccg-ab-compass__ring,
  .ccg-ab-mission__compass .ccg-ab-compass__needle,
  .ccg-ab-spec__stream,
  .ccg-ab-spec__source,
  .ccg-ab-spec__label,
  .ccg-ab-spec__tick,
  .ccg-ab-spec__hub,
  .ccg-ab-spec__hub-ring,
  .ccg-ab-spec__hub-pulse,
  .ccg-ko-hero::before,
  *, *::before, *::after { animation: none; }

  /* The lines and the headline's rule are drawn by an animation that ends at
     their finished state, so switching the animation off has to leave them
     finished rather than at the state they start from. */
  .ccg-network__line,
  .ccg-network__lead { stroke-dashoffset: 0; }
  .ccg-network__amb { opacity: 0.55; }
  .ccg-home-hero__punch::after { width: 100%; opacity: 1; }

  /* The service pages have two of the same shape and they were missed when the
     inner pages arrived, which is what a rule that has to be repeated per
     selector costs. `bhUnderline` ends at `width: 80%` from a start of 0 and
     `bhCstlDraw` ends at a dash offset of 0 from a start of 1000, both
     `forwards`, so switching the animation off without restoring the end state
     leaves the service hero with no rule under its headline and the industries
     constellation with no connecting lines at all - a page with less motion on
     it, and less of the page. 80 rather than 100: the live keyframe's own term,
     and it is not the home page's. */
  .ccg-sv-hero__punch::after { width: 80%; opacity: 1; }
  .ccg-sv-cons__line { stroke-dashoffset: 0; }

  /* THE SAME SHAPE AGAIN, NINE TIMES, IN THE FIVE SECTIONS THE SERVICE PAGES
     ADD. Each of these is declared in the state it starts from and brought to
     the state a visitor sees by an animation that ends there - scaleY(0) on the
     dashboard's bars, opacity 0 on its value labels, its bar shadows and its
     callout, a dash offset on the case trajectories, opacity 0 on their marker,
     their two dots and the CCG label, scaleX(0) on the roadmap's track and
     opacity 0 with an 8 px offset on its phases. `animation: none` on those
     alone leaves a chart with no bars in it, three cards with no line in them
     and six phases nobody can read: less motion turned into less page.

     The dash offset and the opacity are presentation attributes on the SVG
     shapes rather than declarations, and a declaration outranks one - which is
     why none of these needs `!important` to undo what the markup carries. */
  .ccg-sv-dash__bar,
  .ccg-sv-dash__shine,
  .ccg-sv-dash__glow { transform: scaleY(1); }

  /* The portrait drawing's bars are held at `scaleX(0)` rather than `scaleY(0)`,
     so the axis that has to be put back is the other one. Without this line the
     ten bars of that drawing are invisible to a visitor who asked for less
     motion, which is the same defect the line above exists to prevent. */
  .ccg-sv-dash__svg--tall .ccg-sv-dash__bar,
  .ccg-sv-dash__svg--tall .ccg-sv-dash__shine { transform: scaleX(1); }

  .ccg-sv-dash__val,
  .ccg-sv-dash__shadow,
  .ccg-sv-dash__callout { opacity: 1; }

  /* The dash offset and the opacity are presentation attributes on the SVG
     shapes rather than declarations, and a declaration outranks one - which is
     why these four need no `!important` to undo what the markup carries. */
  .ccg-sv-case__line--before,
  .ccg-sv-case__line--after { stroke-dashoffset: 0; }

  .ccg-sv-case__mark,
  .ccg-sv-case__mid,
  .ccg-sv-case__end,
  .ccg-sv-case__traj-lbl--mid { opacity: 1; }

  .ccg-sv-road__line { transform: scaleX(1); }

  /* The narrow arrangement's own six segments, which are the same line one
     element along and would otherwise rest at `scaleY(0)` - drawn nowhere. The
     rule is written unconditionally because the pseudo-element only exists
     inside the 640 block; at every wider band there is nothing here to match. */
  .ccg-sv-road__phase::before { transform: scaleY(1); }

  .ccg-sv-road__phase {
    opacity: 1;
    transform: none;
  }

  /* And on /about/, where the cost of leaving them out is the whole of two
     sections: the five timeline entries start at opacity 0 with a 20 px offset,
     and every part of the spectrum - its five streams, its five source groups,
     its labels, its ticks and its hub - starts invisible. Switching the
     animations off without this leaves a 1572 px timeline and a 1052 px graphic
     that are there, take their space, and cannot be seen. */
  .ccg-ab-tl__entry { opacity: 1; transform: translateY(0); }
  .ccg-ab-spec__stream { stroke-dashoffset: 0; }

  .ccg-ab-spec__source,
  .ccg-ab-spec__label,
  .ccg-ab-spec__tick { opacity: 1; }

  .ccg-ab-spec__hub { opacity: 1; transform: scale(1); }
}

/* --- the wide band ----------------------------------------------------------
 *
 * THE ONLY `min-width` BLOCK IN THIS FILE, AND THE ONLY ONE THERE IS GOING TO
 * BE. Everything above was measured at 1920 on the live site and reproduced;
 * the three blocks before this one are the narrow view, decided for this site.
 * This block is neither: it is a DECLARED DEPARTURE FROM THE TRANSFER. The live
 * site strands the same 1200 px column in a 3840 px window, measured
 * 15.09.2026 on the working tree's own build - margins of 360 / 680 / 1120 /
 * 1320 a side at 1920 / 2560 / 3440 / 3840, content occupying 62.5 / 46.9 /
 * 34.9 / 31.3 per cent - and this block is made because the client asked for
 * it, not because the original does it.
 *
 * 1921 AND NOT 1920. The requirement this block exists under is that everything
 * at the desktop width and below is IDENTICAL, and a `min-width: 1921px` query
 * cannot apply at 1920 or at anything under it. That is the structural half; the
 * measured half is in design.md 2.3, which compares every section's box on all
 * thirty-six routes at 1920, 1440, 1180, 393 and 375 x 667 against a container
 * built from HEAD.
 *
 * `--ccg-measure` IS NOT REASSIGNED HERE, AND THAT IS THE DESIGN OF THIS BLOCK
 * RATHER THAN AN OMISSION. That token holds 1200px, which is what the live
 * site's column measures, and its meaning is a LINE LENGTH. The rail and the
 * line length are two different things and this site had one name for both;
 * above the desktop width they part, so the rail gets its own value and
 * `--ccg-measure` goes on meaning what it was measured to mean. That is what
 * lets the running-prose rule further down cap a column without writing 1200
 * anywhere.
 */
@media (min-width: 1921px) {
  :root {
    /* THE RAIL, AND WHY IT IS A RAMP RATHER THAN A STEP. `62.5vw` is not a new
       number: 1200 / 1920 is 0.625, so this is the share of the window the
       desktop composition already keeps, carried upward. At 1921 it computes
       1200.63px, which is why crossing the boundary moves nothing a person can
       see; it reaches `--ccg-wide-measure-ceiling` at 2560 and stops. */
    --ccg-rail: min(62.5vw, var(--ccg-wide-measure-ceiling));
  }

  /* 1. THE RAIL-BEARING BOXES. Every one of these reads `--ccg-measure` above
        for the same purpose - the width of the band the section lays its
        content across - and each is listed by name rather than reached by a
        rewrite of the token, so that the boxes that are NOT rails keep what
        they had. */
  .ccg-measure,
  .ccg-home-hero__inner,
  .ccg-network { width: var(--ccg-rail); }

  .ccg-sv-mtx,
  .ccg-sv-road { max-width: var(--ccg-rail); }

  /* AND TWO THAT ARE NOT IN THAT LIST, BECAUSE THE NEW WIDE CHECK CAUGHT THEM.
     `.ccg-sv-cons` and `.ccg-sv-cases` were in it for one build. Both carry a
     graphic drawn `preserveAspectRatio="none"` at a height that does not follow
     the width, so widening the box stretches the drawing. Measured at 2560 with
     both in the list, against the same reading on HEAD:

       svg.ccg-sv-cons__lines     0 0 1200 700 into 1600x589   skew 1.108 -> 1.585
       svg.ccg-sv-case__traj-svg  0 0 280 60   into 468x60      skew 1.196 -> 1.671

     `SKEW_MAX` in test/lib/pages_read.py is 1.5, so `case_slice` went red on
     all five service pages - a circle painted 1.58 times as wide as it is tall.
     They keep the 1200 rail and sit centred inside the wider band. */

  /* The recruiting row's own centring arithmetic, which is the same expression
     as the rule it overrides with the rail in place of the measure. */

  /* 2. AND THE TWO COLUMNS OF RUNNING PROSE, WHICH DO NOT GET THE RAIL.
        This is a departure the tasks did not ask for and design.md 2.1 records
        it as one. Measured 15.09.2026 at 1920 on the working tree's build: the
        body of `/datenschutzerklaerung` sets paragraphs 1199.7px wide at 16px
        and `/die-psychologie-des-verkaufs` 1193.6px at 16px - the full rail,
        line for line, on the four legal pages and the seventeen articles, which
        is twenty-one of the thirty-five routes. Giving those a 1600px rail
        would lengthen a line that is already the longest on the site. They keep
        `--ccg-measure`, which is the length that was measured.

        A CAP AND NOT A NARROWER RAIL, AND THE FIRST WRITING OF THIS RULE GOT
        THAT WRONG. It pinned the whole prose BLOCK to 1200 and centred it,
        which at 2560 left /impressum's `h1` starting at x 477 with its own body
        starting at x 680 and the header's logo at 477 - the page split down the
        middle into two left edges. Photographed, seen, and replaced by this:
        the block keeps the rail and shares its left edge with the headline and
        the header, and only the LINE stops short. */
  .ccg-post__body,
  .ccg-rt-text__body { max-width: var(--ccg-measure); }

  /* 3. THE TYPE STEP. `--ccg-wide-type-step` is 1.25 and ccg.yaml says which two
        tokens it is the ratio of. It is applied at the elements rather than by
        rewriting the scale, for the reason CSS gives us no choice about:
        `--ccg-h1-size: calc(var(--ccg-h1-size) * k)` is a cycle and computes to
        the guaranteed invalid value.

        WHAT TAKES THE STEP AND WHAT DOES NOT, said here rather than left to be
        counted off the selectors. The body's own size and leading take it, and
        so do the two elements that declare the SAME two tokens on themselves -
        `.ccg-post__body` and `.ccg-rt-text__body`, the running prose of the
        seventeen articles and the four legal pages. Those two are named because
        naming `body` alone did not reach them and the first build of this block
        proved it: measured at 2560, the paragraphs of /datenschutzerklaerung
        and /die-psychologie-des-verkaufs were still 16px while `body` was 20.
        That prose is the reason the step is worth having: a 27-inch 1440p panel
        at 100 per cent presents 2560 CSS px, and 16px of type on it is
        physically smaller than 16px on the 24-inch 1080p panel this site was
        composed against.

        AND IT SHORTENS THOSE LINES RATHER THAN LENGTHENING THEM, which is the
        second half of the prose decision two rules above. The column is held at
        1200px and the type grows, so the same line carries fewer characters at
        2560 than it does at 1920.

        The page headline takes the step in all three of the shapes this site
        gives an h1. Nothing else does: every other element on this site
        declares its size from a token of its own, and moving a hundred of them
        is a redesign rather than a step. design.md 2.2 lists what is left at
        its desktop size. */
  body,
  .ccg-post__body,
  .ccg-rt-text__body {
    font-size: calc(var(--ccg-size-body) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-line-body) * var(--ccg-wide-type-step));
  }

  /* THE HEADLINE, AND IT IS FIVE ELEMENTS RATHER THAN ONE. `h1` alone reaches
     the home page and the service and about heroes; it does NOT reach the
     article, the blog overview or the legal page, each of which sets a size of
     its own on the title element and outranks a bare `h1`. Measured at 2560
     with `h1` alone in place: /die-psychologie-des-verkaufs stayed at 44px and
     /impressum at 50px while /'s h1 was 75.

     `.ccg-rt-kopf__title` - the legal pages - is NOT in this list, and that is
     the one gap. It carries `font-size: 50px` as a literal rather than from a
     token, so a step for it would mean writing `calc(50px * ...)` here, which
     is the second copy of a number this block is written to avoid. Its body
     prose takes the step and its headline does not; design.md 2.2 records it. */
  h1 {
    font-size: calc(var(--ccg-h1-size) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-h1-line) * var(--ccg-wide-type-step));
  }

  .ccg-post__title {
    font-size: calc(var(--ccg-bl-post-title-size) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-bl-post-title-line) * var(--ccg-wide-type-step));
  }

  .ccg-bl-head__title {
    font-size: calc(var(--ccg-bl-head-title-size) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-bl-head-title-line) * var(--ccg-wide-type-step));
  }

  .ccg-sv-hero__setup {
    font-size: calc(var(--ccg-sv-hero-setup-size) * var(--ccg-wide-type-step));
  }

  .ccg-sv-hero__punch {
    font-size: calc(var(--ccg-sv-hero-punch-size) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-sv-hero-punch-line) * var(--ccg-wide-type-step));
  }

  .ccg-home-hero__setup {
    font-size: calc(var(--ccg-h1-setup-size) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-h1-setup-line) * var(--ccg-wide-type-step));
  }

  .ccg-home-hero__punch {
    font-size: calc(var(--ccg-h1-punch-size) * var(--ccg-wide-type-step));
    line-height: calc(var(--ccg-h1-punch-line) * var(--ccg-wide-type-step));
  }

}

/* --- the two decorative layers, above the shape they can still hold ----------
 *
 * WHAT THIS CLOSES, AND THE NUMBERS IT WAS CLOSED ON. Two elements draw a
 * `0 0 1600 900` field with `preserveAspectRatio="none"`, which is the one
 * fitting that distorts rather than crops or fits. Measured 21.09.2026 on a
 * container built from this tree at 3840 x 1080, with `case_slice`'s own
 * arithmetic - skew is 1.000 unless the fitting is `none`, else the larger of
 * `sx/sy` and `sy/sx`:
 *
 *     /                           ccg-home-zukunft__field   3840 x 1314   1.644
 *     /about                      ccg-sv-particles--wide    3840 x 1080   2.000
 *     /existenzgruender-beratung  ccg-sv-particles--wide    3840 x 1080   2.000
 *     /externer-cfo               ccg-sv-particles--wide    3840 x 1080   2.000
 *     /lohnabrechnung             ccg-sv-particles--wide    3840 x 1080   2.000
 *     /single-service             ccg-sv-particles--wide    3840 x 1080   2.000
 *     /strategieberatung          ccg-sv-particles--wide    3840 x 1080   2.000
 *
 * `SKEW_MAX` is 1.5, so all seven are past it. They are the same seven the
 * previous cycle read by hand and left, and they reproduced exactly.
 *
 * THE CONDITION IS WRITTEN DOWN, NOT COMPUTED FROM THE CEILING, and that is a
 * deliberate difference from how two earlier blocks were derived. A
 * `0 0 1600 900` field in a W x H box has skew `(W/H) x 0.5625`, so it crosses
 * 1.5 at an aspect ratio of 8/3 - that is where the number comes from. But
 * `SKEW_MAX` is a threshold a check applies, and the comment on it already
 * records that deriving layout conditions from it has made the ceiling a
 * parameter of the layout it judges, twice, and that this was a cost. So 8/3 is
 * stated here as this block's own value with its derivation shown, and moving
 * `SKEW_MAX` does not move it. A future change that wants them to move together
 * has to say so.
 *
 * WHAT IT DOES, AND THE ALTERNATIVE IT WAS CHOSEN OVER. It withdraws the two
 * layers above that shape. The alternative was a third rendering, the way the
 * `--wide` and `--tall` pair above already works, and it was not taken: both
 * elements are ornament - a drifting particle field behind a hero and behind the
 * home page's future band - neither carries a word, and neither is what any
 * visitor came for. A third drawing would be a third thing to keep in step with
 * the other two for a shape only a 4K window at full width reaches.
 *
 * WHAT A VISITOR LOSES: at an aspect ratio of 8/3 or wider, the particles behind
 * these bands are not drawn. The band, its photograph, its mesh and all of its
 * text are untouched. What is removed is the layer that was being drawn twice as
 * wide as it was composed.
 *
 * WHAT THIS DOES NOT ASSERT: 3840 is not a width `test/pages.sh` drives, and this
 * change does not make it one - see design.md 3.3 for that decision and its
 * cost. The seven readings above were taken by hand, before and after, and the
 * suite would not catch a regression here. */
@media (min-aspect-ratio: 8/3) {
  .ccg-home-zukunft__particles .ccg-home-zukunft__field--wide,
  .ccg-sv-hero__particles .ccg-sv-particles--wide { display: none; }
}
