/* =========================================================================
   Felix M. Schönberger — public website
   Editorial / anthracite. Plain CSS, no build. See CLAUDE.md + MASTER-PLAN.md.
   Typography is the primary visual element. One restrained accent. One
   orchestrated motion moment. No per-section animation.
   Fonts: self-hosted woff2 via assets/css/fonts.css (Fraunces + Inter, variable).
   ========================================================================= */

:root {
  /* Neutrals — warm off-white → warm earth-dark (2026-09-26: replaced the
     former near-black --anthracite #23262A across the whole dark-neutral
     family - --ink/--ink-2/--nav-active shifted warmer too, not just the
     structural-background token, per Felix's brief. --earth-dark's value
     was chosen by rendering #4A433D against three neighbouring candidates
     (#48413B/#4C453F/#504841) as flat swatches next to --paper and
     eyeballing warmth/legibility, not picked from the hex alone - all
     four cleared >8:1 contrast against --paper, so the choice was purely
     visual. Deliberately a SMALL family (--ink/--ink-2/--earth-dark/
     --earth-hover only) - --nav-ink/--nav-dimmed/--muted/--on-dark-mut
     were already warm-leaning and are left as-is, per the brief's own
     explicit warning against turning every neutral in the site into a
     slightly different brown. */
  --paper:      #F6F5F2;
  --paper-2:    #EEEBE5;
  --card:       #FFFFFF;
  --ink:        #201D1A;   /* was #191A1A (neutral near-black) - warm undertone, still reads as near-black in running text */
  --ink-2:      #2E2A26;   /* was #26282A (slightly cool/blue-leaning) - now warm, matching --ink's family */
  --muted:      #5F5E59;   /* ~5.3:1 on --paper */
  --line:       #D4D1CA;
  --line-soft:  #E4E1DA;
  --earth-dark: #4A433D;   /* was --anthracite #23262A - see family note above */
  --earth-hover:#3A342F;   /* one step deeper than --earth-dark, for button hover only - was a hardcoded #101214 near-black with no token at all */
  --nav-ink:    #4B4A46;   /* nav's resting tone - a step lighter than --earth-dark/--nav-active, so brand and navigation read as two different weights of dark rather than one flat black. Already warm-leaning; left unchanged. */
  --nav-active: #4A433D;   /* explicit hover/focus/interaction-active tone for grouped nav links - same value as --earth-dark (brand), deliberately darker than --nav-ink's resting tone */
  --nav-dimmed: #6B6660;   /* grouped-link sibling-dimming tone - visibly lighter than --nav-ink's resting tone, not the same value, so the interaction reads as a real colour change. ~4.78:1 against --paper-2 (WCAG AA for normal-size text); the original #A09D95 measured ~2.28:1, confirmed by direct computation and fixed 2026-09-25 */
  --on-dark:    #E9E8E3;
  --on-dark-mut:#BAB9B3;   /* was #A6A59F, tuned for the old, darker --anthracite (~5.6:1 there) -
                              only ~3.93:1 against the new, lighter --earth-dark, failing WCAG AA
                              for the normal-size text it's actually used on (.eyebrow inside
                              .section--dark, .site-footer__legal) - lightened to restore ~4.94:1,
                              caught by an explicit contrast pass across every token pairing, not
                              assumed safe from the family shift alone */

  /* One restrained accent — deep pine-slate. Reads as ink with a hint of green.
     Tunable: if it ever reads sage/wellness, cut chroma (toward #363B38), never
     toward copper/terracotta. Used only for: nav underline, focus ring, the one
     section rule, link underline on hover. Never on buttons. */
  --accent:     #34423C;
  --accent-ink: #2A3630;
  --accent-soft:#E8ECE8;

  --danger:     #99342B;

  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --s1: .5rem; --s2: 1rem; --s3: 1.5rem; --s4: 2rem;
  --s5: 3rem;  --s6: 4.5rem; --s7: 6.5rem; --s8: 9rem;

  --radius: 2px;
  --maxw: 1200px;
  --measure: 34rem;         /* body text */
  --measure-serif: 30rem;   /* serif lede — shorter */
  --header-h: 74px;
  --gutter: clamp(1.25rem, 5vw, 5.5rem);

  --ease: cubic-bezier(.2,.6,.35,1);

  /* Sitewide type scale (2026-09-27, consolidated per Felix's explicit
     "one consistent size/system per role, no random page-specific sizes"
     brief). Named tokens for the roles that already existed as scattered
     rules across the file - documents the system rather than changing
     it, except for the one genuine outlier found during the audit
     (.legal h2's flat, non-responsive 1.3rem, fixed below to a real
     token). Two deliberate, structurally-justified tiers remain, now
     named instead of implicit: --text-h1-hero (large, used inside
     .hero - every content page's own headline, all consistent with each
     other) vs. --text-h1 (smaller, used for plain in-page h1s like legal
     pages' "Datenschutzerklärung" - a document label, not a hero
     headline). Same content-role always gets the same token everywhere;
     nothing here is a one-off per-page value. */
  --text-h1-hero: clamp(2.15rem, .6rem + 7.4vw, 7rem);
  --text-h1: clamp(2.5rem, 1rem + 6.5vw, 5rem);
  --text-h2: clamp(1.8rem, 1.1rem + 2.8vw, 3rem);
  --text-h2-compact: clamp(1.15rem, 1rem + .8vw, 1.3rem); /* dense reference sections (legal-page subsections) - same fluid system, deliberately smaller role */
  --text-h3: clamp(1.1rem, 1rem + .5vw, 1.3rem);
  --text-body: clamp(1rem, .97rem + .18vw, 1.09rem); /* matches body{}'s own value below - one source of truth */

  /* Brand-label voice (2026-09-27, formalized per Felix's explicit
     follow-up brief) - the small uppercase/tracked typography of the
     logo's own "PSYCHOLOGISCHE BERATUNG"/"PSYCHOLOGICAL COUNSELLING"
     subline. That subline (.brand__word in the header) is real HTML
     text, not an image, SVG or converted path - inspected directly
     rather than assumed, so these tokens are copied verbatim from its
     own long-settled values, not reverse-engineered or approximated.
     .eyebrow and .brand__word both now read from these tokens instead
     of each hardcoding its own copy of the same three values, so the
     two can never drift apart again. Font-size is deliberately NOT
     tokenized here - .brand__word (.66rem, wordmark-scale) and .eyebrow
     (.74rem, section-label-scale) are different roles at a similar but
     not identical size, and forcing one size on both would make one of
     the two look wrong for its context. */
  --brand-label-font: var(--font-body);
  --brand-label-weight: 400;
  --brand-label-tracking: .13em;
  --brand-label-tracking-tight: .1em; /* narrow-viewport variant, already used by .eyebrow below 480px */
}

/* ----- Reset ----- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 20px); }
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-body); line-height: 1.62;
  -webkit-font-smoothing: antialiased; font-synthesis: none;
  font-optical-sizing: auto; text-rendering: optimizeLegibility;
}
/* Destination-page entrance fade (2026-09-25) - only active when the
   <html> element carries this class, which a tiny blocking inline
   script in <head> (de/index.html, en/index.html,
   de/einfache-sprache.html only) adds by reading a one-time
   sessionStorage flag the gateway's own transition leaves behind before
   navigating (assets/js/main.js triggers the reveal, two rAFs after
   load so the browser has actually painted the hidden state first - see
   that file). Every other visit - direct load, reload, back/forward,
   any page without that inline script - never gets the class, so
   nothing here ever applies; this is strictly additive, not a change to
   normal page load. */
html.is-gateway-arrival body { opacity: 0; transform: translateY(4px); }
html.is-gateway-arrival body.is-gateway-revealed {
  opacity: 1; transform: translateY(0);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  html.is-gateway-arrival body { opacity: 1; transform: none; }
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--ink); text-decoration-thickness: 1px; text-underline-offset: .18em; }
a:hover { color: var(--accent-ink); }

h1, h2, h3, h4 {
  font-family: var(--font-display); color: var(--ink-2);
  font-weight: 440; line-height: 1.06; letter-spacing: -0.02em;
  margin: 0 0 .5em; font-optical-sizing: auto; text-wrap: balance;
}
h1 { font-size: var(--text-h1); font-weight: 360; line-height: 1.0; letter-spacing: -0.032em; }
h2 { font-size: var(--text-h2); font-weight: 400; letter-spacing: -0.026em; }
h3 { font-size: var(--text-h3); font-weight: 500; letter-spacing: -0.012em; }
p { margin: 0 0 var(--s3); }
p:last-child { margin-bottom: 0; }
strong { font-weight: 600; }
ul, ol { margin: 0 0 var(--s3); padding-left: 1.15em; }
li { margin: .35em 0; }
hr { border: 0; border-top: 1px solid var(--line); margin: var(--s5) 0; }
::selection { background: var(--earth-dark); color: var(--paper); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }
a:focus-visible { background: var(--accent-soft); outline-offset: 2px; }
.section--dark :focus-visible { outline-color: var(--on-dark); }
.section--dark a:focus-visible { background: rgba(233,232,227,.16); }

.skip-link {
  position: absolute; left: var(--s2); top: -60px; z-index: 100;
  background: var(--earth-dark); color: var(--on-dark); padding: .7rem 1.1rem; border-radius: var(--radius);
  transition: top .16s var(--ease);
}
.skip-link:focus { top: var(--s2); }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  /* clip-path replaces the deprecated clip:rect() (2026-09-26, QA pass) -
     same screen-reader-only effect, universally supported by evergreen
     browsers, no stylelint property-no-deprecated finding. */
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ----- Layout ----- */
.wrap { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
/* Sitewide section rhythm, raised 2026-09-25 per Felix's feedback that
   consecutive sections read as too tight, especially below ~1600px where
   the vw component hadn't yet caught up to the old --s5 floor. Same
   clamp() expression as the --ground boundary case below, kept in sync -
   floor raised --s5->--s6 and the vw coefficient bumped 9->10, so every
   page gets more air between sections without touching the shared --s*
   scale itself (only ≥1600px, already at the --s8 ceiling either way, is
   unaffected). */
.section { padding-block: clamp(var(--s6), 10vw, var(--s8)); }
.section + .section { padding-top: 0; }
/* The About page's membership-logos section is much shorter content (one
   row of small grayscale marks) than the standard .section padding-block
   was tuned for - at the default rhythm it left a visibly oversized empty
   band before the closing dark CTA. Scoped to just this section, on both
   the DE and EN About pages, rather than changing the sitewide token. */
#mitgliedschaften, #memberships { padding-bottom: clamp(var(--s4), 5vw, var(--s6)); }
/* Homepage (DE+EN) closing-process section, same problem/fix as the
   About-page membership section above: the "Wie eine Zusammenarbeit
   beginnt" / "How a collaboration begins" section's own trailing note +
   "Ablauf und Kosten"/"Process and fees" link read as a short, orphaned
   tail against the sitewide section padding-bottom, leaving a visibly
   oversized pale band before the dark closing CTA (2026-09-26,
   Felix's explicit brief). Scoped to this section's own id, not the
   shared .section rule. The trailing note+link are also centered here
   (they read as left-aligned stray lines otherwise, against the
   section's own centered heading/lede above) - own rule below, not a
   change to .form__note or .link-arrow generally. */
#wie-eine-zusammenarbeit-beginnt, #how-a-collaboration-begins {
  padding-bottom: clamp(var(--s4), 5vw, var(--s6));
}
#wie-eine-zusammenarbeit-beginnt > .wrap > .form__note,
#how-a-collaboration-begins > .wrap > .form__note,
#wie-eine-zusammenarbeit-beginnt > .wrap > p:last-child,
#how-a-collaboration-begins > .wrap > p:last-child {
  text-align: center;
}
/* About page (DE+EN), local spacing harmonization after removing the
   video section (2026-09-25): the video used to sit between the
   portrait row and "Qualifikation und Erfahrung", and again between
   that section and the video's own bottom margin before Mitgliedschaften
   - removing it let both of those boundaries fall back to the sitewide
   --ground-transition rule's FULL padding on both sides at once (a much
   bigger gap than either section was designed to sit at alone). Scoped
   to these ids, which only exist on the About pages, not a change to the
   shared .section/.section--ground rules themselves. ID selectors (not
   .section--botanical--about, which would tie the sitewide ground-
   boundary rule's own specificity and could lose on source order) so
   this reliably wins regardless of where it sits in the file. */
#qualifikation-erfahrung,
#mitgliedschaften, #memberships {
  padding-top: clamp(var(--s4), 6vw, var(--s6));
}
#mitgliedschaften .affiliations, #memberships .affiliations {
  gap: var(--s3) var(--s4);
}
.section--ground { background: var(--paper-2); }
.section--ground + .section,
.section + .section--ground { padding-top: clamp(var(--s6), 10vw, var(--s8)); }
/* The final-CTA dark section is a distinct, high-contrast component that
   should never visually merge with whatever precedes it - unlike the
   zero-padding rule above (meant for two plain sections stacking directly).
   Without this, its padding-top depended on an incidental accident of
   markup (whether a --ground section happened to precede it), producing a
   measured 0px/144px top/bottom asymmetry on pages where it didn't, versus
   a symmetric 144px/144px where it did. Pin padding-top explicitly so it's
   always symmetric with the base .section padding-block, everywhere. */
