/* =============================================================
   Treuhand Moser, Buehlikon AG
   Sample site for pronta.ch. Design charter: sites/CHARTERS.md, chapter 1.
   Three colour worlds by Martin (blue, anthracite, brown), one primary each.
   ============================================================= */

/* ---------- fonts ------------------------------------------- */

@font-face {
  font-family: "Plex Sans";
  src: url("../fonts/ibmplexsans-latin-var.woff2") format("woff2");
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- tokens ------------------------------------------ */

/* Three colour worlds, set by Martin (round 3, 01.09.2026, points 1 to
   6): six anchors each, his hex values verbatim. Blau / Weiss is the
   default and the only one that applies without JavaScript; the demo
   layer switches the other two in through data-palette on <html>. Every
   further colour is derived from the six anchors with color-mix, so a
   palette block is exactly six lines and the derived tones follow the
   palette's own hue. The mix runs in oklab, not oklch: paper is warm and
   ink is cool, and a hue arc between the two passes through green, the
   straight line in oklab cannot. Contrasts measured 01.09. (derive.py in
   the build report): every text pair 4.96:1 or better in all three. */

:root {
  color-scheme: light;

  /* Blau / Weiss: cool and serious, the fiduciary world without the
     SaaS or bank blue. --surface is Martin's pure white, the one
     deliberate exception to DESIGN-RULES 3 (spec R3, A10). */
  --paper: #F7F6F2;    /* main ground */
  --surface: #FFFFFF;  /* light surfaces: alternating bands, form fields */
  --ink: #222426;      /* running text */
  --seal: #294E73;     /* primary: buttons, links, active navigation, marks, states */
  --deep: #172C40;     /* the stage at the top and the footer */
  --rule: #A9BBCB;     /* hairlines and small accents */

  /* Derived, not authored. */
  --ink-soft: color-mix(in oklab, var(--ink) 68%, var(--paper));        /* secondary text: 5.7:1+ on paper */
  --seal-deep: color-mix(in oklab, var(--seal) 70%, var(--deep));       /* hover: one step towards the deep anchor */
  --seal-ink: var(--surface);                                            /* text on the primary: the palette's white */
  --deep-ink: color-mix(in oklab, var(--paper) 94%, var(--deep));       /* text on the deep ground */
  /* Secondary text on the deep ground. Derived from the palette's own light
     tone (--rule, Martin's "dezente Linien und Akzente"), not from the paper
     white: mixing white into a blue ground cancels the hue and leaves a grey
     that sits ON the surface instead of belonging to it, which is the
     unentschiedene Graustufe DESIGN-RULES section 3 warns about (design hook,
     02.09.2026). Measured against the deep ground: saturation 17 -> 35 and
     contrast 5.44 -> 5.85 in blue, 5.34 -> 5.36 in brown; anthracite stays
     near-neutral because Martin's ground there is near-neutral. */
  --deep-soft: color-mix(in oklab, var(--rule) 88%, var(--deep));
  --deep-rule: color-mix(in oklab, var(--deep-ink) 16%, transparent);
  /* The accent on the deep ground is Martin's line colour: in every world
     it is the light stop of the primary's family, and in anthracite it is
     the only tone that still reads on --deep at all, where the primary
     (#292B2C on #171819) would vanish. As hover text 6.5:1 or better. */
  --seal-on-deep: var(--rule);

  --sans: "Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif;

  --ease-out: cubic-bezier(0.2, 0, 0, 1);

  /* Derived, not authored: both are the palette's own ink at low
     opacity, so no scheme ever gets a shadow in a colour it does not
     own. There are exactly three shadows on the site (the letterhead
     card, the stacked running head, the buttons); everything else stays
     flat and is carried by hairlines. */
  --shade-near: color-mix(in oklch, var(--ink) 12%, transparent);
  --shade-far: color-mix(in oklch, var(--ink) 9%, transparent);

  --gutter: clamp(1.25rem, 5vw, 3.25rem);
  --colgap: clamp(1rem, 2.2vw, 2.25rem);
  /* One lead column for every two-column body block (trust, services,
     zone-split), so the second column starts at the same x on every
     section of every page instead of three near-misses (critic round 3,
     customer lens: 540 / 584 / 616 at 1440). */
  --split: 22rem;
  /* One radius for the interactive surfaces of the firm layer: fields,
     buttons and the action bar spoke in 7, 10 and 14px side by side.
     Sections, bands and the map stay square; the tool layer keeps its
     own pills. */
  --r-ui: 8px;
  /* 38rem, and the gaps sensor caps the rhythm: anything past ~4.5rem
     stacks two paddings over the 165px desktop threshold (round 2b) */
  --measure: 38rem;
  --rhythm: clamp(2.75rem, 5.5vw, 4.5rem);

  /* Type scale: five steps (Martin, round 3, 01.09.2026) and no size
     outside this list; the check is grep 'font-size' on this file.
     Tracking is bound to the step, not to the element, so a size never
     appears with two different letter spacings. Weight carries the
     hierarchy inside a step (400 normal, 500 navigation, 650 bold),
     never a sixth size. The numbering is historical and kept, so every
     rule below still names the same step. */
  --t2: 0.875rem;   --tr2: 0.02em;    /* 5: eyebrows, labels, small print; eyebrows take 0.06em via .label */
  --t4: 1.075rem;   --tr4: 0em;       /* 4: running text, navigation, controls, action bar */
  --t6: 1.35rem;    --tr6: -0.004em;  /* 3: entry, person and step names, footer name */
  --tr6-title: -0.008em;              /* the same step set as a heading */
  --t7: clamp(1.5rem, 1.2rem + 1.1vw, 2.05rem);
  --tr7: -0.014em;                    /* 2: h2 and the hero values line */
  --t8: clamp(2.1rem, 1.5rem + 2vw, 3.1rem);
  --tr8: -0.02em;                     /* 1: h1 and the wordmark */

  /* Two underline offsets for the whole site: one for text sizes, one for
     the display sizes, where the same distance would read as detached. */
  --ul-text: 0.22em;
  --ul-display: 0.14em;
}

/* Anthrazit / Weiss: reduced and quiet, no pure black. The primary is
   itself a dark grey, so links live by their underline (WCAG 1.4.1). */
:root[data-palette="anthrazit"] {
  --paper: #F6F5F1;
  --surface: #FFFFFF;
  --ink: #222222;
  --seal: #292B2C;
  --deep: #171819;
  --rule: #B7B5AF;
}

/* Braun / Creme: warm and traditional, lighter than the brown of rounds
   1 to 2b; the light surface is Martin's cream, not white. */
:root[data-palette="braun"] {
  --paper: #F6F1E9;
  --surface: #FFFCF8;
  --ink: #28231F;
  --seal: #694B38;
  --deep: #34271E;
  --rule: #C6AA91;
}

/* Fallback for engines without color-mix(): the derived tones as hex,
   computed the same way (derive.py, 01.09.2026). The anchors above are
   plain hex and need none. */
@supports not (color: color-mix(in oklab, red, blue)) {
  :root {
    --ink-soft: #5F6060;
    --seal-deep: #234363;
    --deep-ink: #E8E9E6;
    --deep-soft: #96A8B9;
    --deep-rule: rgba(232, 233, 230, 0.16);
    --shade-near: rgba(34, 36, 38, 0.12);
    --shade-far: rgba(34, 36, 38, 0.09);
  }

  :root[data-palette="anthrazit"] {
    --ink-soft: #5E5E5D;
    --seal-deep: #232526;
    --deep-ink: #E7E6E2;
    --deep-soft: #A1A09B;
    --deep-rule: rgba(231, 230, 226, 0.16);
    --shade-near: rgba(34, 34, 34, 0.12);
    --shade-far: rgba(34, 34, 34, 0.09);
  }

  :root[data-palette="braun"] {
    --ink-soft: #635E58;
    --seal-deep: #584030;
    --deep-ink: #E9E3DB;
    --deep-soft: #B39982;
    --deep-rule: rgba(233, 227, 219, 0.16);
    --shade-near: rgba(40, 35, 31, 0.12);
    --shade-far: rgba(40, 35, 31, 0.09);
  }
}

@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

/* ---------- base -------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* lets height: auto animate; ignored where unsupported, and the
     disclosure then simply snaps open as it always did */
  interpolate-size: allow-keywords;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- reveals -------------------------------------------
   Switched on by js/site.js and by nothing else: without the script,
   without IntersectionObserver, or with reduced motion asked for, <html>
   never gets rv-init and none of this applies, so everything is simply
   there. Only opacity and translate move, so nothing reflows. The
   script drops the attribute once the movement is over, which also
   keeps the finished element from being a containing block for the tool
   layer's fixed editing sheet. */
html.rv-init [data-reveal] {
  opacity: 0;
  translate: 0 26px;
}

html.rv-init [data-reveal].rv-on {
  opacity: 1;
  translate: none;
  transition: opacity 600ms var(--ease-out), translate 600ms var(--ease-out);
  transition-delay: calc(var(--rv-i, 0) * 120ms);
}

/* the three balance strokes draw themselves as their column arrives */
html.rv-init [data-reveal]::after {
  transform: scaleX(0);
  transform-origin: left;
}

html.rv-init [data-reveal].rv-on::after {
  transform: scaleX(1);
  transition: transform 500ms var(--ease-out) 250ms;
}

/* Round 2b motion. The portrait reveals as a curtain from the top
   instead of sliding, on the same observer and the same reduced-motion
   switches as everything else; clip-path does not make the figure a
   containing block for the tool layer's fixed sheet, and the attribute
   drop at transition end clears the state anyway. */
html.rv-init [data-reveal].rv-curtain {
  opacity: 1;
  translate: none;
  clip-path: inset(0 0 100% 0);
}

html.rv-init [data-reveal].rv-curtain.rv-on {
  clip-path: inset(0 0 0% 0);
  transition: clip-path 800ms var(--ease-out);
}

/* A section heading rises out of its own line box: the generic fade
   plus a short rise under a self-clip, which reads as a line mask
   without wrapping the editable text in extra spans. The negative end
   inset frees the descenders before the attribute drops. */
html.rv-init h2[data-reveal] {
  translate: 0 0.45em;
  clip-path: inset(0 0 100% 0);
}

html.rv-init h2[data-reveal].rv-on {
  translate: none;
  clip-path: inset(0 0 -0.25em 0);
  transition: opacity 600ms var(--ease-out), translate 600ms var(--ease-out),
    clip-path 600ms var(--ease-out);
  transition-delay: calc(var(--rv-i, 0) * 120ms);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  line-height: 1.62;
  font-optical-sizing: auto;
  font-synthesis-weight: none;
  /* every figure on the page stands on one vertical axis: the cheapest
     signal there is that this is a bookkeeper's page */
  font-variant-numeric: tabular-nums;
  text-rendering: optimizeLegibility;
}

/* One family for the whole site, three weights and no more: 400 for
   running text, 500 for emphasised meta, navigation, values and
   buttons, 650 for the h3 step and the wordmark. Not 600: a grotesque
   at 600 goes shapeless at display sizes, and the Plex axis runs to
   700. The headings also carry tighter tracking than the same step does
   in running text, which is what a display cut would otherwise do for
   us (operator decision, 25.08.2026). */
h1,
h2,
h3 {
  font-weight: 650;
  margin: 0;
  text-wrap: balance;
}

/* The h1 shares its step with the bold wordmark (Martin S2, spec E2):
   regular weight keeps the two apart, and the round-2b hairline 300
   would look thin next to a 650 name in the same size. */
h1 {
  font-size: var(--t8);
  font-weight: 400;
  line-height: 1.03;
  letter-spacing: var(--tr8);
}

h2 {
  font-size: var(--t7);
  font-weight: 450;
  line-height: 1.18;
  letter-spacing: var(--tr7);
}

h3 {
  font-size: var(--t6);
  letter-spacing: var(--tr6-title);
  line-height: 1.28;
}

/* The balance stroke under the footer number is gone since round 2b:
   the footer became a designed block on the deep anchor and the loose
   closing line under the phone number fell with it (trend report,
   measure 9). The title stroke on the stage remains. */

/* A visitor types the headline themselves, so a field can hold a pasted
   URL or 90 characters without a single break opportunity. anywhere, not
   break-word: only anywhere also lowers the min-content width, which is
   what keeps a grid or flex track from being pushed past the viewport. */
[data-edit] {
  overflow-wrap: anywhere;
}

p,
li,
dd {
  text-wrap: pretty;
  -webkit-hyphens: auto;
  hyphens: auto;
  hyphenate-limit-chars: 10 5 4;
  -webkit-hyphenate-limit-before: 5;
  -webkit-hyphenate-limit-after: 4;
  hyphenate-limit-lines: 2;
  -webkit-hyphenate-limit-lines: 2;
}

p {
  margin: 0 0 1.05em;
  max-inline-size: var(--measure);
}

a {
  color: inherit;
}

/* Martin's colour logic (F6) names links among the places the primary
   colour has to reach; until now only their underline carried it. Two
   kinds of link stay in ink on purpose: a service name is a heading that
   happens to link, and the map is a picture whose whole frame is the
   link. Everything else that reads as a link in running text takes the
   accent. */
.svc__more a,
.map__credit a,
.meta a,
.facts a,
.prose a,
.fold > summary {
  color: var(--seal);
}

picture {
  display: contents;
}

figure {
  margin: 0;
}

img {
  display: block;
  max-inline-size: 100%;
  height: auto;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

:focus-visible {
  outline: 2px solid var(--seal);
  outline-offset: 3px;
  border-radius: 1px;
}

/* A telephone number never breaks across two lines. */
.tel {
  white-space: nowrap;
}

.wrap {
  inline-size: 100%;
  max-inline-size: 74rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* One rubric style for the whole site. It used to be three, and until
   the parity round it was set in versals, which put it one step away
   from the eyebrow label the design rules forbid. Weight and grey do
   the same job without shouting: the site now carries no versals at
   all outside the tool layer. */
.label,
.facts dt,
.field label {
  font-size: var(--t2);
  font-weight: 500;
  letter-spacing: var(--tr2);
  line-height: 1.4;
  color: var(--ink-soft);
}

.label {
  margin-block: 0 0.35rem;
  /* wide tracking is reserved for short uppercase labels (design hook,
     31.08.): the eyebrows go caps, the rest of the t2 step stays 0.02em */
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.label + p {
  margin-block-end: 1.6rem;
}

.meta {
  font-family: var(--sans);
  font-size: var(--t2);
  line-height: 1.5;
  letter-spacing: var(--tr2);
  color: var(--ink-soft);
}

/* sr-only until focused: a 1px box, not an off-screen one, so the
   link never registers as page geometry while it is hidden */
.skip {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 60;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  padding: 0;
  background: var(--seal);
  color: var(--seal-ink);
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  text-decoration: none;
}

.skip:focus {
  inline-size: auto;
  block-size: auto;
  overflow: visible;
  clip-path: none;
  white-space: normal;
  padding: 0.75rem 1.25rem;
}

/* ---------- buttons ----------------------------------------- */

/* WCAG 2.5.8: keep small phone and mail targets at least 24px tall */
a[href^="tel:"]:not(.actionbar a),
a[href^="mailto:"]:not(.actionbar a) {
  display: inline-block;
  padding-block: 0.35rem;
}

/* Softly rounded and lifted a hair off the page, which is what a
   control looks like in 2026; the site's own flat, ruled surfaces stay
   square. The radius and the primary colour also keep the firm's
   buttons unmistakably apart from the tool layer's slate-grey pills,
   which are full circles and pill shapes. Weight 650 is the action
   bar's: the page's own action must not read lighter than the floating
   bar (critic round 3, owner lens). */
.btn {
  display: inline-block;
  padding: 0.95rem 1.7rem;
  min-block-size: 3rem;
  background: var(--seal);
  color: var(--seal-ink);
  font-family: var(--sans);
  font-size: var(--t4);
  font-weight: 650;
  letter-spacing: var(--tr4);
  /* one height for a.btn and button.btn: a link would inherit the body's
     1.62, a button gets the browser's normal (round 5 acceptance) */
  line-height: 1.35;
  text-decoration: none;
  border: 1px solid var(--seal);
  border-radius: var(--r-ui);
  box-shadow: 0 1px 2px var(--shade-near);
  cursor: pointer;
  transition: background-color 140ms var(--ease-out), border-color 140ms var(--ease-out),
    box-shadow 140ms var(--ease-out), translate 100ms var(--ease-out);
}

/* The primary gets darker under the pointer. It used to turn into the
   paper colour, which on a tinted band left a bright hole where the most
   important control had been. */
.btn:hover {
  background: var(--seal-deep);
  border-color: var(--seal-deep);
  color: var(--seal-ink);
  translate: 0 -1px;
  box-shadow: 0 2px 3px var(--shade-near), 0 6px 14px var(--shade-far);
}

/* Druckpunkt: der Knopf gibt unter dem Finger sichtbar nach */
.btn:active {
  translate: 0 1px;
  background: var(--seal);
  color: var(--seal-ink);
  box-shadow: 0 1px 2px var(--shade-near);
}

/* Done, not broken: after the preview form confirms, its button is
   locked, and the pale outline it used to take read as a defect rather
   than as a finished step (critic round 3, customer lens). It settles
   one shade deeper instead and keeps its white label. */
.btn:disabled {
  background: var(--seal-deep);
  border-color: var(--seal-deep);
  color: var(--seal-ink);
  cursor: default;
  translate: none;
  box-shadow: none;
}

/* Outlined secondary: the link into depth on the start page (round 5,
   issue 35) must not weigh as much as the form's send button below it. */
.btn--line {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
  box-shadow: none;
}

.btn--line:hover,
.btn--line:active {
  background: transparent;
  color: var(--seal);
  border-color: var(--seal);
  box-shadow: none;
}

/* narrow phones lose padding, not type size: the scale holds everywhere */
@media (max-width: 22.5em) {
  .btn {
    padding-inline: 1.05rem;
  }
}

/* ---------- masthead: wordmark and pages, static -------------- */

/* Round 3 (Martin A2): the head is the firm name and the pages,
   nothing else. The running head with telephone and appointment
   button, and with it the sticky, compacting strip of round 2b, is
   gone on every page; the contact lives in the permanent action bar
   (.actionbar). The head scrolls away like any other block. */

.masthead__name {
  /* end padding is smaller than it looks: the nav links below carry
     0.7rem of their own tap-target padding */
  padding-block: 0.9rem 0.4rem;
}

/* The firm name is the h1 step, bold (Martin S2, spec E2). Tight
   leading keeps the head compact now that the name is display size. */
.wordmark {
  display: inline-block;
  font-size: var(--t8);
  font-weight: 650;
  letter-spacing: var(--tr8);
  line-height: 1.1;
  text-decoration: none;
}

/* The three page links: sans, step t4, weight 500, full ink; the same
   voice as the action bar, which sets its labels bold (Martin S3, S11).
   The old dress (t1 versals, ink-soft, tracked) was the rubric style of
   the page labels, so the navigation read as inert labelling and was
   the palest thing in the head (operator finding, acceptance round 1).
   The vertical padding makes the real link box 44px or more, no
   invisible hit-area extension needed. */
/* The nav carries the popover attribute for the phone panel (issue 63),
   and with it the user agent's popover dress: fixed, inset 0, fit-content,
   auto margins, a solid border and a quarter em of padding. All of it is
   taken back here, so the head above 28em is exactly the row it was; the
   open panel re-establishes what it needs, at a higher specificity. */
.masthead__nav {
  margin-block-start: 0.15rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0 2rem;
  position: static;
  inset: auto;
  inline-size: auto;
  block-size: auto;
  margin-inline: 0;
  border: 0;
  padding: 0;
  overflow: visible;
  background: transparent;
  color: inherit;
}

.masthead__nav a {
  font-family: var(--sans);
  font-size: var(--t4);
  font-weight: 500;
  letter-spacing: var(--tr4);
  color: var(--ink);
  text-decoration: none;
  padding-block: 0.7rem;
  border-block-end: 2px solid transparent;
}

.masthead__nav a:hover {
  color: var(--seal);
  border-block-end-color: var(--rule);
}

/* The one exception, and it is a step of the same scale, not a new
   size: at 320 the firm name and the menu button do not share a row at
   the h1 step (295.4px of content against 280px of column), so below
   21.5em the name drops to the step below. Measured 24.09.2026. */
@media (max-width: 21.5em) {
  .wordmark {
    font-size: var(--t7);
    letter-spacing: var(--tr7);
  }
}


.masthead__nav a[aria-current="page"] {
  border-block-end-color: var(--seal);
}

/* ---------- stage: place picture with the title on it -------- */

/* The picture is the first thing on the page and the title lies on it,
   on a sheet of paper. Both live in the same grid row, so nothing moves
   after the first paint. The middle column repeats what .wrap does, so
   the card lines up with every text edge below it. */
.stage {
  display: grid;
  grid-template-columns:
    minmax(var(--gutter), 1fr)
    minmax(0, calc(74rem - 2 * var(--gutter)))
    minmax(var(--gutter), 1fr);
}

/* Two rows of its own, so the picture takes whatever the tool layer's
   button row leaves and the aspect-ratio on the img still supplies the
   natural height. When a long title makes the card taller than the
   picture, the grid row grows and object-fit keeps the frame covered. */
.stage > .band--place {
  grid-area: 1 / 1 / 2 / -1;
  display: grid;
  grid-template-rows: auto 1fr;
}

.stage .band--place picture {
  display: block;
  min-block-size: 0;
}

.stage .band--place img {
  block-size: 100%;
}

/* The title lies on the photograph itself, bottom left, carried by a
   scrim: a two-stop wash of the palette's deep anchor that fades out before
   it reaches the top of the frame. The picture stays whole, which is the
   whole point; an opaque band read as a box under the picture instead of
   as a title on it (operator, 26.08.). The gradient sits under the text,
   never on it, so the ban on gradients over type is untouched. */
.stage__card {
  grid-area: 1 / 1 / 2 / -1;
  z-index: 2;
  align-self: end;
  display: grid;
  grid-template-columns: inherit;
  /* the top padding is what keeps the type inside the opaque part of
     the wash: below 5.6rem the first title line rides up into the fade
     and lands on bright roofs (measured 26.08.) */
  padding-block: clamp(5.6rem, 23vw, 10rem) clamp(1.3rem, 2.8vw, 1.9rem);
  /* the palette's white on the deep wash ("Blau / Weiss" literally),
     not the paper: in the brown world the cream paper reads as a colour */
  color: var(--surface);
  /* 92, not 86: the regular-weight title and the h2-step line under it
     need a dense ground to hold 4.5:1 on the bright roofs (round 2b, 3) */
  background: linear-gradient(
    to top,
    color-mix(in oklch, var(--deep) 92%, transparent) 62%,
    transparent
  );
}

.stage__card > * {
  grid-column: 2;
}

.stage__card h1 {
  line-height: 1.05;
}

/* the sentence under the title, on the same sheet (Martin S5: the h2
   step, so also the h2 weight); the wash below it has to carry 4.5:1 */
.stage__card .hero__values {
  margin: 0.75rem 0 0;
  max-inline-size: none;
  color: var(--surface);
  font-size: var(--t7);
  font-weight: 450;
  letter-spacing: var(--tr7);
  /* 1.35 keeps the line box inside the card: at 1.5 it overhung the
     bottom edge by 6 to 8px, waiting for a descender to slip onto the
     brighter ground below (verifier finding, 30.08.) */
  line-height: 1.35;
}

/* the balance stroke signs the title in the light accent */
.stage__card h1::after {
  content: "";
  display: block;
  inline-size: 2.75rem;
  block-size: 3px;
  margin-block-start: 0.9rem;
  border-block-start: 2px solid var(--seal-on-deep);
  border-block-end: 1px solid var(--surface);
  transform-origin: left;
}

/* ---- 2. load-in, CSS only: it runs without JavaScript and, under
   prefers-reduced-motion, the global 0.01ms override lands it straight
   on its finished state. Vertical, which is this site's motion axis. -- */
@keyframes rise {
  from {
    opacity: 0;
    translate: 0 24px;
  }
}

@keyframes draw {
  from {
    transform: scaleX(0);
  }
}

.stage__card h1 > span {
  display: block;
  animation: rise 600ms var(--ease-out) both;
}

.stage__card .hero__values {
  animation: rise 600ms var(--ease-out) 120ms both;
}

.stage__card h1::after {
  animation: draw 500ms var(--ease-out) 350ms both;
}

/* ---------- page heads: the same stage, one step quieter ------ */

/* Round 8 (Martin, issues 64 and 66): the three subpages opened with a
   solid deep bar and, under it, a strip of photograph 240px high, which
   is 6:1 at 1440 and 7.8:1 at 1870, cut from a file only 1400px wide.
   Martin read the bar as nineteen-nineties and the strip as too extreme
   a panorama at too low a resolution. The heads now use the start
   page's stage unchanged (decisions 71 and 85: the title lies free on
   the photograph over a two-stop scrim), so all four heads of the site
   share one geometry: 1400:538 from 40em, and 3:2 below it rather than
   the hero's 4:3, because a page head carries three lines of type and
   must not push the first section further down a phone than the start
   page does. */
@media (max-width: 39.99em) {
  .stage--page .band--place img {
    aspect-ratio: 3 / 2;
  }
}

/* Rubric, title and lead where the hero has a title and one line, so
   the type needs more of the frame and the wash has to reach higher.
   Every line was measured at the rendered pixel, against the brightest
   two percent of the photograph behind it: 6.2:1 in the worst case
   (the rubric at 320), 12.8:1 in the best. */
.stage--page .stage__card {
  /* The upper reserve is what lets the photograph breathe: the wash is
     tied to the card, so the part of the card above the first line is
     the only place the motif can show through. At 17vw the card was the
     whole head on a phone and the head read as a dark gradient rather
     than a picture (Martin on river-plain, 24.09.). At 30vw a third of
     the card, 115 to 120px, is fade with the motif behind it, while the
     desktop keeps the 8.5rem it had. */
  padding-block: clamp(5.5rem, 30vw, 8.5rem) clamp(1.3rem, 2.8vw, 1.9rem);
  /* Four stops, not the hero's two: the rubric sits at about 78 percent
     of the card height on a phone, and a single ramp from 58 percent
     left it at half cover and 3:1 (measured 24.09.). The slope rises
     step by step instead of breaking once, so the wash has no visible
     edge in the picture. */
  background: linear-gradient(
    to top,
    color-mix(in oklch, var(--deep) 94%, transparent) 68%,
    color-mix(in oklch, var(--deep) 68%, transparent) 80%,
    color-mix(in oklch, var(--deep) 22%, transparent) 91%,
    transparent
  );
}

.stage--page h1 {
  max-inline-size: 22ch;
}

/* The rubric is the topmost line and sits where the wash is already
   thinning, so it takes the full white of the palette rather than the
   muted pair the same rubric uses on the solid deep band: at
   --deep-soft it measured 2.7:1 against the brightest two percent
   behind it at 320 (24.09.). Its subordination is carried by the step,
   the tracking and the capitals, which is enough. */
.stage--page .label {
  color: var(--surface);
  /* the same load-in as the title, this site's axis is vertical */
  animation: rise 600ms var(--ease-out) both;
}

.stage--page .lede {
  margin-block: 1.05rem 0;
  max-inline-size: 46rem;
  color: var(--surface);
  animation: rise 600ms var(--ease-out) 120ms both;
}

/* ---------- hero: the deep stage under the band ---------------- */

/* The description alone stands here since round 3 (Martin S7, S8: the
   action bar carries the contact). The lower padding is the bar's 48px
   plus 1.5rem, so the permanent bar never covers the text (S9). */
.hero {
  padding-block-start: clamp(2.1rem, 1.6rem + 1.7vw, 2.8rem);
  padding-block-end: calc(48px + 1.5rem);
}

/* the section after the stage keeps 0.6 of its upper rhythm: the
   stage's own reserve already carries the pause, and the two padded
   edges together would otherwise open an empty stretch (gaps sensor) */
.hero + .section {
  padding-block-start: calc(var(--rhythm) * 0.6);
}

/* running text (Martin S6, L6, U6, K6): the hero description is not a
   lede step of its own any more */
.hero__lede {
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  /* wide enough for Martin's one sentence on one line at 1440 (round 5,
     issue 34); phones wrap as before */
  max-inline-size: 60rem;
  margin: 0;
}

/* The gradation of the pictures is baked into the delivered files, not
   applied at runtime: seven motifs from seven sources were pulled onto one
   curve (mean luminance 97 to 124, mean saturation 0.28 to 0.36, hue 32
   to 43) on 19.08. The place band is the reference of the set and was
   left untouched; two motifs (paper-stack, archive-binders) were replaced
   the same evening and graded onto the same curve. See img/LICENSES.md for
   the recipe per motif.
   ----------------------------------------------------------- */

/* ---------- round 2b: the deep stage ------------------------- */

/* Two zones per page stand on the deep anchor: the stage under the
   head (the hero action zone on the start page, the page head behind
   its stagehead wrapper everywhere else) and the footer. Text on it
   uses the deep pair, the accent its lighter stop. */
.band--deep {
  background: var(--deep);
  color: var(--deep-ink);
}

.band--deep .label,
.band--deep .lede {
  color: var(--deep-soft);
}

.band--deep :focus-visible {
  outline-color: var(--seal-on-deep);
}

/* ---------- full bleed picture bands ------------------------ */

.band img {
  inline-size: 100%;
  object-fit: cover;
}

/* The place picture on the start page carries the "this is your village"
   message (meeting 17.08., decision 35), so it is the loudest picture of
   the site. Fixed proportion, not window height: with 34/44vh the same
   picture was 360px in one 1280px window and 616px in another (measured
   19.08.). Small screens get their own, more upright crop via <picture>;
   from 40em the full panorama shows at its native ratio, so no part of
   the frame is ever cropped away and the composition is nailed down. */
.band--place img {
  aspect-ratio: 4 / 3;
  block-size: auto;
  object-position: 50% 50%;
}

@media (min-width: 40em) {
  .band--place img {
    aspect-ratio: 1400 / 538;
  }
}

/* Desktop start page: a fixed 3:1 whatever the window height (Martin #105,
   decision 178). The page heads keep 1400:538, they carry three lines. */
@media (min-width: 62.5em) {
  .stage:not(.stage--page) .band--place img {
    aspect-ratio: 3 / 1;
  }
}

/* a little air above an anchor target */
[id] {
  scroll-margin-block-start: 1rem;
}

/* ... and below the docked bar on the desktop (3.75rem strip), even
   when the reveal lifts the target afterwards by up to 26px */
@media (min-width: 62.5em) {
  [id] {
    scroll-margin-block-start: 6.5rem;
  }
}

/* ---------- generic section --------------------------------- */

.section {
  padding-block: var(--rhythm);
}

.section--tint {
  background: var(--surface);
}

/* follows a page head that already carries the upper spacing */
.section--tight {
  /* the prose pages open straight with a heading; a small pause is
     enough after the head's own lower reserve */
  padding-block-start: 1.5rem;
}

.section > .wrap > h2 {
  margin-block-end: 1.75rem;
}

/* a lede is short enough to be balanced: without this the first line
   broke at half the measure while the second ran to the edge */
.lede,
.hero__lede {
  text-wrap: balance;
}

.lede {
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  max-inline-size: 44rem;
  color: var(--ink-soft);
}

.rule {
  border: 0;
  border-block-start: 1px solid var(--rule);
  margin: 0;
}

.rule--spaced {
  max-inline-size: 40rem;
  margin-block: 2.5rem 1.25rem;
}

/* ---------- owner portrait ---------------------------------- */

.owner {
  display: grid;
  gap: 2.25rem;
  align-items: start;
}

.owner figure {
  margin: 0;
}

.owner__title {
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  color: var(--ink-soft);
  margin-block: 0.5rem 1.5rem;
}

/* ---------- K1: services register --------------------------- */

/* Four ruled entries. Ordinal and name share one line and one step
   (Martin L17): the ordinal is a lighter span inside the heading, so
   the names line up on the tabular digits and no second size exists.
   The heading is an h2 (nothing but the eyebrow stands above the
   entries), set at the entry-name step. */
.register {
  border-block-start: 1px solid var(--rule);
}

/* column gap only: the tool's in-flow mark is a collapsed first grid
   row while editing is off, and a row gap would open above the name */
.register__row {
  position: relative;
  display: grid;
  column-gap: 2rem;
  /* the indent that houses the hover marker shrinks with the viewport so
     that the longest entry name still fits on one line at 320px (L17) */
  padding: 1.6rem 0 1.7rem clamp(1.15rem, 5.8vw, 1.85rem);
  border-block-end: 1px solid var(--rule);
}

/* the primary-coloured marker slides in front of the ordinal, nothing else moves */
.register__row::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 2.15rem;
  inline-size: 0.5rem;
  block-size: 0.5rem;
  background: var(--seal);
  opacity: 0;
  transform: translateX(-0.4rem);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.register__row:hover::before,
.register__row:focus-within::before {
  opacity: 1;
  transform: none;
}

.register__name {
  font-size: var(--t6);
  line-height: 1.2;
  font-weight: 650;
  letter-spacing: var(--tr6-title);
}

.register__no {
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  margin-inline-end: 0.15em;
}

.register__items {
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  line-height: 1.55;
  color: var(--ink-soft);
  column-gap: 2rem;
}

.register__items li {
  break-inside: avoid;
  margin-block-end: 0.3rem;
}

/* "Details anzeigen" answers the sentence, so it keeps only its own
   small margin above (the gap under the name is the edit zone's own
   heading rule) */
.register__text p {
  margin-block-end: 0;
}

.register__text .register__items {
  margin-block-start: 0.9rem;
}

/* Plus that loses its upright stroke when the fold opens, and takes
   the primary colour while it is open. One sign, two states, no arrow. */
.register__sign {
  position: relative;
  flex: none;
  inline-size: 0.8rem;
  block-size: 0.8rem;
}

.register__sign::before,
.register__sign::after {
  content: "";
  position: absolute;
  background: var(--ink-soft);
  transition: opacity 180ms var(--ease-out), background-color 180ms var(--ease-out);
}

.register__sign::before {
  inset-block-start: 50%;
  inset-inline: 0;
  block-size: 1px;
  translate: 0 -50%;
}

.register__sign::after {
  inset-inline-start: 50%;
  inset-block: 0;
  inline-size: 1px;
  translate: -50% 0;
}

/* ---------- facts strip ------------------------------------- */

/* Round 3 (Martin K24): the map stands in this block, so the block is
   map first, entries after, stacked on the phone and tablet and side by
   side from the desktop tier, where the map keeps its own pixel size. */
.facts {
  display: grid;
  gap: 2.5rem;
  align-items: start;
}

/* One column at every width, and it stays one. Two columns left the
   fourth quadrant empty and hung the closing rule of the right column a
   row higher; from 48em they also fell to 153 px and tore the entries
   into lines of 12 to 18 characters (measured 19.08.). Set as a stack of
   ruled rows, each entry reads on one line and the block closes flat,
   which is also the idiom of the register above it. */
.facts dl {
  margin: 0;
  display: grid;
  gap: 0;
}

/* Stacked under the map (phone and tablet) the entries would otherwise
   drag their rules across the whole column for two words of value; the
   same measure as the form keeps the block dense. In the desktop grid
   the column is narrower than this anyway. */
.facts__list {
  max-inline-size: 30rem;
}

.facts__item {
  border-block-start: 1px solid var(--rule);
  padding-block: 1.05rem;
}

.facts__item:last-child {
  border-block-end: 1px solid var(--rule);
}

.facts dd {
  margin: 0.25rem 0 0;
  font-size: var(--t4);
  font-weight: 500;
  letter-spacing: var(--tr4);
  line-height: 1.4;
}

/* ---------- process, three sentences, typography only ------- */

/* Martin writes the steps as "1. Kontakt" (S25): the ordinal is part of
   the title text and shares its step, no display figure beside it. */
.steps {
  display: grid;
  gap: 2.25rem;
}

.steps li {
  max-inline-size: 44rem;
}

.steps li h3 {
  margin-block-end: 0.3rem;
}

.steps li p {
  margin: 0;
}

/* ---------- contact and form -------------------------------- */

.form {
  display: grid;
  gap: 1.15rem;
  max-inline-size: 30rem;
}

.field label {
  display: block;
  margin-block-end: 0.4rem;
}

/* A box, not a baseline rule: the bare underline is the 2016 form and
   reads as decoration rather than as something to type in. The 1px
   ink-soft edge clears the 3:1 control contrast on the light surface
   (6.3:1 and better in all three colour worlds, measured 01.09.). */
.field input,
.field textarea {
  inline-size: 100%;
  font-family: var(--sans);
  font-size: var(--t4);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--ink-soft);
  border-radius: var(--r-ui);
  padding: 0.6rem 0.75rem;
  min-block-size: 44px;
}

/* Compact and growing: field-sizing lets the box follow the typed
   text, so the empty form does not hold a six-line hole open. Engines
   without it keep the 4.5rem floor and the resize handle.
   Round 3 polish: three lines would read as the friendlier invitation
   for a trust enquiry (critic, owner lens), but the gaps sensor counts
   an empty field as a vertical hole and allows 77px at 390 where three
   lines need 108. The floor stays at two, the field still grows. */
.field textarea {
  resize: vertical;
  min-block-size: 4.5rem;
  field-sizing: content;
  line-height: 1.5;
}

.field input:focus,
.field textarea:focus {
  border-color: var(--seal);
}

/* ---------- team -------------------------------------------- */

/* Any headcount from one to about six (Martin U18, U20) out of one
   track rule: the tracks are laid, not counted, so a card keeps its
   size whatever the list holds: one person gets the same card as six,
   and none of them ever blows up to the full column. On the phone the
   track minimum decides, so two portraits stand beside each other from
   a 19rem column on, which is every phone from 344px up (U23) and one
   card below that; from the tablet tier the count is fixed, two then
   three, so six people read as two calm rows and never as a wall. */
.team {
  display: grid;
  gap: 1.75rem 1rem;
  /* auto-fill, deliberately: with a single person the second track
     stays laid but empty, which keeps the lone card at half the phone
     width. auto-fit lets it fill the whole column and pushes it to 64
     percent of the viewport height, past Martin's 60 (U23, measured
     01.09.). From the tablet tier the count rules below take over and
     the empty track is gone. */
  grid-template-columns: repeat(auto-fill, minmax(min(9rem, 100%), 1fr));
}

.team figure {
  margin: 0;
}

.team h3 {
  margin-block-end: 0.2rem;
}

.team__role,
.team__field {
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  margin-block: 0 0.6rem;
}

.team__field {
  color: var(--ink-soft);
}

/* ---------- location map (kontakt) --------------------------
   A real OpenStreetMap excerpt of the Freiamt, rendered once at build
   time and served from our own machine: nothing is fetched at runtime.
   Two crops, one per viewport class, both centred on the pin, both at
   roughly 1:1 image pixel to CSS pixel so the map lettering stays
   readable. Pin, address flag and attribution are HTML on top of the
   picture, so they stay sharp and keep the 11px floor at every width.
   ----------------------------------------------------------- */

.map {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 0.6rem;
}

/* the frame, not the figure, is the positioning context: the caption
   below must stay clear of the pin and the attribution */
.map__frame {
  position: relative;
}

.map__link {
  display: block;
  line-height: 0;
}

/* The map is a picture too. Without the envelope its meadow green and
   ochre were the only saturated field on a page of blue and paper
   (critic round 3, owner lens); at 0.62 it sits in the same saturation
   band as the place bands, and pin, flag and attribution are HTML on
   top and stay untouched. */
.map__link img {
  inline-size: 100%;
  block-size: auto;
  filter: saturate(0.62) contrast(1.02);
}

/* both crops are centred on the office, so one position serves both */
.map__pin {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: 22px;
  block-size: 30px;
  translate: -50% -100%;
}

/* ODbL: the source has to be named wherever the map is shown -- beside
   the picture is enough, it need not lie in it. Laid over the excerpt it
   covered two thirds of the map on a phone and hid the streets it names
   (critic round 3, customer lens); under the frame, inside the same
   figure, it keeps the attribution and gives the map back. */
.map__credit {
  margin: 0.4rem 0 0;
  text-align: end;
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: var(--t2);
  letter-spacing: var(--tr2);
  line-height: 1.5;
}

.map__credit a {
  display: inline-block;
  padding-block: 0.28rem;
  text-underline-offset: var(--ul-text);
}

/* ---------- footer ------------------------------------------ */

/* Round 2b: the footer is a designed block on the deep anchor, three
   columns under the firm name: address, phone, pages; the legal row
   closes over a rule in the deep tone. Round 3 (Martin S29, S30): the
   name is the entry-name step, bold, address and phone are one running
   line (round 5, issue 38: the phone is plain text, the action bar
   already calls), the page links are navigation on the same top edge
   and step as the address, without underline (issue 36). The footer
   informs, it is not a second call to action.
   The reserve for the fixed action bar lives here, not on the body: a
   body reserve painted the page ground under the tinted footer, and
   that light edge glinted through beside the bar (fixed 30.08.2026).
   Bar height 48px plus 1rem clearance, on every viewport. */
.foot {
  background: var(--deep);
  color: var(--deep-ink);
  padding-block: var(--rhythm) calc(2.25rem + 48px + 1rem + env(safe-area-inset-bottom, 0px));
  margin-block-start: 0;
}

/* Below the desktop tier the pencil button (tool layer, 56px, lifted
   over the action bar) stands where the status line's link ends at 320
   and 360: 3rem more reserve, as on the sister site (decision 114). */
@media (max-width: 56.24em) {
  .foot {
    padding-block-end: calc(2.25rem + 48px + 1rem + 3rem + env(safe-area-inset-bottom, 0px));
  }
}

.foot__name {
  margin: 0 0 1.6rem;
  font-size: var(--t6);
  font-weight: 650;
  letter-spacing: var(--tr6-title);
  line-height: 1.28;
}

.foot__name strong {
  font-weight: inherit;
}

.foot__grid {
  display: grid;
  gap: 1.9rem;
  padding-block-end: 2.4rem;
}

.foot__addr {
  font-style: normal;
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  line-height: 1.5;
}

/* the pages live here now, not in the running head. Martin writes the
   row as one line separated by middots ("Leistungen / Ueber uns /
   Kontakt", final structures of all three subpages), so the separator is
   drawn rather than typed: it stays out of the link text and out of the
   accessibility tree. The 48px tap target comes from the link padding;
   the nav's negative margin takes that padding back out of the layout,
   so the link text starts on the address's top edge (issue 36). */
.foot__pages {
  display: flex;
  flex-wrap: wrap;
  align-content: start;
  gap: 0 1.05rem;
  margin-block: -0.7rem;
  font-family: var(--sans);
  font-size: var(--t4);
  letter-spacing: var(--tr4);
  line-height: 1.5;
}

.foot__pages a {
  display: inline-block;
  padding-block: 0.7rem;
  font-weight: 500;
  text-decoration: none;
}

.foot__pages a:hover {
  color: var(--seal-on-deep);
}

.foot__pages a + a::before {
  content: "\00B7";
  margin-inline-end: 1.05rem;
  color: var(--deep-soft);
}

/* Four pages (issue 40) do not fit one row on a phone: 291px of type in
   the head against 280 of column at 320. Below 28em both navigations
   sit two by two, and the foot's middot only between the two of a row. */
@media (max-width: 27.99em) {
  .masthead__nav,
  .foot__pages {
    display: grid;
    grid-template-columns: repeat(2, max-content);
  }

  .foot__pages a:nth-child(odd)::before {
    content: none;
  }
}

/* ---------- the pages behind three lines on a phone ----------- */

/* Round 8 (Martin, issue 63). Four entries measure 291px of type and
   need 96px of gaps, 387px in all, against 350px of column at 390, 320
   at 360 and 280 at 320 (measured 24.09.): one row is out at every
   phone width, at any gap. The two by two of decision 122 cost the head
   104px, so at 390 the page began 220px down and the title on the place
   picture, with the lead under it, fell past the fold of an ordinary
   phone. The four pages therefore move behind the three lines.

   The panel is a popover, which is why there is no script: the platform
   puts it in the top layer, so nothing in the page moves when it opens
   (no layout jump), closes it on Escape and on a tap beside it, and
   keeps aria-expanded on the button in step. An engine without popover
   support never enters this block, keeps the two by two above and never
   shows the button. The action bar at the foot is untouched. */
.burger {
  display: none;
}

@supports selector(:popover-open) {
  @media (max-width: 27.99em) {
    /* name and button on one line; the editing mark keeps a row of its
       own above them and collapses to nothing when the marks are off */
    .masthead__name {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: space-between;
      gap: 0 0.75rem;
    }

    .masthead__name > .pr-mark {
      flex-basis: 100%;
    }

    /* At 320 the name measures 246.6px at the h1 step and the button
       36.8px after its optical margin: 295.4px against 280px of column,
       so the button dropped onto a second row and the page began 160px
       down instead of 123 (Martin, 24.09.). Below 21.5em the name takes
       the next step of the scale, which is 24px there, and the row
       closes again. No new type size is invented for it: five steps
       stay five steps (decision 91). */
    .burger,
    .wordmark {
      flex: none;
    }

    .burger {
      display: flex;
      flex: none;
      align-items: center;
      justify-content: center;
      /* 48px square, and the optical margin takes the button's own air
         back out so the lines end on the text edge of the column */
      inline-size: 48px;
      block-size: 48px;
      margin-inline-end: -0.7rem;
      padding: 0;
      border: 0;
      border-radius: 6px;
      background: transparent;
      color: var(--ink);
      cursor: pointer;
    }

    .burger:hover {
      color: var(--seal);
    }

    .masthead__nav {
      display: none;
    }

    /* a sheet along the top edge of the screen, so it works wherever
       the reader has scrolled to */
    .masthead__nav:popover-open {
      display: grid;
      /* one page per row, against the two by two the fallback above
         lays for the same element */
      grid-template-columns: minmax(0, 1fr);
      gap: 0;
      position: fixed;
      inset: 0 0 auto 0;
      inline-size: auto;
      max-block-size: 100dvh;
      overflow-y: auto;
      margin: 0;
      padding: 0.5rem var(--gutter) 0.9rem;
      border: 0;
      border-block-end: 1px solid var(--rule);
      background: var(--paper);
      color: var(--ink);
    }

    .masthead__nav::backdrop {
      background: rgb(0 0 0 / 0.38);
    }

    .burger--close {
      justify-self: end;
    }

    .masthead__nav a {
      padding-block: 0.85rem;
      border-block-end: 1px solid var(--rule);
    }

    .masthead__nav a:last-child {
      border-block-end: 0;
    }

    .masthead__nav a[aria-current="page"] {
      color: var(--seal);
    }
  }
}

.foot__bottom {
  padding-block-start: 1.5rem;
  border-block-start: 1px solid var(--deep-rule);
}

/* Status line (Martin, 09.09.2026, decision 107): the preview notice
   closes the footer on every page, small print step; since round 5
   (issue 37) it is the only legal row, the Datenschutz link lives in the
   Impressum. Same line height as address and running text, so the rows
   of the footer share one rhythm. */
.foot__status {
  margin: 0;
  max-inline-size: 56ch;             /* under 80 characters a line at the small print step */
  font-family: var(--sans);
  font-size: var(--t2);
  letter-spacing: var(--tr2);
  line-height: 1.5;
  color: var(--deep-soft);
}

.foot__status a {
  display: inline-block;
  padding-block: 0.3rem;
  margin-block: -0.3rem;
  color: var(--deep-ink);
  text-underline-offset: var(--ul-text);
}

.foot__status a:hover {
  color: var(--seal-on-deep);
}

/* ---------- action bar, permanent on every viewport ----------- */

/* Martin (S10, S11): "Anrufen" and "Schreiben", always visible, the
   only contact controls outside the content. The bar sits on the very
   edge of the device. On a phone with a home indicator its lower part
   lay under the system bar, so the strip keeps the safe area free
   below its two targets, and the footer reserves the same distance. */
.actionbar {
  position: fixed;
  inset-inline: 0.75rem;
  inset-block-end: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  z-index: 40;
  display: grid;
  grid-template-columns: 1fr 1fr;
  overflow: hidden;
  background: var(--paper);
  border-radius: var(--r-ui);
  box-shadow: 0 8px 24px var(--shade-far);
}

/* the navigation's type, bold, both the same; 48px is the tap target
   and the whole bar height (Martin S11: compacter, previously 60px) */
.actionbar a {
  min-block-size: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--sans);
  font-size: var(--t4);
  font-weight: 650;
  line-height: 1.35;
  letter-spacing: var(--tr4);
  text-align: center;
  text-decoration: none;
  padding: 0.5rem;
}

.actionbar a + a {
  background: var(--seal);
  color: var(--seal-ink);
}

.actionbar a {
  transition: background-color 140ms var(--ease-out), translate 100ms var(--ease-out);
}

.actionbar a:active {
  translate: 0 1px;
}

/* From 1000px the bar sits top right instead of bottom centre (Martin,
   issue 39: at the foot it was in the way on the desktop), still fixed,
   so it runs along while scrolling. Right edge on the content edge of
   the head (the .wrap's inner edge, 74rem centred plus gutter), at
   scroll position 0 in the empty right half of the head, below the
   Pronta note (.pr-bar, 36px, not sticky). As wide as its two labels
   need. On the desktop the drop shadow made the bar read as a cookie
   banner or a chat widget floating over the page. A hairline in the
   palette's own rule colour keeps it legible without that idiom
   (critic round 3, customer lens); the phone keeps the shadow, where
   the bar really does sit on the device edge. Hairline OR elevation,
   never both: the pair is one of the measured signatures of generated
   interfaces (design hook, 02.09.2026). */
@media (min-width: 62.5em) {
  .actionbar {
    /* the content edge: the .wrap's inner edge, 74rem centred plus gutter */
    --edge: max(var(--gutter), calc((100vw - 74rem) / 2 + var(--gutter)));
    /* One box for both states, so the switch never counts as a layout
       shift: a strip across the top edge, 3.75rem tall, the two 48px
       buttons on the content edge. Resting (head in view) the strip is
       transparent and only the buttons show, moved down by transform
       into the empty half of the head; the strip lets clicks through. */
    inset-block: 0 auto;
    inset-inline: 0;
    block-size: 3.75rem;
    padding-inline-end: var(--edge);
    grid-template-columns: repeat(2, 13rem);
    justify-content: end;
    align-content: center;
    background: transparent;
    border-radius: 0;
    box-shadow: none;
    pointer-events: none;
    translate: 0 calc(4.8rem - 0.375rem);
    transition: translate 200ms var(--ease-out), background-color 200ms var(--ease-out),
      box-shadow 200ms var(--ease-out);
  }

  .actionbar a {
    pointer-events: auto;
  }

  /* the pair carries its own frame and corners now that the nav box is
     the strip. A hairline in the palette's own rule colour, not the
     phone's drop shadow: on the desktop the shadow made the bar read as
     a cookie banner or a chat widget (critic round 3, customer lens).
     Hairline OR elevation, never both (design hook, 02.09.2026). */
  .actionbar a:first-child {
    background: var(--paper);
    border-radius: var(--r-ui) 0 0 var(--r-ui);
    box-shadow: inset 0 0 0 1px var(--rule);
  }

  .actionbar a + a {
    border-radius: 0 var(--r-ui) var(--r-ui) 0;
  }

  /* Docked (site.js sets the class once the head has left the
     viewport): the strip turns paper with a hairline below and the
     buttons slide up into it. Column text runs under it like under
     any head, so the bar never sits on a line of text. */
  .actionbar.is-docked {
    translate: none;
    background: var(--paper);
    box-shadow: 0 1px 0 var(--rule);
    pointer-events: auto;
  }
}

/* ---------- article pages ----------------------------------- */

.pagehead {
  /* the lower reserve is the hero's (Martin L9, U9): bar height plus
     1.5rem, so the permanent bar never covers the lede */
  padding-block: 2.5rem calc(48px + 1.5rem);
}

/* The block after the deep head keeps 0.6 of its upper rhythm, for the
   same reason as the one after the stage: the head already carries the
   pause in its own lower reserve, and two padded edges together open an
   empty stretch (gaps sensor, 116px at 390). The prose pages set that
   padding to nought themselves and are left alone. */
.stagehead + .section:not(.section--tight),
.stage--page + .section:not(.section--tight) {
  padding-block-start: calc(var(--rhythm) * 0.6);
}

/* ... and the first heading of a prose page must not re-open the pause
   the section above it just closed (gaps sensor, 112px at 390) */
.prose > h2:first-child {
  margin-block-start: 0;
}

.pagehead .lede {
  margin-block-end: 0;
}

.pagehead h1 {
  max-inline-size: 22ch;
}

/* "Treuhandleistungen" is wider than the 320px column at the h1 step:
   a soft hyphen in the markup breaks it there, and from 340px hyphens:
   none keeps text-wrap: balance from taking that break where the whole
   word fits on its own line */
@media (min-width: 21.25em) {
  .pagehead h1,
  .stage--page h1 {
    hyphens: none;
  }
}

.pagehead p {
  margin-block-start: 1.1rem;
}

.prose h2 {
  margin-block: 2.5rem 0.9rem;
}

/* keeps the step every other h3 on the site has: at running text size the
   heading was told apart by its weight alone */
.prose h3 {
  margin-block: 1.8rem 0.5rem;
}

.prose p,
.prose li {
  max-inline-size: 40rem;
}

.prose ul {
  margin-block-end: 1.05em;
}

.prose li {
  padding-inline-start: 1.1rem;
  position: relative;
  margin-block-end: 0.35rem;
}

.prose li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.72em;
  inline-size: 0.32rem;
  block-size: 1px;
  background: var(--ink-soft);
}

/* =============================================================
   from 640px
   ============================================================= */

@media (min-width: 40em) {
  .register__items {
    columns: 2;
  }

  .team {
    gap: 2.5rem 2rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* =============================================================
   from 768px: tablet tier. Two columns where the single column
   would run too wide, but not yet the full twelve column grid.
   ============================================================= */

@media (min-width: 48em) {
  .owner {
    grid-template-columns: minmax(0, 19rem) 1fr;
    column-gap: 2.5rem;
  }
}

/* =============================================================
   from 900px: twelve column grid, desktop composition
   ============================================================= */

@media (min-width: 56.25em) {
  .owner {
    grid-template-columns: repeat(12, 1fr);
    column-gap: var(--colgap);
  }

  .owner figure {
    grid-column: 1 / span 6;
  }

  /* The large portrait (Martin U12) is taller than name, role and the
     short paragraph together. Centred, the text floated at 38 percent
     of the picture height with a hole above and below and read as
     unfinished (critic round 3, customer lens); aligned to the start it
     shares its top edge with the picture, the same move as the values
     beside the owner on the start page. */
  .owner__text {
    grid-column: 8 / span 5;
    align-self: start;
  }

  /* the same lead column as every other two-column block, and still
     wide enough that "04 Abschluss & Beratung" stays on one line
     (measured 254px at the entry step) */
  .register__row {
    grid-template-columns: minmax(0, var(--split)) 1.4fr;
    align-items: start;
    column-gap: var(--gutter);
    padding-block: 2.4rem 2.5rem;
  }

  .register__row::before {
    inset-block-start: 2.95rem;
  }

  .facts {
    grid-template-columns: repeat(12, 1fr);
    column-gap: var(--colgap);
    align-items: start;
  }

  /* eight columns is where the map excerpt is shown at about its own
     pixel size, which is what keeps its lettering readable */
  .facts .map {
    grid-column: 1 / span 8;
  }

  .facts__list {
    grid-column: 9 / span 4;
  }

  /* Three tracks from three people up. With one or two the grid lays
     only the tracks it uses: a third, empty cell beside two portraits
     read as a picture that had failed to load, not as white space
     (critic round 3, both lenses). The card keeps the same width in
     all three cases, so a firm with two people gets the same card as
     one with six. */
  .team {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .team:has(> :nth-child(2):last-child) {
    grid-template-columns: repeat(2, minmax(0, var(--split)));
  }

  .team:has(> :only-child) {
    grid-template-columns: minmax(0, var(--split));
  }

  /* address left, the four pages right at their own width: on twelve
     columns the pages (410px with their middots) wrapped below 940px
     once "Startseite" joined them (issue 40) */
  .foot__grid {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--colgap);
  }

  .pagehead {
    /* the lower value is the bar reserve itself (48px + 1.5rem), not a
       third on top of it: the wider gap read as a place something had
       been cut out of (critic round 3, owner lens) */
    padding-block: 3.75rem calc(48px + 1.5rem);
  }

}

/* =============================================================
   round 2 (30.08.2026): components for Martin's structure
   ============================================================= */

/* a heading inside an edit zone needs its own follow gap: the
   section rule only reaches direct children of the wrap */
.pr-zone > h2:not(:last-child) {
  margin-block-end: 0.9rem;
}

/* content blocks that follow their heading zone in the same wrap */
.wrap > .pr-zone + .trust,
.wrap > .pr-zone + .owner,
.wrap > .pr-zone + .team,
.wrap > .pr-zone + .facts {
  margin-block-start: 2.5rem;
}

/* a ruled list straight under its eyebrow (Martin S19, S20, L15, L16):
   closer than after a heading, the eyebrow is the list's title */
.wrap > .pr-zone + .svc,
.wrap > .pr-zone + .register {
  margin-block-start: 1.25rem;
}

/* The trust block of the start page (Martin S14 to S17, round 3):
   portrait with name and role under it, the three values as a ruled
   list. On a phone it stacks in Martin's order; from the tablet tier
   the person takes the left column and the values stand beside her,
   centred on her height, so the block never leaves half the row empty. */
.trust {
  display: grid;
  gap: 1.1rem 0;
}

.trust figure {
  max-inline-size: 22rem;
}

/* the role is the last line under the portrait here; its follow gap
   is for the about page, where a paragraph comes after it */
.trust .owner__title {
  margin-block-end: 0;
}

.trust .values {
  margin-block-start: 1.25rem;
}

@media (min-width: 48em) {
  .trust {
    grid-template-columns: minmax(0, var(--split)) minmax(0, 1fr);
    column-gap: var(--gutter);
  }

  .trust figure {
    grid-row: 1;
  }

  .trust__text {
    grid-row: 2;
  }

  /* start, not centre: the rule over "Persönlich" is the strongest
     alignment signal of the block and used to hang 85px below the top
     edge of the portrait, hitting nothing (critic round 3, both
     lenses). Now the two columns share one top edge. */
  .trust .values {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: start;
    margin-block-start: 0;
  }
}

/* three values, ruled like the site's other columns, no cards */
.values__grid {
  display: grid;
  gap: 1.75rem;
}

.values__grid > div {
  border-block-start: 1px solid var(--rule);
  padding-block-start: 1.1rem;
}

.values__grid h3 {
  margin-block-end: 0.35rem;
}

.values__grid p {
  margin: 0;
}

/* owner block: the name leads, the role under it */
.owner__name {
  margin: 0;
  font-size: var(--t6);
  font-weight: 650;
  letter-spacing: var(--tr6-title);
  line-height: 1.28;
}

/* services on the start page: a ruled list, title against sentence */
.svc {
  border-block-start: 1px solid var(--rule);
}

.svc__item {
  border-block-end: 1px solid var(--rule);
}

.svc__row {
  display: grid;
  gap: 0.35rem 2.5rem;
  padding-block: 1.35rem 1.5rem;
}

.svc__row p {
  margin: 0;
  max-inline-size: var(--measure);
}

.svc__name a {
  text-decoration: none;
}

.svc__name a:hover,
.svc__name a:focus-visible {
  color: var(--seal);
  text-decoration: underline;
  text-decoration-color: var(--seal);
  text-decoration-thickness: 2px;
  text-underline-offset: var(--ul-display);
}

.svc__more {
  margin-block-start: 1.75rem;
}

/* Martin S22 wants a link into depth, "no superfluous conversion CTA".
   As a filled primary it carried the same weight as the send button of
   the form below and gave the page two equal actions (critic round 3,
   customer lens). Running text, underlined, with the site's arrow. */
.svc__more a {
  font-family: var(--sans);
  font-size: var(--t4);
  font-weight: 500;
  letter-spacing: var(--tr4);
  text-underline-offset: var(--ul-text);
}

.svc__more a::after {
  content: " \2192";
  text-decoration: none;
}

.svc__more a:hover {
  color: var(--seal);
}

@media (min-width: 56.25em) {
  .svc__row {
    grid-template-columns: minmax(0, var(--split)) 1fr;
    column-gap: var(--gutter);
  }
}

/* the "Details anzeigen" fold inside a register row: body size with
   the register's plus sign, keyboard-native (Martin L22) */
/* Martin L22 asks for a functional link, and on a phone there is no
   hover to reveal one: the underline is the site's own link idiom and
   is the only signal at rest that the one way to what the firm actually
   does is a control (critic round 3, customer lens). Colour still stays
   out of the resting state, so the accent sensor holds. */
.fold > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin-block-start: 0.9rem;
  padding-block: 0.35rem;
  min-block-size: 24px;
  font-size: var(--t4);
  font-weight: 500;
  letter-spacing: var(--tr4);
  cursor: pointer;
  list-style: none;
}

.fold > summary .fold__closed,
.fold > summary .fold__open {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: var(--ul-text);
}

.fold > summary::-webkit-details-marker {
  display: none;
}

.fold > summary:hover {
  color: var(--seal);
}

.fold > summary:hover .register__sign::before,
.fold > summary:hover .register__sign::after {
  background: var(--seal);
}

.fold[open] > summary .register__sign::after {
  opacity: 0;
}

.fold[open] > summary .register__sign::before {
  background: var(--seal);
  block-size: 2px;
}

.fold__open {
  display: none;
}

.fold[open] > summary .fold__open {
  display: inline;
}

.fold[open] > summary .fold__closed {
  display: none;
}

.fold::details-content {
  block-size: 0;
  overflow: clip;
  transition: block-size 300ms var(--ease-out), content-visibility 300ms allow-discrete;
}

.fold[open]::details-content {
  block-size: auto;
}

/* team cards: picture, then name, role, optional field of work */
.team__meta {
  margin-block-start: 0.85rem;
}

.team__role {
  margin-block-end: 0.15rem;
}

.team__field {
  margin-block-end: 0;
}

/* the trust detail under the contact entries */
.facts__trust {
  margin: 1.5rem 0 0;
  max-inline-size: var(--measure);
}

/* the quiet privacy note */
.form__privacy {
  font-family: var(--sans);
  font-size: var(--t2);
  letter-spacing: var(--tr2);
  line-height: 1.5;
  color: var(--ink-soft);
  margin: 0;
  max-inline-size: 30rem;
}

/* =============================================================
   round 2b (30.08.2026): modernisation pass, visual and motion
   ============================================================= */

/* A half-empty section head becomes two columns from the desktop tier:
   rubric and heading left, the section's content right, so the right
   half of the twelve column grid stops standing empty (trend report,
   measure 5). */
.zone-split__body {
  margin-block-start: 1.35rem;
}

@media (min-width: 56.25em) {
  .zone-split {
    display: grid;
    grid-template-columns: minmax(0, var(--split)) minmax(0, 1fr);
    column-gap: var(--gutter);
    align-items: start;
  }

  .zone-split__body {
    margin-block-start: 0.4rem;
  }

  /* Where Martin took the h2 away the head holds nothing but its
     rubric: 25px of type beside a 253px column left a 24rem hole in the
     grid, while the services section two blocks up solved the same case
     over the full width (critic round 3, owner lens). One rule, and
     every headless section falls back to that same full width. */
  .zone-split:not(:has(h2)) {
    display: block;
  }

  .zone-split:not(:has(h2)) .zone-split__body {
    margin-block-start: 1.35rem;
  }
}

/* One tonal envelope over every photographic motif (not the pale
   symbolic portraits, not the map), so stock from mixed sources reads as
   one shoot: slight desaturation and a touch of contrast. The extra
   layer of paper that used to be multiplied into the narrow strips went
   with the strips in round 8; all four heads now carry the same
   envelope as the start page's own. */
.band img {
  filter: saturate(0.82) contrast(1.05);
}

/* Scroll progress: a primary-coloured hairline along the top edge, driven
   by the scroll timeline alone. Engines without scroll-driven
   animations never display it, reduced motion hides it outright, and
   no script is involved either way. */
.progress {
  display: none;
}

@supports (animation-timeline: scroll()) {
  .progress {
    display: block;
    position: fixed;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: 2px;
    z-index: 55;
    background: var(--seal);
    transform-origin: 0 50%;
    transform: scaleX(0);
    animation: fill-progress linear both;
    animation-timeline: scroll(root);
    pointer-events: none;
  }

  @keyframes fill-progress {
    to {
      transform: scaleX(1);
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  .progress {
    display: none;
  }
}