.section + .section--dark { padding-top: clamp(var(--s6), 10vw, var(--s8)); }
.section--dark { background: var(--earth-dark); color: var(--on-dark); position: relative; overflow: hidden; }
.section--dark > .wrap { position: relative; z-index: 1; }
/* The final-CTA pattern (.section--dark > .wrap.prose: h2 + p + button)
   otherwise inherits .prose's 34rem measure, which is meant for running
   body text — on a short headline + button it collapses into a narrow,
   center-floating column instead of sitting in the site's normal
   left-aligned content grid. Widen the container back to the shared
   --maxw here specifically (leaves every other .wrap.prose use, e.g.
   Mögliche Erstattung, untouched); the heading and body paragraph keep
   their own sensible reading widths via the rules below instead. */
.section--dark > .wrap.prose { max-width: var(--maxw); }
.section--dark > .wrap.prose h2 + p { max-width: 42rem; }
.section--dark::before {
  content: ""; position: absolute; right: -3%; bottom: -12%;
  width: clamp(220px, 28vw, 400px); aspect-ratio: 720 / 1064;
  background: url("../img/botanical-editorial.webp") right bottom / contain no-repeat;
  opacity: .1; pointer-events: none;
}
@media (max-width: 760px) { .section--dark::before { display: none; } }

/* ----- Botanical background motif — extremely subtle editorial watermark.
   Same url()/no-repeat technique as .section--dark's own leaf accent
   above, at much lower opacity for light paper backgrounds. Decorative
   only: a CSS background on a ::before, never an <img>, so nothing is
   exposed to screen readers and no alt text is needed. Used sparingly —
   two placements only (Themen thematic transition, About professional
   transition) — each with its own crop/position/scale so the motif
   reads as a considered system rather than a repeated wallpaper. */
.section--botanical { position: relative; overflow: hidden; }
.section--botanical > .wrap { position: relative; z-index: 1; }
.section--botanical::before { content: ""; position: absolute; pointer-events: none; }
@media (max-width: 760px) { .section--botanical::before { display: none; } }

.section--botanical--themen::before {
  right: -6%; top: -8%;
  width: clamp(260px, 30vw, 460px); aspect-ratio: 720 / 1064;
  background: url("../img/botanical-editorial.webp") right top / contain no-repeat;
  opacity: .06;
}
@media (max-width: 1100px) { .section--botanical--themen::before { width: clamp(220px, 26vw, 360px); opacity: .05; } }

/* The source asset has an opaque white canvas around the leaves (not
   transparent), so a plain low-opacity crop left a faint but visible
   rectangle where that white met the paper background. mix-blend-mode:
   multiply drops the white entirely (white × anything = anything) so
   only the sepia leaf pixels tint the page, and the mask fades the
   remaining crop edge to nothing instead of ending in a hard line. */
.section--botanical--about::before {
  left: -9%; top: -7%;
  width: clamp(300px, 32vw, 460px); height: clamp(400px, 40vw, 560px);
  background: url("../img/botanical-editorial.webp") 30% 15% / 240% auto no-repeat;
  opacity: .08; mix-blend-mode: multiply;
  mask-image: radial-gradient(circle at 12% 10%, #000 0%, transparent 42%);
  -webkit-mask-image: radial-gradient(circle at 12% 10%, #000 0%, transparent 42%);
}
@media (max-width: 1100px) { .section--botanical--about::before { width: clamp(230px, 28vw, 340px); height: clamp(300px, 34vw, 420px); opacity: .06; } }

/* Qualifikation und Erfahrung (About): each .wrap.prose child otherwise
   inherits .prose's 34rem measure on the whole container (same bug as
   the dark final-CTA above), collapsing the heading and list into a
   narrow centred column with dead space either side. Widen both back to
   the section's normal grid; only the intro lede keeps its own capped
   reading width. */
.section--botanical--about > .wrap.prose { max-width: var(--maxw); }
.section--botanical--about > .wrap.prose:first-child .lede { max-width: 48rem; }

/* Themen "Meine Schwerpunkte" / "My focus areas" (2026-09-27, Felix's
   explicit brief): same narrow-centred-column bug, same fix as
   --about/.section--dark above. */
#lebensrealitaeten-im-fokus > .wrap.prose,
#lived-realities-in-focus > .wrap.prose { max-width: var(--maxw); }
#lebensrealitaeten-im-fokus > .wrap.prose p,
#lived-realities-in-focus > .wrap.prose p { max-width: 48rem; }

/* Fine-divider list, no bullets — same row technique as .rates/.timeline,
   used for short editorial lists that would otherwise fall back to
   browser-default disc markers (e.g. Qualifikation). */
.list-plain { list-style: none; margin: 0 0 var(--s3); padding: 0; border-top: 1px solid var(--line); max-width: 42rem; }
.list-plain li { padding: .6rem 0; border-bottom: 1px solid var(--line); margin: 0; line-height: 1.5; }

/* Qualification rows paired with an institution mark — same fine-divider
   row technique as .list-plain, with a fixed logo column added. Logos sit
   muted/grayscale at rest (quiet supporting element, never competing with
   the text) and settle to full opacity+colour on hover — same restrained
   interaction model as .affiliation, just without the click-to-open
   label (there is nothing hidden here to reveal, so no extra keyboard
   handling is needed beyond the default focus outline on any real link). */
.qual-list { list-style: none; margin: 0 0 var(--s3); padding: 0; border-top: 1px solid var(--line); max-width: 46rem; }
.qual-list li {
  display: grid; grid-template-columns: 3.5rem 1fr; gap: var(--s3); align-items: center;
  padding: .8rem 0; border-bottom: 1px solid var(--line);
}
.qual-list__row--text-only .qual-list__text { grid-column: 2; }
.qual-list__logo { display: flex; align-items: center; justify-content: flex-start; height: 2.5rem; }
.qual-list__logo img {
  display: block; max-width: 100%; max-height: 100%; width: auto; height: auto;
  filter: grayscale(1); opacity: .55; transition: opacity .22s var(--ease), filter .22s var(--ease);
}
.qual-list li:hover .qual-list__logo img, .qual-list li:focus-within .qual-list__logo img { opacity: 1; filter: grayscale(0); }
.qual-list__text { color: var(--ink-2); line-height: 1.5; }
@media (prefers-reduced-motion: reduce) { .qual-list__logo img { transition: none; } }
@media (max-width: 560px) {
  .qual-list li { grid-template-columns: 2.75rem 1fr; gap: var(--s2); }
  .qual-list__logo { height: 1.9rem; }
}
/* A heraldic crest (shield + crown) reads tall and narrow by nature — at
   the shared logo height it would sit far narrower than the landscape
   institution wordmarks beside it, unbalanced not because it's wrong but
   because it's simply a different, correct shape. Give it more height
   (never distorted, never forced to their width) so its visual weight
   reads comparably instead of like an afterthought. */
.qual-list__logo--crest { height: 3.3rem; }
@media (max-width: 560px) { .qual-list__logo--crest { height: 2.5rem; } }

.section--dark h1, .section--dark h2, .section--dark h3 { color: var(--on-dark); }
.section--dark a { color: var(--on-dark); }
.section--dark .lede { color: var(--on-dark); }
/* keep button label colours intact inside dark sections (beat `.section--dark a`) */
.section--dark a.btn--on-dark { color: var(--earth-dark); }
.section--dark a.btn:not(.btn--on-dark):not(.btn--ghost) { color: var(--on-dark); }
.section--dark a.btn--ghost { color: var(--on-dark); border-color: rgba(233,232,227,.5); }
.section--dark a.btn--ghost:hover { background: var(--paper); color: var(--earth-dark); border-color: var(--paper); }

.prose { max-width: var(--measure); }
.prose > * + h2, .prose > * + h3 { margin-top: 1.7em; }

/* Section opener: heading + a single accent rule. No eyebrow.
   The rule is the brand's one chromatic mark — a short pine-slate bar set
   above every section heading (not in the hero, not on dark sections). */
.opener { margin-bottom: var(--s5); }
.opener h2 { margin-bottom: 0; }
.rule {
  width: 60px; height: 2px; background: var(--accent); border: 0; margin: var(--s3) 0 0;
  transform-origin: left; transform: scaleX(1);
}
.opener .rule { margin-top: var(--s3); }

/* Automatic opener mark on section headings that lead a block of content */
.section:not(.hero):not(.section--dark) > .wrap > h2:first-child::before,
.section:not(.hero):not(.section--dark) > .wrap > .split:first-child > div:first-child > h2::before,
.section:not(.hero):not(.section--dark) > .wrap > .opener > h2::before {
  content: ""; display: block; width: 44px; height: 2px;
  background: var(--accent); margin-bottom: var(--s3);
}

/* Editorial section number — ONLY for genuine sequences (Ablauf) */
.seq { list-style: none; margin: var(--s5) 0 0; padding: 0; }
.seq > li {
  display: grid; grid-template-columns: minmax(3.5rem, 8vw) 1fr; gap: var(--s3) var(--s4);
  padding: var(--s4) 0; border-top: 1px solid var(--line);
}
.seq > li:last-child { border-bottom: 1px solid var(--line); }
.seq .n {
  font-family: var(--font-display); font-weight: 300; line-height: .8;
  /* Was var(--line) (~1.3:1 on --paper-2) - a border/hairline token, not a
     text colour, so this large decorative numeral was nearly invisible
     and failed axe's WCAG 1.4.3 contrast check (2026-09-26, QA pass) even
     though it's genuine visible text, not something aria-hidden could
     exempt (that only affects screen readers, not sighted low-vision
     perception). --nav-dimmed reaches ~4.78:1 here - the quietest token
     in the palette that still passes, keeping the numeral as unobtrusive
     as the design intends. */
  font-size: clamp(2rem, 1rem + 4vw, 3.6rem); color: var(--nav-dimmed);
  font-variant-numeric: tabular-nums lining-nums; letter-spacing: -.03em;
}
.seq h3 { margin: 0 0 .35em; }
.seq p { margin: 0; color: var(--muted); }

.lede {
  font-family: var(--font-display); font-weight: 380;
  font-size: clamp(1.3rem, 1rem + 1.5vw, 1.85rem); line-height: 1.44;
  letter-spacing: -0.015em; color: var(--ink-2); max-width: 40rem;
}

/* Homepage pillar headline as the whole link (Männergesundheit / Behinderung):
   a quiet colour deepen + slight shift, same restrained idea as Themen's
   .index__row, without a background-tone fill (these sit on differing
   section backgrounds, ground and plain, so a fixed fill colour would be
   invisible against one of them). Still looks like a headline first. */
.pillar-link {
  display: inline-block; color: inherit; text-decoration: none;
  transition: color .2s var(--ease), transform .2s var(--ease);
}
.pillar-link:hover, .pillar-link:focus-visible { color: var(--ink); transform: translateX(.3rem); }
.pillar-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) { .pillar-link { transition: none; } }

/* Asymmetric split. Generous column gap, alignment left / ragged right. */
.split { display: grid; grid-template-columns: 1fr; gap: var(--s4); }
@media (min-width: 900px) {
  .split { grid-template-columns: 4fr 7fr; gap: clamp(var(--s5), 7vw, var(--s7)); align-items: start; }
  .split--reverse { grid-template-columns: 5fr 6fr; }
  .split--wide-media { grid-template-columns: 5fr 6fr; }
  /* Near-even split — image and text column read as equal visual weight,
     rather than one dominating. Order in the DOM decides which side each
     sits on; no reordering here. */
  .split--balanced { grid-template-columns: 1fr 1fr; }
}

/* Larger body copy for a text column meant to carry equal visual weight
   next to an image (e.g. a .split--wide-media pairing), rather than
   reading as short supporting copy beside a dominant photo. */
.prose--lg { max-width: 42rem; }
.prose--lg p { font-size: clamp(1.35rem, 1.1rem + .5vw, 1.5rem); line-height: 1.55; }

/* Controlled grid-break: pull an element to the page margin on wide screens */
@media (min-width: 1240px) {
  .bleed-left { margin-left: calc((100% - 100vw) / 2 + var(--gutter)); }
}

/* ----- Header / nav ----- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-soft);
}
.site-header__inner {
  max-width: var(--maxw); margin-inline: auto; padding: 0 var(--gutter);
  min-height: var(--header-h); display: flex; align-items: center; gap: var(--s3);
  justify-content: space-between;
}
/* Brand unit: logo + name, horizontal, both set in the site's own dark
   earth-toned ink directly on the normal paper header - no background
   block. The dark tone deliberately echoes "Mein Bereich" (same
   var(--earth-dark)) so the two read as a family, but only as a letterform
   colour: the filled dark rectangle is reserved for that one real action
   button, keeping BRAND (dark lettering on paper) and ACTION (dark filled
   button) visually distinct. The monogram is the ink-fill PNG variant
   (opaque earth-dark on transparent, recoloured 2026-09-26 along with the
   CSS token - it's a flat-fill raster silhouette, no vector master, so
   the PNG itself was repainted pixel-for-pixel rather than just changing
   a CSS value the image wouldn't otherwise pick up), matching the
   lettering colour by construction - no CSS colour needed on the image
   itself. Never dimmed by the nav's sibling-dimming (it isn't part of
   .nav__list). */
/* Open horizontal word-and-image lockup (2026-09-25, third header version
   this session, replacing the compact dark tile per Felix's explicit new
   brief): monogram left, a two-line serif wordmark ("Psychologische" /
   "Beratung", set in var(--font-display) like the rest of the site's
   editorial type, not the tagline's old uppercase Inter treatment) right
   of it - no background plate, ink directly on the paper header.

   Precise bottom alignment to the monogram's "S" (the brief's central
   requirement): logo-fs-mono-ink.png's own glyph (F + hands + S) sits
   inside its 191x160 canvas with a measured ~6.25% transparent margin on
   every side (alpha bbox: rows 10-149 of 160, cols 10-180 of 191 - both
   letters' own bottoms land within 1px of each other at row ~149, i.e.
   the tight glyph bbox's bottom IS effectively the S's bottom). Naive
   align-items:flex-end would align the WORDMARK to the canvas's bottom
   edge, which sits ~6.25% of the rendered height below the actual S - so
   .brand__mark carries a negative margin-bottom (height x 0.0625) at
   every breakpoint, pulling the flex alignment line up to the glyph's
   real bottom instead of the canvas's. Confirmed by live measurement
   (getBoundingClientRect, not eyeballing) that the wordmark's own last
   line then lands within ~1px of that same edge. */
.brand {
  display: inline-flex; align-items: flex-end; gap: .6rem;
  text-decoration: none;
}
.brand__mark {
  height: 44px; width: auto; display: block;
  margin-bottom: -1.22px; /* 44px x ~2.78%: 6.25% canvas padding minus a
    measured ~3.47% correction for the sans wordmark's own generous
    line-height leaving real headroom below the caps' ink (net LESS
    negative than the padding alone) - see note above */
}
/* Font corrected 2026-09-25: Felix pointed at the original source
   artwork (the sage-green square the monogram itself was extracted
   from) - its own "PSYCHOLOGISCHE BERATUNG" caption is a light
   geometric sans, uppercase, wide-tracked, not a serif. That's the same
   typography already baked into logo-fs-full-*.png, so this live-text
   version matches it with the site's own sans (var(--font-body),
   Inter - self-hosted only down to weight 400, the lightest available,
   used here as the closest match to the source's lighter stroke).
   text-transform:uppercase on mixed-case source text (not upper-cased
   in the HTML) so a screen reader still says "Psychologische Beratung"
   normally rather than risking a letter-by-letter acronym reading. */
.brand__word {
  display: flex; flex-direction: column;
  font-family: var(--brand-label-font); font-weight: var(--brand-label-weight); font-style: normal;
  text-transform: uppercase; letter-spacing: var(--brand-label-tracking);
  color: var(--earth-dark);
  font-size: .66rem; line-height: 1.4; white-space: nowrap;
}
.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius); }
@media (min-width: 1191px) and (max-width: 1399px) {
  .brand__mark { height: 38px; margin-bottom: -1.06px; }
  .brand__word { font-size: .6rem; }
}
@media (max-width: 480px) {
  .brand { gap: .45rem; }
  .brand__mark { height: 32px; margin-bottom: -0.89px; }
  .brand__word { font-size: .56rem; letter-spacing: var(--brand-label-tracking-tight); }
}
.nav { display: flex; align-items: center; gap: 1.15rem; }
.nav__list { display: flex; flex-wrap: wrap; gap: .5rem 1.35rem; list-style: none; margin: 0; padding: 0; }
.nav__list a {
  font-size: .96rem; font-weight: 500; letter-spacing: .005em;
  color: var(--nav-ink); text-decoration: none; padding: .5rem 0; position: relative; white-space: nowrap;
  transition: color .2s var(--ease);
}
/* Permanent active-page tone (no interaction needed): darker than the
   resting --nav-ink, same weight as --nav-active below. Interaction
   (below) temporarily overrides this at higher specificity, then lets it
   fall back once the pointer/focus leaves the whole group. */
.nav__list a[aria-current="page"] { color: var(--nav-active); }
.nav__list a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .1rem; height: 1.5px; background: var(--accent);
  transform: scaleX(0); transform-origin: right; transition: transform .28s var(--ease);
}
.nav__list a[aria-current="page"]::after { transform: scaleX(1); transform-origin: left; }
/* "Lebensrealitäten im Fokus" / "Therapie-Formen": each a real top-level
   link to its own destination, that also reveals labelled sub-items on
   hover/focus. The sub-items live inside the same <li> as the trigger, so
   (a) the hover area is continuous - no dead gap between trigger and
   panel - and (b) keyboard tab order visits them immediately after their
   trigger "for free", with no extra JS needed to redirect focus. At
   ≥1191px the panel is a full-width, header-coloured overlay band (see the
   detailed note at the ≥1191px override below for why it stays an overlay
   rather than genuinely joining document flow - a deliberate, permanent
   decision, not a stopgap). Below 1191px it's the compact vertical
   disclosure toggled by .nav__subtoggle. */
.nav__item--has-sub { position: relative; }
.nav__subtoggle { display: none; }
.nav__submenu {
  list-style: none; margin: 0; padding: .4rem; position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%) translateY(-4px); min-width: 12rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(25,26,26,.07);
  visibility: hidden; opacity: 0;
  transition: opacity .2s var(--ease), visibility .2s var(--ease), transform .2s var(--ease);
}
/* The sitewide `li { margin: .35em 0; }` reset otherwise leaks ~12px of
   extra vertical size into this band's height (both breakpoints size
   their submenu rows off the <a>'s own padding, not the <li>'s margin). */
.nav__submenu li { margin: 0; }
/* No base (unscoped) open-state rule here on purpose - it's fully owned by
   the two breakpoint-specific overrides below (mobile's own unconditional
   visibility/opacity + max-height toggle; desktop's .is-pointer-open/
   :focus-within rule). An older, unscoped `:hover ... { transform:
   translateX(-50%) ... }` version of this rule used to live here and was
   missed when the desktop submenu moved to explicit JS pointer-state and
   full-width positioning - it kept winning the transform cascade at equal
   specificity (same issue the mobile override below already had to work
   around once), silently reintroducing the old centered-under-trigger
   translateX on top of the new alignment logic in main.js. Removed
   outright rather than patched, since both breakpoints are already
   complete without it - one less place for "only one explicit system" to
   quietly stop being true. */
.nav__submenu a {
  display: block; padding: .45rem .65rem; font-size: .88rem; white-space: nowrap;
  color: var(--ink-2); text-decoration: none; border-radius: var(--radius);
  transition: color .2s var(--ease);
}
.nav__submenu a::after { display: none; }
.nav__submenu a[aria-current="page"] { color: var(--nav-active); font-weight: 500; }
@media (prefers-reduced-motion: reduce) { .nav__submenu, .nav__submenu a { transition: none; } }

/* Sibling-dimming / interaction-active state - ONE explicit, JS-driven
   mechanism (main.js: wireNavFocusGroup), not CSS :has(). Earlier CSS-only
   attempts (:hover, :focus-visible, :has()) read as correct against this
   session's own testing tools, but Felix's own screen recordings showed
   the colour change was not actually happening in his real browser - only
   the underline moved. Rather than add a third parallel implementation on
   top of that unreliable one, this replaces it outright: main.js adds
   .is-interacting to the group and .is-interaction-active to the engaged
   item on real pointer/focus events, and these three rules are the only
   thing that colours grouped nav/submenu links. A has-sub trigger's
   "engaged" scope is its whole <li> (covers its own open submenu too), so
   it stays active while its sub-items are being browsed, not just while
   the trigger text itself is hovered. */
.nav__list a.is-interaction-active,
.nav__submenu a.is-interaction-active { color: var(--nav-active); }
.nav__list a.is-interaction-active::after { transform: scaleX(1); transform-origin: left; }
/* Scoped to `> li > a` (not a bare descendant selector) on purpose: a
   .nav__submenu lives nested inside its trigger's own <li>, so a plain
   ".nav__list.is-interacting a" would also match every submenu link the
   moment the trigger's <li> is engaged - dimming the submenu's own items
   before the user has hovered/focused any of them, even though the
   submenu's own separate wireFocusGroup() group hasn't done anything yet.
   The direct-child chain matches only the top-level trigger/plain-link
   <a>, leaving submenu links to whatever colour the .nav__submenu group
   below decides - fixed 2026-09-25, confirmed via real DOM structure
   (.nav__list > li > a for triggers vs .nav__list > li > ul.nav__submenu >
   li > a for sub-items, three levels deeper). */
.nav__list.is-interacting > li > a:not(.is-interaction-active),
.nav__submenu.is-interacting a:not(.is-interaction-active) {
  color: var(--nav-dimmed);
}

.lang-switch { display: flex; gap: .4rem; font-size: .8rem; color: var(--muted); }
.lang-switch a { text-decoration: none; }
.lang-switch a[aria-current="true"] { color: var(--ink); font-weight: 600; }

/* Compact language/accessibility cluster: DE / EN on top, the quieter
   Einfache Sprache link directly beneath it, both as one grid/flex item
   in .nav so they read as a single unit next to Mein Bereich. */
.lang-cluster { display: flex; flex-direction: column; align-items: center; gap: .15rem; }
.lang-cluster__easy {
  font-size: .72rem; font-weight: 500; color: var(--muted); text-decoration: none;
  line-height: 1.2; padding: .2rem 0; transition: color .18s var(--ease);
}
.lang-cluster__easy:hover, .lang-cluster__easy:focus-visible { color: var(--ink); }
.lang-cluster__easy:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) { .lang-cluster__easy { transition: none; } }

.nav-toggle { display: none; }

@media (max-width: 1190px) {
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; border: 1px solid var(--line); background: var(--card);
    border-radius: var(--radius); color: var(--ink); cursor: pointer;
  }
  .nav-toggle svg { width: 22px; height: 22px; }
  .nav {
    position: fixed; inset: var(--header-h) 0 auto 0;
    background: var(--paper-2); border-bottom: 1px solid var(--line);
    flex-direction: column; align-items: stretch; gap: var(--s3);
    padding: var(--s3) var(--gutter); transform: translateY(-8px); opacity: 0; pointer-events: none;
    transition: opacity .2s var(--ease), transform .2s var(--ease);
  }
  .nav[data-open="true"] { opacity: 1; transform: none; pointer-events: auto; }
  .nav__list { flex-direction: column; gap: 0; }
  .nav__list li { border-bottom: 1px solid var(--line-soft); }
  .nav__list a { display: block; padding: .95rem 0; font-size: 1rem; }
  .nav__list a::after { display: none; }
  .btn--nav { align-self: flex-start; }
  /* "Lebensrealitäten im Fokus" is a compact disclosure on mobile: the
     link itself still navigates (it is a real destination), and a small
     separate toggle button expands/collapses its two sub-items — collapsed
     by default so the item takes one row, not three, until asked to open.
     The group's own bottom border (inherited from .nav__list li) sits
     after the sub-items, so the nested <li> stays borderless to avoid a
     doubled line. */
  .nav__item--has-sub { position: static; display: flex; flex-wrap: wrap; align-items: center; }
  .nav__item--has-sub > a { flex: 1 1 auto; }
  .nav__subtoggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; margin: .2rem 0; padding: 0;
    border: none; background: none; color: var(--muted); cursor: pointer;
  }
  .nav__subtoggle svg { width: 18px; height: 18px; transition: transform .2s var(--ease); }
  .nav__item--has-sub.is-open .nav__subtoggle svg { transform: rotate(180deg); }
  .nav__submenu {
    position: static; visibility: visible; opacity: 1; transform: none;
    box-shadow: none; border: none; background: none; min-width: 0;
    list-style: none; padding: 0 0 0 var(--s3); margin: 0; width: 100%;
    max-height: 0; overflow: hidden; transition: max-height .22s var(--ease);
  }
  /* A real tap on .nav__subtoggle moves DOM focus onto the button, which
     satisfies the desktop dropdown's `:focus-within` rule (centred,
     absolutely positioned, translateX(-50%)) - same specificity as the
     block above, so without this it wins on source order and the mobile
     submenu renders shifted off-screen the moment it's opened by tapping
     rather than by the (non-focusing) row link. Repeat the mobile reset
     under the same states so mobile always wins regardless of focus. */
  .nav__item--has-sub:hover .nav__submenu,
  .nav__item--has-sub:focus-within .nav__submenu {
    position: static; transform: none;
  }
  /* 7rem comfortably fit the original 2-item Lebensrealitäten submenu but
     clips the 4-item Therapie-Formen one to ~2.75 rows (measured: 4 × 40.6px
     ≈ 162px of real content vs 112px of cap) - "Gruppen" was unreachable by
     mobile disclosure. Raised for both groups; a 2-item group still just
     renders at its own shorter content height, unaffected by the larger cap. */
  .nav__item--has-sub.is-open .nav__submenu { max-height: 12rem; }
  .nav__submenu li { border-bottom: none; }
  .nav__submenu a { display: block; padding: .5rem 0; font-size: .95rem; color: var(--ink); }
  @media (prefers-reduced-motion: reduce) {
    .nav__submenu, .nav__subtoggle svg { transition: none; }
  }
}

/* Desktop header: brand / nav / actions as three true columns so the nav
   sits genuinely centered and "Kontakt" + DE/EN + Mein Bereich never wrap.
   Mirrors the >1190px breakpoint the mobile nav switches on - raised from
   1120/1121 when the nav link font-size grew (.82rem -> .9rem), which
   pushed the row's min-content width to ~1173px, leaving no room between
   the old breakpoint and the content's real minimum. */
@media (min-width: 1191px) {
  .site-header__inner {
    max-width: 1460px;
    padding-inline: 2.5rem;
    display: grid;
    grid-template-columns: max-content minmax(max-content, 1fr);
    align-items: center;
    column-gap: 2rem;
  }
  .nav {
    display: grid;
    grid-template-columns: minmax(max-content, 1fr) auto auto;
    align-items: center;
    column-gap: 1.1rem;
  }
  .nav__list {
    justify-content: center;
    flex-wrap: nowrap;
    gap: 1.6rem;
    white-space: nowrap;
  }
  .nav__list a { white-space: nowrap; }
  .lang-switch, .btn--nav, .lang-cluster__easy { white-space: nowrap; }

  /* Second row of the header — intentional, permanent architecture
     (decided 2026-09-24, do not revisit without a real new bug): reads
     visually as HEADER ROW 1 + HEADER ROW 2, but stays position:absolute,
     anchored to .site-header (sticky, so a valid containing block) rather
     than genuinely joining normal document flow. An in-flow version was
     tried twice and reverted twice for real regressions: moving the
     submenu out of its trigger's <li> broke keyboard tab order (it landed
     last in the whole nav instead of right after its trigger), and even a
     nested, closed, zero-height in-flow submenu still counted its own
     full width toward the trigger <li>'s flex sizing, overflowing the
     six-item nowrap row. The overlay has none of that: it never changes
     the header's own height, so opening/closing a submenu causes no
     layout shift, no scroll-position jump, and no anchor-offset
     complications elsewhere on the page - an intentional benefit of this
     model, not just a fallback. `.nav__item--has-sub` is reset to
     position:static here (its base position:relative below only matters
     below this breakpoint, where the submenu is an in-flow mobile
     disclosure instead), so .nav__submenu's `left`/`right` resolve against
     the full-width header rather than one trigger's narrow <li> - this is
     what makes the band span edge-to-edge instead of reading as a floating
     pill under whichever word triggered it. */
  .nav__item--has-sub { position: static; }
  /* transition: none on the closed/default state on purpose - closing is
     instant. Two adjacent submenus are both absolutely positioned overlays
     sharing the same full-width band, so if closing faded out over the
     same .2s the opening fades in over, a fast sweep of the pointer across
     two triggers caught both submenus visible at once, each showing its
     own items in one confusing merged-looking row (seen on video, not just
     a hover test). The transition lives only on the :hover/:focus-within
     (open) rule below, so opening still animates in but closing snaps away
     the instant the pointer/focus leaves. */
  .nav__submenu {
    position: absolute; top: 100%; left: 0; right: 0; transform: translateY(-4px);
    visibility: hidden; opacity: 0;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start;
    gap: .3rem 1.6rem; margin: 0;
    max-width: 1460px; margin-inline: auto; padding: .5rem 2.5rem;
    background: var(--paper-2); border: none; border-radius: 0; box-shadow: none;
    border-top: 1px solid var(--line-soft);
    transition: none;
  }
  /* Open state is .is-pointer-open (JS, main.js) OR :focus-within (native
     keyboard) - deliberately NOT :hover. Plain :hover closed the instant
     the pointer left the trigger <li>'s own small box, including partway
     through the real, measured ~24px gap between that box and the
     full-width submenu band below it (a side effect of anchoring the band
     to .site-header instead of the trigger's own <li> - see the note
     above), making the submenu practically unreachable by mouse. main.js
     tracks pointerenter/pointerleave on the whole <li> subtree (trigger +
     its submenu) with a short close-delay, so briefly crossing that gap
     no longer closes it. Named .is-pointer-open, not .is-open, to avoid
     colliding with the mobile tap-disclosure's own .is-open on the same
     <li> (wireDisclosure() in main.js) - two different mechanisms, two
     different classes, even though only one is ever active at a given
     breakpoint. */
  .nav__item--has-sub.is-pointer-open .nav__submenu,
  .nav__item--has-sub:focus-within .nav__submenu {
    visibility: visible; opacity: 1; transform: translateY(0);
    transition: opacity .2s var(--ease), transform .2s var(--ease);
  }
  /* line-height set explicitly (not inherited from body's 1.62) so the
     band's own height stays a restrained ~38-46px regardless of the
     fluid base font-size, rather than growing with body's generous
     reading line-height. */
  .nav__submenu a {
    display: block; padding: .2rem .9rem; font-size: .95rem; line-height: 1.2; white-space: nowrap;
    color: var(--nav-ink); text-decoration: none; border-radius: 0;
    transition: color .2s var(--ease);
  }
  /* Color is owned entirely by the base .nav__submenu a[aria-current] rule
     and the .is-interaction-active / .is-interacting JS-class system above —
     no :hover/:focus-visible color rule here, so there is exactly one
     mechanism deciding nav link color. The focus-visible outline stays: it's
     an accessibility affordance, independent of the color-dimming system. */
  .nav__submenu a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  /* Matches the open-state rule's own selectors (line 617-618) exactly,
     not just ".nav__submenu" - that lower-specificity version lost the
     cascade to the open state's own explicit `transition: opacity .2s,
     transform .2s`, so the submenu still animated open/closed under
     prefers-reduced-motion:reduce (confirmed via computed specificity:
     0,3,0 for the open-state rule vs. 0,1,0 for the old override - real
     bug, not a false alarm from the original audit). Equal specificity +
     later source order now wins correctly. */
  @media (prefers-reduced-motion: reduce) {
    .nav__item--has-sub.is-pointer-open .nav__submenu,
    .nav__item--has-sub:focus-within .nav__submenu,
    .nav__submenu {
      transition: none;
    }
  }
}
/* 1191–1399px only: fewer/shorter labels than before ("Lebensrealitäten
   im Fokus" is still the widest, but the row is six items instead of ten
   now) leaves more headroom than the old ten-item row had, but the larger
   §2 type size re-introduces some of that pressure back. Reduce gaps and
   the header's own side padding first (order per the brief: gaps, then
   layout spacing, only then typography); nothing here affects 1400px+,
   where the row already fits with its full, more generous spacing. */
@media (min-width: 1191px) and (max-width: 1399px) {
  .site-header__inner { padding-inline: 1rem; column-gap: 1.25rem; }
  .nav { column-gap: .45rem; }
  .nav__list { gap: .5rem .85rem; }
  /* Last resort per the ordering above: only as far as needed once gaps
     and padding are already trimmed, and only in this narrow range.
     Previously .86rem, which sat BELOW the submenu's own .95rem and
     .btn--nav's .82rem - an inverted hierarchy where the second nav level
     read larger than the first. Raised main nav back above both, and
     added matching submenu/button overrides so all three keep the same
     relative order (main nav > submenu > utility button) as everywhere
     else on the site - fixed 2026-09-25. */
  .nav__list a { font-size: .93rem; }
  .nav__submenu a { font-size: .88rem; }
  /* No brand override needed in this range anymore - the compact tile
     (fixed 2026-09-25, superseding the wider horizontal lockup that did
     need one here) is narrow regardless of viewport width. */
}
/* A true full-header-width centering mode was tried above 1400px (nav__list
   absolutely centered on the whole header, independent of the brand
   column's width), but it collides with the lang-cluster's two-line
   "DE / EN" + "Einfache Sprache" block once that block is wider than the
   space its own flex track has to spare — verified colliding at 1440px and
   1920px alike, since the header's own max-width (1300px) caps how far the
   internal layout ever grows. Centering within the nav's own grid track
   (the 1191px behaviour above) has no such collision at any tested width
   and is used at all widths ≥1191px now. */
@media (prefers-reduced-motion: reduce) { .nav, .nav__list a, .nav__list a::after { transition: none; } }

/* ----- Buttons ----- */
.btn {
  display: inline-flex; align-items: center; gap: .5rem; min-height: 46px;
  font-family: var(--font-body); font-weight: 600; font-size: .95rem; line-height: 1;
  padding: .9rem 1.6rem; border-radius: var(--radius);
  border: 1.5px solid var(--earth-dark); background: var(--earth-dark); color: var(--on-dark);
  text-decoration: none; cursor: pointer;
  transition: background-color .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease);
}
/* --earth-hover replaces a hardcoded #101214 - that near-black had no
   token at all and undid the whole point of a warm dark-neutral family
   the instant a button was hovered. */
.btn:hover { background: var(--earth-hover); border-color: var(--earth-hover); color: var(--on-dark); }
.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { background: var(--earth-dark); color: var(--on-dark); }
.btn--nav { padding: .6rem 1.05rem; font-size: .82rem; min-height: 40px; }
/* Placed after the base .btn--nav rule on purpose: same specificity, so an
   earlier occurrence (e.g. inside the 1191-1399px block above) would lose
   the cascade to this later, unscoped one regardless of its own media
   condition - the exact bug this override avoids. */
@media (min-width: 1191px) and (max-width: 1399px) {
  .btn--nav { font-size: .88rem; }
}
/* Light button on a dark section - was pure #fff, now --paper (the
   site's own warm off-white) so it reads as part of the same palette
   instead of a stark white cutout; hover deepens one step to --paper-2,
   matching the "surface -> ground" relationship those two tokens already
   have everywhere else on the site. */
.btn--on-dark { background: var(--paper); border-color: var(--paper); color: var(--earth-dark); }
.btn--on-dark:hover { background: var(--paper-2); border-color: var(--paper-2); }

/* Editorial text link, site-wide: the title/row itself is the link, no
   permanent arrow — a quiet close, not a CTA. Two usage shapes share this
   one class: (a) the class sits directly on the <a> itself (most pages —
   "Mehr über mich", "Alle Themen im Überblick", etc.), where
   .link-arrow:hover/:focus-visible fires naturally; (b) inside a .formate
   card, .link-arrow is a <span> and the whole <a> card is the hit target,
   so .formate a:hover/:focus-visible .link-arrow triggers the same state
   from anywhere in the card, not just over the text. */
.link-arrow {
  display: inline-flex; align-items: baseline;
  font-weight: 500; color: var(--ink-2);
  text-decoration: none; position: relative; padding-bottom: .2em;
  transition: color .2s var(--ease), transform .2s var(--ease);
}
.link-arrow::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: currentColor; opacity: .35;
  transform: scaleX(0); transform-origin: right; transition: transform .3s var(--ease);
}
.link-arrow:hover, .link-arrow:focus-visible,
.formate a.is-interaction-active .link-arrow {
  color: var(--ink); transform: translateX(3px);
}
.link-arrow:hover::before, .link-arrow:focus-visible::before,
.formate a.is-interaction-active .link-arrow::before {
  transform: scaleX(1); transform-origin: left;
}
@media (prefers-reduced-motion: reduce) { .link-arrow, .link-arrow::before { transition: none; } }

/* ----- Hero — typography carries it ----- */
.hero { padding-block: clamp(var(--s5), 12vw, calc(var(--s8) + var(--s4))); position: relative; overflow: hidden; }
.hero > .wrap { position: relative; z-index: 1; }
.leaf-accent {
  position: absolute; top: -10%; right: -6%; z-index: 0;
  width: clamp(240px, 38vw, 560px); height: auto;
  opacity: .09; pointer-events: none; user-select: none;
}
@media (max-width: 860px) { .leaf-accent { display: none; } }
.hero h1 {
  font-size: var(--text-h1-hero);
  font-weight: 320; line-height: .98; letter-spacing: -0.035em;
  max-width: 16ch; margin-bottom: var(--s5);
}
@media (min-width: 640px) { .hero h1 { line-height: .96; letter-spacing: -0.04em; } }
/* Homepage hero motto (2026-09-27, Felix's explicit brief, confirmed
   against an annotated screenshot of the exact current large-serif
   treatment as the thing to replace): "Understand the present — Shape
   the future" now uses the SAME typographic identity as its own
   appearance on the language gateway's confirmation motto
   (.gateway__confirm-line in index.html) - same font-family (Inter, not
   Fraunces), same uppercase+tracking, same color, same em dash (not a
   period, not a plain hyphen - the gateway's own text is built from
   three spans, "Understand the present" / "—" / "Shape the future").
   Scoped to this one homepage element via a dedicated class, not a
   change to .hero h1 itself - every other page's hero headline (all
   still large Fraunces serif) is untouched. `.hero h1.hero__motto`
   (not just `.hero__motto` alone) is deliberate: `.hero h1`'s own
   selector already has higher specificity than a single class, so this
   needs the extra qualifier to reliably win regardless of source order.
   Sized up from the gateway's own small confirmation-line scale
   (2026-09-27, second pass, Felix's explicit follow-up brief): on the
   gateway this text is a secondary confirmation once a language is
   already chosen, but here it's the page's actual H1 and needs to read
   as the dominant hero statement, so the size is deliberately larger
   than .gateway__confirm-line's while every other typographic property
   (family/weight/transform/color) stays identical - tuned via live
   measurement so the full phrase still holds one line through the
   1191-1600px range without shrinking to the gateway's own small
   scale. */
.hero h1.hero__motto {
  font-family: var(--font-body); font-weight: 400; font-style: normal;
  text-transform: uppercase; letter-spacing: .08em;
  font-size: clamp(1.2rem, .69rem + 2.41vw, 2.05rem);
  color: var(--earth-dark);
  line-height: 1.35; max-width: none;
}
.hero .lede { margin-bottom: var(--s5); max-width: var(--measure-serif); }
/* Narrow screens only: the hero's own padding plus the two large
   margins below h1/lede added up to ~64% of a typical phone viewport
   before any content was visible. Tightened here without touching the
   headline size or the >=640px clamp above. */
@media (max-width: 600px) {
  .hero { padding-block: var(--s2); }
  .hero h1 { margin-bottom: var(--s3); }
  .hero .lede { margin-bottom: var(--s3); }
}
.hero__kicker {
  font-family: var(--font-display); font-style: italic; font-weight: 400;
  font-size: 1.05rem; color: var(--muted); margin: 0 0 var(--s4); letter-spacing: 0;
}

/* ----- Gateway — the root "/" language-entry screen, shown before any
   language version. Deliberately quiet: no dark brand block (unlike the
   in-site header, this is a first impression, not a navigation bar) - the
   monogram is already anthracite-on-transparent by construction
   (logo-fs-mono-ink.png), so it reads dark-on-paper with no extra colour
   applied; the name below it is set in the same anthracite tone the
   brief asked for ("oriented on Mein Bereich"), not the button treatment
   itself. ----- */
.gateway {
  min-height: 100svh; position: relative; overflow: hidden;
  display: grid; place-items: center; text-align: center;
  padding-inline: var(--gutter);
  /* Vertical padding is viewport-HEIGHT-relative, not width-relative like
     --gutter - the point is fitting the composition into one screen, which
     is a height problem, so it needs to shrink on short viewports
     regardless of how wide they are (a 1920×1080 window is wide but not
     short; an 1808×372 recording is both). */
  padding-block: clamp(1.5rem, 5vh, 3.5rem);
  /* Built from the same two existing paper tokens, not a new colour -
     an extremely subtle lift toward --paper behind the composition's own
     centre so the page reads as having a little depth rather than one
     flat fill, without becoming a visible "gradient design" moment
     (2026-09-25, visual-polish pass). */
  background: radial-gradient(ellipse 70% 60% at 50% 42%, var(--paper) 0%, var(--paper-2) 75%);
}
/* display:block overrides .leaf-accent's own @media(max-width:860px)
   {display:none} - that rule is meant for the in-page hero motif (hidden
   on mobile so it doesn't compete with hero text at narrow widths), but
   the gateway's leaf is meant to survive down to phone width, just more
   cropped/quieter there. Equal specificity to that rule, later in source
   order, so this wins at every width.

   Opacity (2026-09-25, second pass): the element was never actually
   broken - confirmed via computed style (display:block, visibility:
   visible, correct position/size, z-index:0, no transform/clip-path/
   blend-mode) and a network check (200 OK, real decoded image data,
   naturalWidth/Height both non-zero) - it was rendering exactly as
   coded. The bug was perceptual: .03-.045 opacity is too faint to
   register on a real monitor even though every technical property was
   correct. Raised into the range confirmed visible in an actual
   screenshot, not just in computed-style output.

   Positioning (2026-09-25, third pass): reverted the bottom-right/
   transform treatment above in favour of reusing .leaf-accent's own
   default position untouched (top:-10%, right:-6%, same width clamp,
   same opacity, same asset - botanical-editorial.webp, not the -2.webp
   green variant the gateway used before) - per Felix's explicit
   instruction to match the homepage hero's own leaf placement exactly
   rather than invent a gateway-specific treatment. This override block
   now only adds what the hero doesn't need: staying visible below
   860px. Every position/size/opacity value comes from .leaf-accent
   itself, unmodified - see that rule, not here. */
.gateway__leaf {
  display: block;
}
.gateway__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center;
  max-width: 30rem;
}
/* Gateway brand composition (2026-09-25, visual-polish pass): logo,
   heading and language chooser read as one connected block instead of
   the logo sitting alone in a large gap above - "Choose Language" is
   visible again (real <h1>, no longer .visually-hidden - CLAUDE.md's
   one-heading-per-page rule is still satisfied, it's just shown once
   more) as a quiet italic cue between the brand and the choice, not a
   page headline, plus a short .gateway__rule beneath it - a narrower,
   more deliberate divider than .gateway__langs's own full-width border,
   which is what visually ties the heading to the language row below it
   rather than leaving them as two separate floating elements. Spacing
   is intentionally asymmetric: more room from the heading DOWN to the
   language choice than from the logo UP to the heading (Felix's own
   brief), via .gateway__mark's margin-bottom vs. the combined
   .gateway__welcome + .gateway__rule margins. Logo sized ~8% larger
   than the previous pass, still the dominant element. */
.gateway__mark {
  width: clamp(205px, 20.5vw, 300px); height: auto; display: block;
  margin-bottom: clamp(2.5rem, 5vh, 3.5rem);
}
/* No separate rule element between the heading and the language row
   anymore (2026-09-25, visual-refinement pass): a short .gateway__rule
   plus .gateway__langs's own top border read as two lines back to back
   with barely any air between them - "weniger Linien, dafür bewusstere
   Linien", per Felix's brief. .gateway__langs's own top/bottom border is
   now the ONE line, and this margin-bottom is what gives it room to
   read as deliberate rather than crowding the heading. */
.gateway__welcome {
  position: relative;
  font-family: var(--font-display); font-style: italic; font-weight: 400;
  font-size: clamp(1.35rem, 1.15rem + .7vw, 1.65rem); color: var(--earth-dark); line-height: 1.35;
  margin: 0 0 clamp(2rem, 4vh, 3rem);
  transition: opacity .26s var(--ease), transform .26s var(--ease);
}
.gateway__welcome.is-hiding { opacity: 0; transform: translateY(-3px); }
/* Confirmation statement (2026-09-25, transition choreography): anchored
   to .gateway__welcome's own box (position:relative added above, just
   for this) via position:absolute/top:100%, so it always sits directly
   below wherever the heading is at any breakpoint with zero JS
   positioning - only the SELECTED language's move (below) needs a
   measured transform, per the brief. Sits completely out of flow, so it
   costs no layout space and the approved initial composition is
   unchanged until a language is actually chosen. All three language
   statements are real static markup (not JS-injected text) - only the
   one matching the chosen language gets .is-visible. */
/* Width fixed 2026-09-26 (final high-end transition pass, supersedes the
   previous min(26rem,80vw) fix): that value was sized for the old
   per-language two-line mottos and wraps the wider fixed English
   composition below awkwardly at typical desktop widths. min(90vw,760px)
   gives "Understand the present — Shape the future" real room; each
   phrase also carries its own white-space:nowrap (below) as a hard
   guarantee against a phrase breaking mid-word regardless of container
   width. Gap to the language row widened .85rem->clamp(28px,4vh,40px)
   to match the brief's explicit target.

   Margin-top increased again (2026-09-26, mist-removal pass): with the
   mist gone, a real leaf/text bounding-box overlap became the visible
   issue instead - measured directly (getBoundingClientRect, not
   guessed): at 1440x900 the leaf's lower-left corner overlapped this
   block's upper-right corner by ~3660px². clamp(1.75rem,4vh,2.5rem)
   wasn't enough clearance; clamp(3rem,7vh,4.5rem) was verified (same
   measurement method) to clear the leaf at 1440x900, 1024x768, 390x844
   and 430x900 - re-verify with the same technique if the leaf's own
   size/position ever changes, rather than adjusting this by eye. Leaf
   itself is untouched, per explicit instruction - only this block moved. */
.gateway__confirm {
  position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
  margin-top: clamp(3.5rem, 11vh, 6.5rem); width: min(90vw, 760px);
  pointer-events: none;
}
/* Typography (2026-09-26): the site's brand-label voice (same values as
   .eyebrow/.brand__word - the logo's own "PSYCHOLOGISCHE BERATUNG"
   subline), the gateway's highest-priority use of it. Structure changed
   in the final high-end pass: no longer three alternative per-language
   mottos stacked via position:absolute+opacity - one fixed English
   statement, always the same regardless of language chosen, built from
   three independently-staged inline units (phrase / dash / phrase) so
   each can reveal in its own held moment. The whole phrase fades in as
   one unit each time - never letter-by-letter or word-by-word. */
.gateway__confirm-line {
  margin: 0; display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: center; gap: .6em;
  font-family: var(--font-body); font-weight: 400; font-style: normal;
  text-transform: uppercase; letter-spacing: .12em;
  font-size: clamp(1.05rem, .95rem + .5vw, 1.35rem); color: var(--earth-dark);
  text-align: center; line-height: 1.5;
}
.gateway__confirm-phrase, .gateway__confirm-dash {
  display: inline-block; white-space: nowrap;
  opacity: 0; transform: translateY(3px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
}
/* The dash's own reveal is shorter/lighter than a full phrase - it's a
   punctuation beat between two statements, not a third statement. */
.gateway__confirm-dash { transition-duration: .3s; }
.gateway__confirm-phrase.is-visible, .gateway__confirm-dash.is-visible {
  opacity: 1; transform: translateY(0);
}
@media (max-width: 480px) {
  .gateway__confirm-line {
    font-size: clamp(.9rem, .82rem + .4vw, 1.1rem); letter-spacing: .09em; gap: .5em;
  }
}
.gateway__prompt {
  font-size: .92rem; color: var(--muted); line-height: 1.5;
  /* Only 404.html still uses this paragraph (the gateway itself dropped
     its prompt line entirely, per Felix's earlier brief) - its own
     margin-bottom is what gives 404 a gap down to its language links,
     since .gateway__langs's own margin-top is 0 (the gateway's version
     of this gap instead comes from .gateway__welcome's margin-bottom
     landing directly above .gateway__langs's own top border). */
  margin: .9rem 0 clamp(1.5rem, 3vh, 2.25rem);
}
.gateway__langs {
  display: flex; flex-direction: column; width: 100%;
  margin-top: 0; border-top: 1px solid var(--line);
  transition: border-color .5s var(--ease);
}
.gateway__lang {
  display: block; padding: clamp(1.4rem, 3vh, 1.75rem) clamp(1rem, 3vw, 1.5rem);
  border-bottom: 1px solid var(--line);
  text-decoration: none; color: inherit;
  transition: color .2s var(--ease), transform .2s var(--ease), border-color .5s var(--ease);
}
/* Transition choreography (2026-09-26): by the time the brand statement
   appears the dividing rules should be visually gone, per the brief -
   the frame lines (.gateway__langs's own top/bottom border) and, on
   mobile, each row's own bottom rule fade to transparent together. */
.gateway__langs.is-dividers-fading,
.gateway__langs.is-dividers-fading .gateway__lang {
  border-color: transparent;
}
/* Label-only rows (2026-09-25): the three language options carry no
   description line anymore - "only the three language names visible",
   per Felix's brief. Centred within each column/row now that there's no
   second line pulling everything left-aligned against it. */
.gateway__lang-label {
  display: block; font-family: var(--font-display); font-weight: 380;
  font-size: clamp(1.2rem, 1.05rem + .5vw, 1.4rem); color: var(--ink-2); letter-spacing: -0.01em;
  white-space: nowrap; text-align: center;
}
/* No card background here on purpose - flat and editorial, matching the
   rest of the gateway. Only a subtle shift + the label/desc colour change
   below (plus the desktop lift further down) mark the engaged row -
   background removed 2026-09-25, it read as a visible white rectangle. */
.gateway__lang.is-interaction-active {
  transform: translateX(.3rem);
}
/* Engaged state communicated only through colour, a fine underline and
   the small transform above - never a fill/border/shadow/radius, per
   the brief's explicit no-card rule for this page. */
.gateway__lang.is-interaction-active .gateway__lang-label {
  color: var(--ink); text-decoration: underline;
  text-decoration-color: var(--line); text-underline-offset: .35em;
}
.gateway__lang:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius); }
/* Transition choreography states. .is-fading: the two unselected
   languages, opacity only - geometry stays put so nothing jumps.
   .is-selected-moving: the chosen language, moved via a JS-measured
   transform (--gw-dx/--gw-dy, real bounding-box deltas set inline by
   assets/js/main.js, not a hardcoded guess) plus a small built-in scale
   so the whole move animates as one continuous transform rather than a
   separate resize step. Selector carries an extra .gateway__langs
   qualifier purely to out-rank .is-interaction-active's own transform
   below at equal class-count - without it, whichever rule happened to
   load later would win, which is fragile once two transform-setting
   states can land on the same element at once (a link can be both
   "hovered/focused" and "the one just chosen"). */
.gateway__langs .gateway__lang.is-fading { transition: opacity .3s var(--ease); opacity: 0; }
.gateway__langs .gateway__lang.is-selected-moving {
  transition: transform .48s var(--ease);
  transform: translate(var(--gw-dx, 0px), var(--gw-dy, 0px)) scale(1.045);
}
/* Same explicit main.js-driven sibling-dimming as the header (see the
   ".is-interacting"/".is-interaction-active" note near .nav__list):
   engaging one language eases the other two to a quieter grey.
   Class-based, not :has() - one mechanism only. */
.gateway__langs.is-interacting .gateway__lang:not(.is-interaction-active) .gateway__lang-label {
  color: var(--muted);
}
/* ≥700px: the three language destinations sit side by side as equal
   editorial columns instead of stacked rows, and the hover/focus lift
   becomes a small upward shift (translateX reads oddly once the items
   are columns, not a list). No vertical divider between them anymore
   (2026-09-25, visual-refinement pass) - three equal-width flex tracks
   framed by one shared top+bottom rule read as "table cells" the moment
   a line also runs between them; removing just that one line keeps the
   equal top/bottom rules (still wanted, per the brief) while the three
   names now stand as one open, continuous field instead of three boxed
   compartments. Inner max-width narrowed 60rem->44rem (was widened to
   60rem for "Einfache Sprache" to fit on one line per column without
   wrapping - re-verified via live measurement at 44rem before landing on
   it, still comfortably one line, per the brief's own request for a
   less "wide and flat" selection area). */
@media (min-width: 700px) {
  .gateway__inner { max-width: 44rem; }
  .gateway__langs {
    flex-direction: row; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  }
  .gateway__lang {
    flex: 1 1 0; border-bottom: none;
    padding: clamp(1.75rem, 3vh, 2.25rem) clamp(1rem, 1.6vw, 1.5rem);
  }
  .gateway__lang.is-interaction-active { transform: translateY(-3px); }
}
@media (max-width: 480px) {
  .gateway__mark { width: clamp(146px, 43vw, 205px); margin-bottom: var(--s5); }
  .gateway__welcome { margin-bottom: var(--s4); }
  .gateway__lang-label { white-space: normal; }
}
/* Leaf intensity change, phase 2 of the transition (2026-09-25, revised -
   an earlier version of this rule translated the leaf inward; Felix
   asked for it to stay spatially pixel-static instead and change only
   in visual intensity, starting the instant the confirmation statement
   begins revealing, both triggered together in main.js). No transform,
   no position/size/crop property touched anywhere in this rule - only
   opacity + filter. The source asset (botanical-editorial.webp) is a
   single-hue sepia tint, close to monochrome, so saturate() alone would
   barely register - opacity + a contrast lift is what actually reads as
   "richer" on this specific asset, with saturate() included too since
   it's harmless and covers the general case. */
/* Staged in two steps now (2026-09-26, final high-end pass): .is-warming
   is the mid-point, reached alongside the first brand phrase; .is-
   confirmed (unchanged values - the final richness already tuned in the
   colour-refinement pass) is reached alongside the second. Equal
   specificity, so source order decides which wins when both classes are
   present together - .is-confirmed declared after .is-warming means it
   correctly takes over as the final state without needing .is-warming
   removed first. Longer transition duration so each step reads as a
   slow bloom across its ~650-750ms window, not a snap. Still no
   transform anywhere in this rule - the leaf stays spatially pixel-fixed
   throughout, per Felix's explicit correction earlier this project. */
.gateway__leaf { transition: opacity .9s var(--ease), filter .9s var(--ease); }
.gateway__leaf.is-warming { opacity: .1125; filter: saturate(1.15) contrast(1.05); }
.gateway__leaf.is-confirmed { opacity: .135; filter: saturate(1.4) contrast(1.1); }
/* Mist removed entirely (2026-09-26, Felix's explicit direction after
   seeing it live): despite the previous pass's intent (see git history),
   the rendered result read as a milky/foggy overlay, not "the light in
   the room changed" - a blurred radial bloom at up to .85 opacity is
   simply visible as a cloud regardless of the tones it's built from.
   Removed rather than tuned down, since the brief was explicit: no
   overlay effect at all, page stays calm/minimal with just the leaf.
   HTML (.gateway__mist div) and JS (index.html's own inline transition
   script's mist class calls) removed to match - do not reintroduce
   without a new, explicit request. */
/* Whole-page fade immediately before navigating away (final stage) - the
   warm paper background stays exactly as-is underneath (.gateway's own
   background is untouched), so this reads as the composition quietly
   dissolving, not a colour flash. */
.gateway.is-leaving { transition: opacity .3s var(--ease); opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .gateway__lang,
  .gateway__welcome, .gateway__confirm-phrase, .gateway__confirm-dash,
  .gateway__langs, .gateway__langs .gateway__lang,
  .gateway__langs .gateway__lang.is-fading, .gateway__langs .gateway__lang.is-selected-moving,
  .gateway__leaf, .gateway.is-leaving {
    transition: none;
  }
}
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s2); }

/* ----- Typographic index (Themen) — replaces the card grid ----- */
.index { border-top: 1px solid var(--line); margin: var(--s5) 0 0; }
.index__row {
  display: grid; grid-template-columns: 1fr; gap: .35rem;
  padding: var(--s4) 0; border-bottom: 1px solid var(--line);
  text-decoration: none; color: inherit;
  transition: padding-left .25s var(--ease), background-color .25s var(--ease);
}
@media (min-width: 800px) {
  .index__row { grid-template-columns: minmax(11rem, 22%) 1fr; gap: var(--s4); align-items: baseline; }
  /* Wider title column for lists carrying long single-word German
     compounds (e.g. "Trennungsgedanken" on Paare) that would otherwise
     wrap into a one-word-plus-orphan-fragment line. */
  .index--wide-title .index__row { grid-template-columns: minmax(15rem, 24%) 1fr; }
}
/* .topic-columns already splits the page into two tracks (>=640px) - an
   .index nested inside it that ALSO tries to go title|desc two-up at
   >=800px leaves the title column too narrow for this page's longer
   single-word German compounds ("Funktionsunsicherheiten",
   "Lustunterschiede", "Fremdgehgedanken", ...), forcing real mid-word
   breaks (confirmed by measuring actual rendered line-rects, not just
   estimating). Rather than widening the column further (tried - it just
   pushes the two topic-columns tracks into a new horizontal-overflow
   bug) or breaking single words with <br>, keep title/desc stacked here
   at every width - normal word-wrap on a near-full-track-width single
   column handles any word length without splitting one - fixed 2026-09-25. */
@media (min-width: 800px) {
  .topic-columns .index__row { grid-template-columns: 1fr; gap: 0; }
  .topic-columns .index__title { display: block; margin-bottom: .2rem; }
}
a.index__row:hover, a.index__row:focus-visible { padding-left: .6rem; background: var(--paper-2); }
/* non-link index rows (editorial lists) are static */
div.index__row { transition: none; }
.index__title { font-family: var(--font-display); font-size: clamp(1.3rem, 1rem + 1vw, 1.7rem); font-weight: 440; letter-spacing: -0.02em; color: var(--ink-2); min-width: 0; overflow-wrap: break-word; }
.index__desc { color: var(--muted); font-size: 1rem; line-height: 1.55; min-width: 0; }
@media (prefers-reduced-motion: reduce) { .index__row { transition: none; } }

/* Topic image-flip cards (2026-09-27, Felix's explicit brief, homepage
   "Lebensbereiche"/"Areas of life" section - replaces the plain
   .index text-row list above with clickable image cards). Editorial,
   restrained: a flat image, a fine 1px line, generous gap, plus (added
   2026-09-27, second follow-up brief) a quiet resting elevation and a
   clearer hover/focus lift - restrained enough to still read as
   editorial rather than a SaaS card, but enough to signal these are
   objects you can pick up and turn over. Front face = the photo alone,
   no title/overlay/caption of any kind. Back face = a single centred
   topic label, white on the site's existing --earth-dark, set in the
   exact typographic system as the logo's own "PSYCHOLOGISCHE BERATUNG"
   subline (see .eyebrow) rather than a new face - just sized up here
   for a long topic phrase read from arm's length. Flip is triggered on
   click/tap/Enter/Space. Screen-reader content does NOT depend on the
   flip: the topic label is also present as permanent .visually-hidden
   text serving as the button's accessible name, so a screen-reader
   user gets it the instant the button receives focus, with no gesture
   required - the flip itself is a purely visual affordance for sighted
   pointer/touch/keyboard users, and the back face's own (identical)
   text is aria-hidden to avoid double-announcing the same phrase.

   Layout: flex+wrap+justify-content:center rather than CSS grid,
   specifically because grid's auto-placement always left-aligns a
   partial last row (7 cards in 4 columns landed as a flush 4-then-3
   with an empty trailing slot) - flexbox centers a short last row for
   free once every card shares one fixed, per-tier percentage width
   sized to fill exactly N columns (25% - 4 columns desktop, 50% - 2
   columns tablet, 100% - 1 column mobile), so the 4-then-3 desktop
   split centers itself with no JS and no nth-child bookkeeping. */
.topic-cards {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s4); margin-top: var(--s5); perspective: 1600px;
}
.topic-card {
  display: block; width: 100%; padding: 0; margin: 0; border: none;
  background: none; font: inherit; color: inherit; text-align: left;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(32,29,26,.05), 0 6px 16px rgba(32,29,26,.06);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
@media (min-width: 1024px) { .topic-card { width: calc(25% - var(--s4) * .75); } }
@media (min-width: 640px) and (max-width: 1023px) { .topic-card { width: calc(50% - var(--s4) * .5); } }
.topic-card:hover, .topic-card:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 2px 6px rgba(32,29,26,.08), 0 16px 28px rgba(32,29,26,.12);
}
.topic-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .topic-card { transition: none; } }
.topic-card__inner {
  display: block; position: relative; width: 100%; aspect-ratio: 4 / 3;
  transform-style: preserve-3d; transition: transform .32s var(--ease);
}
.topic-card.is-flipped .topic-card__inner { transform: rotateY(180deg); }
.topic-card__face {
  position: absolute; inset: 0; display: flex;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
.topic-card__face--front { background: var(--paper-2); }
.topic-card__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.topic-card__face--back {
  transform: rotateY(180deg); background: var(--earth-dark);
  align-items: center; justify-content: center; padding: 1.3rem; text-align: center;
}
.topic-card__label {
  font-family: var(--font-body); font-weight: 400; letter-spacing: .1em;
  text-transform: uppercase; color: var(--on-dark);
  font-size: clamp(.85rem, .76rem + .4vw, 1rem); line-height: 1.5;
}
@media (prefers-reduced-motion: reduce) {
  /* No 3D rotation - the back face simply replaces the front instantly.
     Both faces already occupy the same box (position:absolute/inset:0),
     so removing the transition and pre-rotating nothing here is enough;
     visibility itself still comes from backface-visibility above,
     which needs no animation to work. */
  .topic-card__inner { transition: none; }
}

/* Two .index lists placed side by side — a compact 2-column topic-
   navigation grid (Themen/Topics hero), reusing .index__row's existing
   fine-divider rows and hover/focus interaction as-is rather than
   inventing a new component. */
.topic-columns { display: grid; grid-template-columns: 1fr; column-gap: var(--s6); }
@media (min-width: 640px) { .topic-columns { grid-template-columns: 1fr 1fr; } }
/* .index__row also carrying [data-reveal] (per-item staggered entrance)
   needs its transition list merged explicitly — otherwise the higher-
   specificity [data-reveal].is-revealed rule's own `transition` shorthand
   would silently replace .index__row's hover transition instead of
   combining with it, making the hover padding-left/background jump
   instantly instead of easing. */
a.index__row[data-reveal] {
  transition: opacity .5s var(--ease), transform .5s var(--ease),
    padding-left .25s var(--ease), background-color .25s var(--ease);
}

/* ----- Editorial page system (Themen, Einzelgespräche, Paare, Familien,
   Gruppen) — a small shared toolkit so the five pages read as one system
   without looking identical. ----- */

/* Small label above a heading (2026-09-26, typography updated to match
   the logo's own "PSYCHOLOGISCHE BERATUNG" subline exactly - weight 400
   not 600, .13em tracking not .14em, --earth-dark not --muted - so this
   now reads as the site's one recurring "brand label" identity, per
   Felix's brief, rather than a generic small-caps eyebrow). Scope has
   organically grown past the original "five editorial pages only" note
   this comment used to carry - by the time this was revisited, real
   usage already spanned Über mich/About, Ablauf/Process and the
   Simple-Language pages too, always in the same non-redundant pattern
   the brief asks for (a short contextual label over a DIFFERENT
   Fraunces heading, e.g. "Kosten" over "Transparent, bevor Sie sich
   entscheiden.", never the heading's own text repeated in caps). Stays
   deliberately restrained: still not added above every H2, still never
   used for body copy, navigation or long text. */
.eyebrow {
  display: block; font-family: var(--brand-label-font); font-size: .74rem; font-weight: var(--brand-label-weight);
  letter-spacing: var(--brand-label-tracking); text-transform: uppercase; color: var(--earth-dark);
  margin: 0 0 var(--s3);
}
.section--dark .eyebrow { color: var(--on-dark-mut); }
@media (max-width: 480px) { .eyebrow { letter-spacing: var(--brand-label-tracking-tight); } }

/* A single standalone line, set large — pulled from existing copy only,
   never a new claim. The one "typographic break" per page. */
.statement {
  font-family: var(--font-display); font-style: italic; font-weight: 340;
  font-size: clamp(1.7rem, 1.15rem + 2.6vw, 2.75rem); line-height: 1.24;
  letter-spacing: -0.012em; color: var(--ink-2); max-width: 40rem;
}
.statement--center { margin-inline: auto; text-align: center; max-width: 44rem; }
.section--dark .statement { color: var(--on-dark); }

/* Editorial media slot — a fixed-ratio container for real photography,
   later. img src is swapped in place; nothing here changes when it is. */
.anchor { display: block; background: var(--paper-2); overflow: hidden; }
.anchor img { display: block; width: 100%; height: 100%; object-fit: cover; }
.anchor--4-5 { aspect-ratio: 4 / 5; }
.anchor--2-3 { aspect-ratio: 2 / 3; }
.anchor--3-2 { aspect-ratio: 3 / 2; }
.anchor--2-1 { aspect-ratio: 2 / 1; }
.anchor--16-9 { aspect-ratio: 16 / 9; }
.anchor--bleed { margin-left: calc((100% - 100vw) / 2 + var(--gutter)); }

/* ----- Formate — flat text blocks, one hairline between (no cards).
   Whole area is the link, same interaction concept as Themen's
   .index__row: a quiet background tone plus a slight shift on
   hover/focus, no underline, no arrow, no card lift. */
/* Grid lines removed (2026-09-27, Felix's explicit brief, confirmed
   against an annotated screenshot showing exactly this section's
   horizontal/vertical border lines as the thing to delete): the
   border-top/border-left rules that used to draw a literal table grid
   between the four format cards are gone. Separation now comes from
   generous gap alone - "lighter and more editorial", per the brief -
   structure, spacing and clickability are otherwise unchanged (still a
   real 2-column grid at >=720px, still one <a> per card). */
.formate { display: grid; grid-template-columns: 1fr; margin-top: var(--s5); row-gap: var(--s5); }
@media (min-width: 720px) { .formate { grid-template-columns: 1fr 1fr; column-gap: var(--s6); } }
.formate a {
  display: block; padding: 0; text-decoration: none; color: inherit;
}
.formate a.is-interaction-active { transform: translateX(.4rem); background: var(--paper-2); }
/* Differentiated title/description typography (2026-09-27), matching
   the brief's reference: restrained serif heading (h3's own sitewide
   family/weight, untouched), smaller sans description with generous
   line-height, clear price metadata as its own quiet line. */
.formate h3 { margin: 0 0 .45em; }
.formate p { margin: 0 0 .7rem; color: var(--muted); font-size: .97rem; line-height: 1.6; }
.formate .formate__price { margin: 0; color: var(--muted); font-size: .86rem; font-variant-numeric: tabular-nums lining-nums; }
/* Same explicit main.js-driven sibling-dimming as the header (see
   style.css's ".is-interacting"/".is-interaction-active" note near
   .nav__list): hovering or focusing one format destination eases the
   others' heading colour. Class-based, not :has() — one mechanism only. */
.formate a { transition: transform .25s var(--ease), background-color .25s var(--ease), color .25s var(--ease); }
.formate.is-interacting a:not(.is-interaction-active) { color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .formate a { transition: none; } }

/* ----- Realities — two equal editorial columns, no cards. Same fine
   vertical divider technique as .formate (padding + border-left on the
   second column, no gap/negative-margin trickery), but both columns
   share one heading style (.lede on the h3) and one interaction
   (.pillar-link) so neither reads as primary. */
.realities { display: grid; grid-template-columns: 1fr; margin-top: var(--s6); }
.realities__col + .realities__col { margin-top: var(--s5); }
@media (min-width: 900px) {
  .realities { grid-template-columns: 1fr 1fr; }
  .realities__col + .realities__col { margin-top: 0; }
  .realities__col:first-child { padding-right: clamp(var(--s5), 6vw, var(--s7)); }
  .realities__col:last-child { padding-left: clamp(var(--s5), 6vw, var(--s7)); border-left: 1px solid var(--line); }
}
.realities__col h3 { margin: 0; }
.realities__col h3 + p { margin-top: var(--s3); }
/* The two columns read as one grouped choice: bringing one destination
   (link or its surrounding column) into focus eases the other to a quieter
   grey, same principle as the header's sibling-dimming. .pillar-link's own
   colour is `inherit`, so dimming the column is enough to dim its link too. */
.realities__col, .realities__col .lede { transition: color .22s var(--ease); }
.realities.is-interacting .realities__col:not(.is-interaction-active),
.realities.is-interacting .realities__col:not(.is-interaction-active) .lede {
  color: var(--muted);
}
@media (prefers-reduced-motion: reduce) { .realities__col, .realities__col .lede { transition: none; } }

/* ----- Rates — editorial rows, no cards ----- */
.rates { list-style: none; border-top: 1px solid var(--line); margin: var(--s5) 0 0; padding: 0; }
.rates > li {
  display: grid; gap: .3rem; padding: var(--s4) 0; border-bottom: 1px solid var(--line); margin: 0;
}
@media (min-width: 640px) {
  .rates > li { grid-template-columns: minmax(11rem, 28%) 1fr; align-items: baseline; gap: var(--s4); }
}
.rates__name { font-family: var(--font-display); font-size: clamp(1.1rem, 1rem + .5vw, 1.35rem); color: var(--ink-2); }
.rates__meta { color: var(--muted); font-variant-numeric: tabular-nums lining-nums; }

/* ----- Format grid — quiet 2x2 overview (Ablauf/Process "Kosten"), no card
   chrome: reuses .index__title's display-serif scale for the name and
   .form__note for the meta line so no new font sizes are introduced. */
.format-grid { display: grid; grid-template-columns: 1fr; gap: var(--s6); margin: var(--s5) 0 0; }
@media (min-width: 640px) { .format-grid { grid-template-columns: 1fr 1fr; } }
.format-grid__item h3 {
  font-family: var(--font-display); font-weight: 440; letter-spacing: -0.02em;
  font-size: clamp(1.3rem, 1rem + 1vw, 1.7rem); color: var(--ink-2); margin: 0 0 .5rem;
}
.format-grid__item p { margin: 0 0 .6rem; color: var(--muted); max-width: 32rem; }
.format-grid__item p.form__note { margin-bottom: 0; font-variant-numeric: tabular-nums lining-nums; }

/* ----- Timeline — editorial rows for Berufserfahrung, same grid pattern as .rates ----- */
.timeline { list-style: none; border-top: 1px solid var(--line); margin: var(--s4) 0 0; padding: 0; }
.timeline > li {
  display: grid; gap: .3rem; padding: var(--s4) 0; border-bottom: 1px solid var(--line); margin: 0;
}
@media (min-width: 640px) {
  .timeline > li { grid-template-columns: minmax(10rem, 26%) 1fr; align-items: baseline; gap: var(--s4); }
}
.timeline__period { color: var(--muted); font-variant-numeric: tabular-nums lining-nums; font-size: .95rem; }
.timeline__role { font-family: var(--font-display); font-size: clamp(1.05rem, 1rem + .3vw, 1.2rem); color: var(--ink-2); margin: 0; }
.timeline__meta { color: var(--muted); margin: .2rem 0 0; }
.timeline__desc { color: var(--muted); margin: .5rem 0 0; }

/* ----- FAQ — restrained native accordion, no cards -----
   (Previously two overlapping rulesets both matched this markup — .faq__item
   -scoped and bare .faq details/.faq summary — with conflicting icon rules
   that combined into a rotated minus sign when open. Merged into one.) */
.faq { border-top: 1px solid var(--line); margin-top: var(--s5); }
.faq__item { border-bottom: 1px solid var(--line); padding: var(--s3) 0; }
.faq__item summary {
  font-family: var(--font-display); font-size: clamp(1.05rem, 1rem + .3vw, 1.2rem);
  color: var(--ink-2); cursor: pointer; list-style: none; padding-right: 2rem;
  position: relative; -webkit-tap-highlight-color: transparent;
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after {
  content: "+"; position: absolute; right: 0; top: 0; font-family: var(--font-body);
  font-weight: 400; color: var(--accent); font-size: 1.2rem; line-height: 1.4;
  transition: transform .18s var(--ease);
}
.faq__item[open] summary::after { transform: rotate(45deg); }
.faq__item summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.faq__item p { color: var(--muted); max-width: 60ch; margin: var(--s2) 0 0; }
@media (prefers-reduced-motion: reduce) { .faq__item summary::after { transition: none; } }

/* ----- Forms ----- */
.form { display: grid; gap: var(--s3); max-width: 34rem; }
.field { display: grid; gap: .35rem; }
.field > span { font-size: .92rem; font-weight: 600; color: var(--ink-2); }
.field .hint { font-weight: 400; color: var(--muted); font-size: .84rem; }
.field input, .field textarea, .field select {
  font: inherit; color: var(--ink); background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); padding: .75rem .9rem;
}
.field input:focus-visible, .field textarea:focus-visible, .field select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
.field textarea { min-height: 8.5rem; resize: vertical; }
.field--check { grid-template-columns: auto 1fr; align-items: start; gap: .65rem; }
.field--check input { margin-top: .25rem; width: 1.05rem; height: 1.05rem; }
.field--check span { font-weight: 400; color: var(--muted); font-size: .92rem; }
.form__note { color: var(--muted); font-size: .86rem; }

/* ----- Notice ----- */
.notice { border-left: 2px solid var(--accent); background: var(--card); padding: var(--s3) var(--s4); }
.notice h3 { margin-top: 0; }
.notice p:last-child { margin-bottom: 0; }
/* Non-heading status label (e.g. "Entwurf"/"Draft" on the still-drafted
   legal pages) - deliberately not .eyebrow, which CLAUDE.md scopes to five
   specific editorial pages only. A heading here would sit between the
   page's own H1 and its first real H2, skipping a level for a marker that
   isn't a section title. */
.notice__status { font-weight: 600; font-size: .85rem; color: var(--muted); margin: 0 0 .5rem; }

/* ----- Portrait / figures ----- */
figure { margin: 0; }
.portrait { background: var(--paper-2); overflow: hidden; }
.portrait img { width: 100%; }
figcaption { font-size: .82rem; color: var(--muted); margin-top: .5rem; }
/* Homepage personal-introduction portrait — an approved brand/personal element
   (wheelchair-visible portrait + signature). Shown whole: the wheelchair stays
   visible at every breakpoint, so it is never height-cropped. Keep on the
   homepage through future simplification passes. */
.portrait--intro img { display: block; width: 100%; height: auto; object-fit: contain; }
/* Follows the text column while scrolling, but only within its own grid row
   (the row's height is set by the taller text column, so the sticky image
   naturally stops around the signoff instead of trailing into the next
   section) — a calm reading aid, not a page-wide fixed image. */
@media (min-width: 900px) {
  .portrait--intro {
    align-self: start; max-width: 30rem;
    position: sticky; top: calc(var(--header-h) + var(--s4));
  }
}
/* Über mich: seated portrait runs taller and bleeds to the page edge on wide screens.
   max-height keeps it sized to roughly its text column's height instead of
   stretching to the image's own full intrinsic aspect ratio — at a wide
   column width that intrinsic height can run to 2x a short paragraph block. */
@media (min-width: 1000px) {
  /* Was max-height: 40rem, which - combined with object-position: center
     top on a source image markedly wider than the box (900x1200 into a
     ~822px-wide, 640px-tall box) - cropped out the entire lower ~42% of
     the frame, including the wheelchair. Confirmed by simulating the
     exact object-fit:cover math against the real source file: at 40rem
     the crop ends at the shirt hem, wheelchair completely absent -
     violates CLAUDE.md's explicit "never height-crop the wheelchair out"
     rule for this photo. Raised to 56rem, the point where the wheelchair
     becomes clearly, unambiguously visible (armrest + wheel) rather than
     an ambiguous sliver - fixed 2026-09-25. This does make the portrait
     taller relative to its short paired paragraph than the original
     40rem was deliberately tuned for (see this rule's own prior
     comment) - a real trade-off, worth a visual sanity check by Felix,
     not just a mechanical fix. */
  .portrait--tall { align-self: stretch; max-height: 56rem; }
  .portrait--tall img { height: 100%; object-fit: cover; object-position: center top; }
  .portrait--bleed { margin-left: calc((100% - 100vw) / 2 + var(--gutter)); }
  /* The taller portrait (above) noticeably outgrows its short paired
     paragraph. align-self:center (tried first) left too much dead space
     above the text - Felix's own feedback (2026-09-25) was that the text
     column starts too low and the pairing doesn't read as one unit.
     Anchored to the top with a tuned margin-top instead, landing the
     text roughly in the portrait's own upper third rather than dead
     centre or flush top - on both pages that use this exact
     figure+prose pairing (checked - only these two). */
  .portrait--tall + .prose { align-self: start; margin-top: 15rem; }
}
/* Editorial landscape photograph (session / workspace) — shown at its full
   natural aspect, never force-cropped, so nothing in the frame is cut away. */
.portrait--wide img { display: block; width: 100%; height: auto; object-fit: contain; }
@media (min-width: 900px) { .portrait--wide { align-self: center; } }

/* Personal sign-off */
.signoff { margin-top: var(--s5); }
.signoff__name { font-family: var(--font-display); font-size: 1.05rem; color: var(--ink-2); margin: 0; }
/* 188px -> 213px (~13%, within the requested 10-15%) - aspect ratio is
   untouched (height:auto), so this is a pure scale-up, not a crop. A
   border-bottom stands in for a cue "beneath the signature": ::after
   generated content doesn't render on replaced elements like <img>, so a
   real border is the only way to draw a line flush under the image
   itself rather than under some wrapping box. */
.signature {
  display: block; width: 213px; height: auto; margin: .4rem 0 0; opacity: .8;
  border-bottom: 1px solid var(--line); padding-bottom: .5rem;
  transition: opacity .22s var(--ease), transform .22s var(--ease), border-color .22s var(--ease);
}
/* When the signature block itself is the link to About (homepage), instead
   of a separate "Mehr über mich" line: no underline, no link colour, no
   button look — just a quiet brightening on hover/focus, matching the
   restraint of the rest of the editorial link system. The permanent
   border-bottom above is the "this is clickable" cue at rest; hover/focus
   darkens it further rather than only relying on the image brightening. */
a.signoff { display: block; text-decoration: none; color: inherit; width: max-content; }
a.signoff .signoff__name { transition: color .22s var(--ease); }
a.signoff:hover .signoff__name, a.signoff:focus-visible .signoff__name { color: var(--ink); }
a.signoff:hover .signature, a.signoff:focus-visible .signature {
  opacity: 1; transform: translateY(-2px); border-color: var(--ink-2);
}
a.signoff:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) {
  a.signoff .signoff__name, .signature { transition: none; }
}

/* One-time image clip reveal */
[data-img-reveal] { clip-path: inset(0 0 0 0); }
@media (prefers-reduced-motion: no-preference) {
  [data-img-reveal].is-armed { clip-path: inset(0 100% 0 0); }
  [data-img-reveal].is-revealed { clip-path: inset(0 0 0 0); transition: clip-path .9s var(--ease); }
}

/* One-time text reveal — same is-armed/is-revealed IntersectionObserver
   pattern as [data-img-reveal] above, applied to type instead of images.
   Used sparingly: [data-reveal="strong"] for a page's few headline
   moments, [data-reveal="soft"] for a lighter touch (e.g. a subheading).
   Never on running body copy, prices, buttons or forms. */
@media (prefers-reduced-motion: no-preference) {
  [data-reveal].is-armed { opacity: 0; transform: translateY(14px); }
  [data-reveal].is-revealed {
    opacity: 1; transform: none;
    transition: opacity .7s var(--ease), transform .7s var(--ease);
  }
  [data-reveal="soft"].is-armed { transform: translateY(8px); }
  [data-reveal="soft"].is-revealed { transition-duration: .5s; }
}

/* ----- Footer ----- */
/* Same --earth-dark as the CTA above it, deliberately, not a third
   "slightly deeper" shade - the brief allows either, and with only two
   structural dark surfaces on the whole site (CTA, footer) a matched
   pair reads as one calm dark family; introducing a second shade for
   this alone risked exactly the "three different browns" failure mode
   the brief explicitly warns against. */
.site-footer { background: var(--earth-dark); color: var(--on-dark); padding-block: var(--s5); margin-top: var(--s7); }
.site-footer a { color: var(--on-dark); text-decoration: none; }
.site-footer a:hover { color: var(--paper); text-decoration: underline; }
.site-footer__grid { display: grid; gap: var(--s4); grid-template-columns: 1fr; }
@media (min-width: 760px) { .site-footer__grid { grid-template-columns: 2fr 1fr 1fr; } }
/* Was font-weight:600/letter-spacing:.1em before 2026-09-27 - close
   enough to the brand-label voice to look "almost right" but not
   provably the same, which reads as a generic bold all-caps UI label
   rather than the quiet logo-derived voice repeated here. Now
   reads from the same tokens as .eyebrow/.brand__word. */
.site-footer h4 { color: var(--paper); font-family: var(--brand-label-font); font-size: .78rem; letter-spacing: var(--brand-label-tracking); text-transform: uppercase; margin: 0 0 var(--s2); font-weight: var(--brand-label-weight); }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin: .32em 0; }
.site-footer__logo { display: block; height: 56px; width: auto; margin-bottom: var(--s3); }
.site-footer__brand { font-family: var(--font-display); font-size: 1.15rem; color: var(--paper); margin-bottom: var(--s2); display: block; }
/* Uniform tiles (same box size regardless of each logo's own aspect
   ratio) so four differently-shaped marks (wide/landscape, square,
   tall/portrait) read as one calm, evenly aligned row rather than a
   ragged strip of differently sized images. Background is --paper, not
   pure #fff, per the brief - these hold unmodified THIRD-PARTY logo
   marks (PTK Berlin, BDP, ...), not our own brand colour, so this is a
   neutral card surface for them to sit on, not itself a "brand" choice -
   still switched for overall palette consistency since off-white reads
   no differently to those logos and keeps the footer from having one
   stark-white patch against the warm dark background. */
.member-badges { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin-top: var(--s2); }
.member-badges__tile {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 4.25rem; height: 2.75rem; background: var(--paper); border-radius: var(--radius);
  padding: .4rem; transition: transform .2s var(--ease);
}
.member-badges__tile img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; }
.member-badges__tile:hover { transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .member-badges__tile { transition: none; } }

/* Membership list — each badge paired with its own description, used on
   ueber-mich.html's "Qualifikation und Mitgliedschaften". Not a bulleted
   <li> like the plain qualification lines above it: badge + text as one
   row, so the logo reads as that specific membership's mark, not a
   generic footer strip repeated mid-page. */
.member-list { list-style: none; margin: var(--s3) 0 0; padding: 0; }
.member-list li {
  display: flex; align-items: center; gap: .85rem;
  padding: .55rem 0; border-top: 1px solid var(--line-soft);
}
.member-list li:last-child { border-bottom: 1px solid var(--line-soft); }
.member-list img {
  display: block; flex: none; height: 30px; width: auto;
  background: #fff; padding: .3rem .5rem; border-radius: var(--radius);
}
.member-list span { color: var(--ink-2); font-size: .97rem; line-height: 1.4; }
.site-footer__legal { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid rgba(233,232,227,.14); font-size: .82rem; color: var(--on-dark-mut); display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: space-between; }

/* ----- Active memberships — editorial logo reveal -----
   Muted/grayscale at rest, on purpose (a quiet detail, not a sponsor
   wall). Hover, keyboard focus and tap/click all land on the exact same
   .is-open-equivalent visual state, so no information is hover-only.
   Each item is a <button> (not a link — no verified outbound URLs exist
   for these organisations, so none were invented; nothing to visit).
   Reusable as-is on /en/ later: only the visible text differs. */
.affiliations { display: flex; flex-wrap: wrap; gap: var(--s4) var(--s5); list-style: none; margin: var(--s4) 0 0; padding: 0; }
.affiliation {
  display: flex; flex-direction: column; align-items: flex-start; gap: .5rem;
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: inherit; text-align: left; -webkit-tap-highlight-color: transparent;
}
.affiliation img {
  display: block; height: 42px; width: auto;
  filter: grayscale(1); opacity: .5;
}
.affiliation__rule {
  display: block; width: 2.25rem; height: 1px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
}
.affiliation__label {
  display: block; max-width: 13rem; font-size: .82rem; line-height: 1.4; color: var(--muted);
  opacity: 0; transform: translateY(-4px);
}
/* Below ~480px, the label's own max-width (13rem/208px) was setting each
   .affiliation item's layout width even at rest (the label stays in flow,
   just invisible, until hover/tap), leaving only one item per row with a
   lot of unused space beside each small logo - a real "weak use of
   space" issue on mobile, not just a subjective read. Narrowing the
   label and the row's own gap here lets two logos sit side by side
   instead, closer to how the desktop row reads - fixed 2026-09-25. */
@media (max-width: 480px) {
  .affiliations { gap: var(--s3) var(--s3); }
  .affiliation__label { max-width: 8.5rem; }
}
.affiliation:hover img, .affiliation:focus-visible img, .affiliation.is-open img { opacity: 1; filter: grayscale(0); }
.affiliation:hover .affiliation__rule, .affiliation:focus-visible .affiliation__rule, .affiliation.is-open .affiliation__rule { transform: scaleX(1); }
.affiliation:hover .affiliation__label, .affiliation:focus-visible .affiliation__label, .affiliation.is-open .affiliation__label { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .affiliation img, .affiliation__rule, .affiliation__label { transition: .22s ease-out; transition-property: opacity, filter, transform; }
}

/* ----- Evidence module -----
   Sourced, population-level context figures (Men / Disability pillar pages).
   Default: figure + short label, muted. Hover/focus/tap moves the item into
   focus and reveals its citation via a 0fr->1fr grid-rows animation (no
   layout jump, no fixed height guess). Same click-toggle-for-touch pattern
   as .affiliation above. */
.evidence { list-style: none; margin: var(--s4) 0 0; padding: 0; display: grid; gap: var(--s3) var(--s5); }
@media (min-width: 640px) { .evidence { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); } }
.evidence__item { border-top: 1px solid var(--line-soft); padding-top: var(--s3); }
.evidence__toggle {
  display: block; width: 100%; text-align: left; background: none; border: none; padding: 0;
  margin: 0; font: inherit; color: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.evidence__figure {
  display: block; font-family: var(--font-display); font-weight: 440;
  font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem); line-height: 1.05; color: var(--muted);
}
.evidence__label { display: block; margin-top: .4rem; font-size: .92rem; color: var(--muted); max-width: 22rem; }
.evidence__detail { display: grid; grid-template-rows: 0fr; }
.evidence__detail > div { overflow: hidden; min-height: 0; }
.evidence__detail p { margin: var(--s3) 0 0; color: var(--muted); font-size: .92rem; line-height: 1.55; max-width: 24rem; }
.evidence__detail cite { display: block; margin-top: .5rem; font-style: normal; font-size: .78rem; color: var(--muted); opacity: .75; }
.evidence__toggle:hover .evidence__figure, .evidence__toggle:focus-visible .evidence__figure,
.evidence__item.is-open .evidence__figure { color: var(--ink); }
.evidence__toggle:hover .evidence__label, .evidence__toggle:focus-visible .evidence__label,
.evidence__item.is-open .evidence__label { color: var(--ink-2); }
.evidence__toggle:hover + .evidence__detail, .evidence__toggle:focus-visible + .evidence__detail,
.evidence__item.is-open .evidence__detail { grid-template-rows: 1fr; }
.evidence__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
@media (prefers-reduced-motion: no-preference) {
  .evidence__figure, .evidence__label, .evidence__detail { transition: color .22s var(--ease), grid-template-rows .28s var(--ease); }
}
@media (prefers-reduced-motion: reduce) { .evidence__detail { grid-template-rows: 1fr; } }

/* ----- Motion -----
   The one orchestrated moment: the hero entrance. Pure CSS animation (runs on
   load, no JS, completes even if the tab is throttled). Content is fully visible
   with reduced-motion or if animations are unsupported. */
@media (prefers-reduced-motion: no-preference) {
  /* A gentle settle. No opacity — content is never hidden pending motion
     (fill-mode default 'none' → normal position before/after/during delay). */
  .hero-seq > * { animation: hero-in .65s var(--ease); }
  .hero-seq > :nth-child(2) { animation-delay: .07s; }
  .hero-seq > :nth-child(3) { animation-delay: .14s; }
  .hero-seq > :nth-child(4) { animation-delay: .21s; }
  .hero-seq > :nth-child(5) { animation-delay: .28s; }

  /* scroll-triggered, JS-armed only (default state = shown) */
  .rule.is-armed { transform: scaleX(0); }
  .rule.is-drawn { transform: scaleX(1); transition: transform .6s var(--ease); }
}
@keyframes hero-in {
  from { transform: translateY(15px); opacity: .55; }
  60%  { opacity: 1; }
  to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-seq > * { animation: none; }
  [data-img-reveal] { clip-path: none !important; }
  .rule { transform: none !important; }
}

/* ----- Legal pages ----- */
.legal { padding-block: var(--s6); }
.legal .wrap { max-width: var(--measure); }
.legal h1 { margin-bottom: var(--s5); }
/* Legal-page H1s ("Datenschutzerklärung", "Impressum") are single long
   words or short plain labels, not hero headlines - they don't need the
   sitewide h1's full hero-scale clamp, which clipped "Datenschutzerklärung"
   at the right edge on narrow phones (confirmed ~19px overflow at 390px,
   ~3px at 430px - one long unbroken German compound word with no natural
   break point, so normal word-wrap can't help). Scoped smaller clamp
   fixes it without touching the sitewide h1 scale used everywhere else -
   fixed 2026-09-25. */
@media (max-width: 480px) {
  .legal h1 { font-size: clamp(1.9rem, 1rem + 4.5vw, 2.5rem); }
}
.legal h2 { font-size: var(--text-h2-compact); margin-top: var(--s5); }
.legal p, .legal li { color: var(--ink); }

/* Legal-page identity strip: logo + heading + portrait, wider than the
   narrow reading column below it so the portrait has room without
   competing with the legal text's line length. */
.legal .wrap.legal-intro {
  display: grid; grid-template-columns: 1fr; gap: var(--s4);
  align-items: start; margin-bottom: var(--s6); padding-bottom: var(--s5);
  border-bottom: 1px solid var(--line);
}
.legal-intro__logo { display: block; height: 40px; width: auto; margin-bottom: var(--s3); }
.legal-intro__portrait { max-width: 14rem; }
@media (min-width: 900px) {
  .legal .wrap.legal-intro { max-width: var(--maxw); grid-template-columns: 7fr 5fr; gap: clamp(var(--s5), 6vw, var(--s6)); }
  .legal-intro__portrait { max-width: 16rem; justify-self: end; }
}
.placeholder { background: #F3E7C8; color: #6c4f14; padding: .05em .35em; border-radius: 3px; font-size: .92em; font-family: var(--font-body); }
.tag { display: inline-block; font-size: .72rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--accent-ink); border: 1px solid var(--accent); padding: .12rem .5rem; border-radius: 999px; }
