/* ============================================================================
   ecoPi components
   Replaces the per-page variants: .clean-card / .value-card / .feature-karte /
   .project-card all become .ep-card, and so on. Built only from tokens.
   ========================================================================= */

/* ---- Section ------------------------------------------------------------
   Owns vertical rhythm. Variants set the surface. */

.ep-section {
  padding-block: var(--ep-section-y);
  background: var(--ep-bg-page);
}

.ep-section--alt {
  background: var(--ep-bg-alt);
}

.ep-section--card {
  background: var(--ep-bg-card);
}

.ep-section--inverse {
  background: var(--ep-bg-inverse);
  color: var(--ep-text-inverse);
}

/* Closing CTA band ("Fragen...?" / "Bereit für den nächsten Schritt?" etc.)
   repeated near-identically at the bottom of several pages. Tighter top gap
   than the generic .ep-section rhythm: the block right above it already ends
   with its own bottom padding, so the full --ep-section-y on both sides
   stacked into too much dead space. */
.ep-section--cta {
  padding-top: 5rem;
}

/* ecopi-base.css sets `h1,h2,h3,h4 { color: var(--ep-text) }` as an element
   selector, which overrides inheritance. Any section that relied on inheriting
   white from its parent rendered dark-on-dark instead — the ecoPi page's
   statement band measured 1.28:1. Inverse surfaces must therefore restate the
   heading colour rather than rely on inheritance. */
.ep-section--inverse :is(h1, h2, h3, h4, h5, h6) {
  color: var(--ep-text-inverse);
}

.ep-section--inverse p,
.ep-section--inverse li {
  color: var(--ep-text-inverse-muted);
}

/* Inline links only. `.ep-section--inverse a` (0,1,1) outranks
   `.ep-btn--on-inverse` (0,1,0), so without this exclusion a white CTA button
   inside an inverse band got pale teal text on white — 1.9:1. */
.ep-section--inverse a:not([class*="ep-btn"]):not([class*="ep-pill"]) {
  color: var(--ep-primary-light);
}

/* ---- Section heading ---------------------------------------------------- */

/* The block itself is unconstrained; the measure is applied to the lead, which
   is what actually needs a readable line length. Capping the whole block at
   56ch also capped the heading, so "What ecoPi delivers in the field" wrapped
   to two lines at 1280px and above while the German fitted on one. */
.ep-section-heading {
  margin-bottom: var(--ep-space-7);
}

.ep-section-heading > h2,
.ep-section-heading > h3 {
  /* Wide enough for the longer of the two languages on one line. */
  max-width: 34ch;
  text-wrap: balance;
}

.ep-section-heading > .ep-lead {
  max-width: var(--ep-measure-lead);
}

.ep-section-heading--center {
  text-align: center;
}

.ep-section-heading--center > h2,
.ep-section-heading--center > h3,
.ep-section-heading--center > .ep-lead {
  margin-inline: auto;
}

.ep-eyebrow {
  display: block;
  margin: 0 0 var(--ep-space-3);
  color: var(--ep-primary);
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.ep-section--inverse .ep-eyebrow {
  color: var(--ep-primary-light);
}

.ep-lead {
  margin: 0;
  color: var(--ep-text-secondary);
  font-size: var(--ep-text-lg);
}

.ep-section-index {
  display: block;
  margin-bottom: var(--ep-space-3);
  color: var(--ep-primary);
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
}

/* ---- Page header ---------------------------------------------------------
   The opening band of every interior page. Replaces the ad-hoc
   `.block-heading` + inline `margin-bottom:60px` pattern that each page
   carried its own copy of. */

.ep-page-header {
  padding-block: var(--ep-space-8) var(--ep-space-7);
  background: var(--ep-bg-alt);
  border-bottom: 1px solid var(--ep-border);
}

.ep-page-header h1 {
  max-width: var(--ep-measure-head);
  margin: 0;
}

.ep-page-header .ep-lead {
  max-width: var(--ep-measure-lead);
  margin-top: var(--ep-space-4);
}

.ep-page-header--center {
  text-align: center;
}

.ep-page-header--center h1,
.ep-page-header--center .ep-lead {
  margin-inline: auto;
}

/* The technology page opens on a statement rather than a label, so its header
   runs taller and the headline is given room to breathe. */
.ecopi-hero {
  padding-block: var(--ep-space-9) var(--ep-space-8);
}

.ecopi-hero h1 {
  max-width: 18ch;
  margin-bottom: var(--ep-space-5);
}

/* ---- Prose ---------------------------------------------------------------
   Long-form body copy: sets the measure once so paragraphs never run to the
   full container width. */

.ep-prose {
  max-width: var(--ep-measure);
  color: var(--ep-text-secondary);
}

.ep-prose > * + * {
  margin-top: var(--ep-space-4);
}

.ep-prose h2 {
  margin-top: var(--ep-space-7);
  color: var(--ep-text);
}

.ep-prose > h2:first-of-type {
  margin-top: 0;
}

.ep-prose h3 {
  margin-top: var(--ep-space-6);
  color: var(--ep-text);
}

.ep-prose ul,
.ep-prose ol {
  padding-left: 1.25rem;
}

.ep-prose li + li {
  margin-top: var(--ep-space-2);
}

.ep-prose--narrow {
  max-width: var(--ep-measure-lead);
}

/* ---- Back link ------------------------------------------------------------ */

.ep-backlink {
  display: inline-flex;
  align-items: center;
  gap: var(--ep-space-2);
  min-height: 44px;                      /* WCAG 2.2 target size */
  color: var(--ep-primary);
  font-size: var(--ep-text-sm);
  font-weight: 600;
  text-decoration: none;
}

.ep-backlink:hover {
  color: var(--ep-primary-hover);
  text-decoration: underline;
}

/* ---- Definition table -----------------------------------------------------
   Two-column "what you get" table. Collapses to stacked blocks below the md
   breakpoint rather than scrolling, so the row label stays with its list. */

.ep-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--ep-bg-card);
  border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-lg);
  overflow: hidden;
}

.ep-table th,
.ep-table td {
  padding: var(--ep-space-4) var(--ep-space-5);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--ep-border);
}

.ep-table thead th {
  background: var(--ep-bg-alt);
  color: var(--ep-text);
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.ep-table tbody th {
  width: 30%;
  color: var(--ep-text);
  font-weight: 700;
}

.ep-table tbody tr:last-child th,
.ep-table tbody tr:last-child td {
  border-bottom: 0;
}

@media (max-width: 767.98px) {
  .ep-table,
  .ep-table tbody,
  .ep-table tr,
  .ep-table th,
  .ep-table td {
    display: block;
  }

  .ep-table thead {
    /* The column headers only make sense side by side. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .ep-table tbody th {
    width: auto;
    padding-bottom: 0;
    border-bottom: 0;
  }
}

/* ---- Checklist ----------------------------------------------------------- */

.ep-checklist {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--ep-text-secondary);
}

.ep-checklist li {
  position: relative;
  padding-left: var(--ep-space-6);
}

.ep-checklist li + li {
  margin-top: var(--ep-space-2);
}

.ep-checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 0.7rem;
  height: 0.38rem;
  border-left: 2px solid var(--ep-accent);
  border-bottom: 2px solid var(--ep-accent);
  transform: rotate(-45deg);
}

/* ---- Button ------------------------------------------------------------- */

.ep-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ep-space-2);
  min-height: 44px;                      /* WCAG 2.2 target size */
  padding: var(--ep-space-3) var(--ep-space-5);
  border: 1px solid transparent;
  border-radius: var(--ep-radius-md);
  background: var(--ep-primary);
  color: #fff;                           /* 6.41:1 on --ep-primary */
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-base);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--ep-duration-fast) var(--ep-ease),
              border-color var(--ep-duration-fast) var(--ep-ease),
              color var(--ep-duration-fast) var(--ep-ease);
}

.ep-btn:hover {
  background: var(--ep-primary-hover);
  color: #fff;
}

.ep-btn:active {
  background: var(--ep-primary-active);
}

.ep-btn--secondary {
  background: transparent;
  border-color: var(--ep-primary);
  color: var(--ep-primary);
}

.ep-btn--secondary:hover {
  background: var(--ep-primary);
  color: #fff;
}

.ep-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--ep-primary);
  padding-inline: var(--ep-space-3);
}

.ep-btn--ghost:hover {
  background: var(--ep-primary-faint);
  color: var(--ep-primary-hover);
}

.ep-btn--on-inverse {
  background: #fff;
  color: var(--ep-bg-inverse);
}

.ep-btn--on-inverse:hover {
  background: var(--ep-primary-light);
  color: var(--ep-bg-inverse);
}

.ep-btn__icon {
  flex-shrink: 0;
}

.ep-btn--sm {
  min-height: 36px;
  padding: var(--ep-space-2) var(--ep-space-4);
  font-size: var(--ep-text-sm);
}

/* ---- Card --------------------------------------------------------------- */

.ep-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-lg);
  background: var(--ep-bg-card);
  transition: transform var(--ep-duration-base) var(--ep-ease),
              box-shadow var(--ep-duration-base) var(--ep-ease),
              border-color var(--ep-duration-base) var(--ep-ease);
}

.ep-card--interactive:hover {
  transform: translateY(-3px);
  border-color: var(--ep-border-strong);
  box-shadow: var(--ep-shadow-md);
}

.ep-card__media {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--ep-bg-alt);
}

/* Carries a text label instead of an image, so it must not be forced to a
   photographic aspect ratio — long labels have to be able to wrap. */
.ep-card__media--label {
  aspect-ratio: auto;
  min-height: 11rem;
}

.ep-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ep-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--ep-space-5);
}

.ep-card__title {
  margin: 0 0 var(--ep-space-2);
  font-family: var(--ep-font-body);
  font-size: var(--ep-h4);
  font-weight: 700;
  line-height: var(--ep-leading-snug);
}

.ep-card__text {
  margin: 0 0 var(--ep-space-4);
  color: var(--ep-text-secondary);
}

/* The whole card is clickable, but only the title text is the link, so the
   accessible name is the headline rather than the entire card contents. The
   old markup wrapped image + title + teaser in one <a>, which read out the
   full card as a single link. */
.ep-card__link {
  color: inherit;
  text-decoration: none;
}

.ep-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.ep-card__link:hover {
  color: var(--ep-primary);
  text-decoration: underline;
}

.ep-card:has(.ep-card__link) {
  position: relative;
}

/* Keep the focus ring on the card, not on the invisible stretched box. */
.ep-card:has(.ep-card__link:focus-visible) {
  outline: 3px solid var(--ep-primary);
  outline-offset: 2px;
}

.ep-card__link:focus-visible {
  outline: none;
}

.ep-list-reset {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ep-card__teaser {
  margin: 0 0 var(--ep-space-4);
  color: var(--ep-primary);
  font-size: var(--ep-text-sm);
  font-style: italic;
}

.ep-card__meta {
  display: block;
  margin-top: var(--ep-space-3);
  color: var(--ep-text-muted);
  font-size: var(--ep-text-sm);
}

/* Circular icon badge. Started on the home page's flow diagram, then reused
   on /workflow.html and /pricing.html — shared here so all three read as one
   visual system rather than three near-identical rules. */
.ep-flow__icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--ep-primary);
  color: var(--ep-text-inverse);
}

.ep-flow__icon svg {
  width: 1.375rem;
  height: 1.375rem;
}

/* On the home page the badge doubles as a tooltip trigger (a real <button>,
   so it's reachable and announced by keyboard/AT); on /workflow.html and
   /gesamtpaget.html it's still a plain, non-interactive <span> — reset only
   applies when the badge actually is a button. */
button.ep-flow__icon {
  border: 0;
  padding: 0;
  font: inherit;
  cursor: help;
  transition: background-color 0.15s ease;
}

button.ep-flow__icon:hover,
button.ep-flow__icon:focus-visible {
  background: var(--ep-primary-hover);
}

button.ep-flow__icon:focus-visible {
  outline: 2px solid var(--ep-primary);
  outline-offset: 2px;
}

/* Tooltip is a sibling of the button and label, not a child of the button —
   otherwise its text would become part of the button's accessible name
   instead of a separate description. It's positioned relative to the whole
   step (not just the badge) so it clears the label underneath the badge
   rather than covering it. */
.ep-flow__step {
  position: relative;
}

/* Mobile default: an in-flow reveal that pushes the steps below it down,
   rather than an overlay — the column layout stacks steps close enough that
   an absolutely-positioned tooltip would sit on top of the next one or two
   steps instead of the empty space it has on the row layout at 1100px+
   (overridden there in journey.css). Expands via a max-height transition
   (height:auto isn't transitionable) capped well above the tallest real
   tooltip so nothing gets clipped. A grid-template-rows 0fr→1fr version was
   tried first but never actually reached 0px — it settled at one line's
   height, leaving a sliver of the next tip's text visible underneath.
   Hidden with opacity, never display:none or visibility:hidden: content
   reachable via aria-describedby must stay in the accessibility tree so
   screen readers announce it on focus regardless of visual state. */
.ep-flow__tip {
  /* Root cause of the collapse never actually reaching 0: a <span> is
     display:inline by default, and inline elements ignore max-height
     entirely — it was a no-op until this was set explicitly. */
  display: block;
  max-height: 0;
  width: 16rem;
  margin-top: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 0.2s ease, opacity 0.15s ease, margin-top 0.2s ease;
}

.ep-flow__tip-inner {
  /* Without this, the <span> stays display:inline and its background/padding
     wrap each line of text separately instead of forming one fixed-size
     bubble — the "hugging the text" look. */
  display: block;
  width: 100%;
  padding: var(--ep-space-3);
  border-radius: var(--ep-radius-md);
  background: var(--ep-bg-raised);
  box-shadow: var(--ep-shadow-md);
  color: var(--ep-text-secondary);
  font-size: var(--ep-text-sm);
  font-weight: 400;
  line-height: var(--ep-leading-body);
}

.ep-flow__step:hover .ep-flow__tip,
.ep-flow__step:focus-within .ep-flow__tip {
  max-height: 20rem;
  margin-top: var(--ep-space-2);
  opacity: 1;
}

.ep-card__actions {
  margin-top: auto;
  padding-top: var(--ep-space-2);
}

/* When a card carries a .ep-audio-samples list (applications.html), that
   list's own auto top/bottom margins (audio-spectrogram.css) already claim
   the free space in the card to centre it — ceding this auto margin too
   would split that space between both, leaving the list off-centre. */
.ep-card__body:has(.ep-audio-samples) > .ep-card__actions {
  margin-top: 0;
}

/* .ep-btn is inline-flex, so it no longer picks up the full-width stretch
   .ep-card__body's flex column gave it directly — restored explicitly here. */
.ep-card__actions > .ep-btn {
  width: 100%;
}

/* Cards in a row stretch to equal height, so a trailing disclosure would
   otherwise sit at whatever height its own text ended — leaving dead space
   under the shorter card and misaligning the summary rows across the row. */
.ep-card__body > .ep-disclosure:last-child {
  margin-top: auto;
}

/* ---- Grid ---------------------------------------------------------------
   auto-fit means columns can never be pinned at a count that breaks on
   small screens (the bug that made live-monitoring.html unusable). */

/* `min(<track>, 100%)` matters: a bare minmax(21rem, 1fr) cannot shrink below
   21rem, so at 320px the track stayed 336px wide and pushed the document
   sideways — the same failure mode as the row-cols-4 bug, just with a
   different number. The min() lets the track collapse to the container. */
.ep-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: var(--ep-space-5);
}

.ep-grid--wide {
  grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
}

.ep-grid--narrow {
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}

/* Exactly two columns, for sets of four that must read as a 2x2 block.
   auto-fit would give three columns plus an orphan at 1440px. */
.ep-grid--2 {
  grid-template-columns: 1fr;
}

@media (min-width: 48em) {
  .ep-grid--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---- Disclosure (details/summary) --------------------------------------- */

.ep-disclosure {
  border-top: 1px solid var(--ep-border);
}

.ep-disclosure > summary {
  position: relative;
  padding: var(--ep-space-4) var(--ep-space-8) var(--ep-space-4) 0;
  color: var(--ep-primary);
  font-weight: 700;
  list-style: none;
  cursor: pointer;
}

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

.ep-disclosure > summary::after {
  content: "+";
  position: absolute;
  top: 50%;
  right: 0;
  display: grid;
  place-items: center;
  width: 1.65rem;
  height: 1.65rem;
  border: 1px solid var(--ep-border);
  border-radius: 50%;
  font-size: 1rem;
  line-height: 1;
  transform: translateY(-50%);
  transition: background var(--ep-duration-fast) var(--ep-ease);
}

.ep-disclosure[open] > summary::after {
  content: "\2212";
  background: var(--ep-bg-faint);
}

.ep-disclosure__body {
  padding-bottom: var(--ep-space-4);
  color: var(--ep-text-secondary);
}

/* ---- Pill navigation ----------------------------------------------------- */

.ep-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ep-space-3);
}

.ep-pills--center {
  justify-content: center;
}

.ep-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--ep-space-2);
  min-height: 40px;
  padding: var(--ep-space-2) var(--ep-space-5);
  border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-pill);
  background: var(--ep-bg-card);
  color: var(--ep-primary);
  font-weight: 600;
  font-size: var(--ep-text-sm);
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--ep-duration-fast) var(--ep-ease),
              background var(--ep-duration-fast) var(--ep-ease);
}

.ep-pill:hover,
.ep-pill[aria-current="true"] {
  border-color: var(--ep-primary);
  background: var(--ep-primary-faint);
  color: var(--ep-primary-hover);
}

/* ---- Media frame --------------------------------------------------------- */

.ep-figure {
  margin: 0;
}

.ep-figure img {
  width: 100%;
  border-radius: var(--ep-radius-lg);
}

/* For images with no high-resolution version to fall back on. width: 100% will
   happily blow a 490px render up to 616px and it turns to mush; this caps the
   display size at the file's own pixels and still shrinks on narrow screens. */
.ep-figure--natural img {
  width: auto;
  max-width: 100%;
  height: auto;
  margin-inline: auto;
}

.ep-figure figcaption {
  margin-top: var(--ep-space-2);
  color: var(--ep-text-muted);
  font-size: var(--ep-text-sm);
}

/* Was position: sticky, but the parent column (align-items-start, single
   child) always sizes to exactly this figure's own height, leaving sticky
   no room to move — dead weight that only cost a resize-repositioning bug in
   Chromium where the figure could render at a stale offset until the next
   scroll, landing on top of the text column's content next to it. */
.ep-figure--sticky {
  position: static;
}

/* ---- Article --------------------------------------------------------------
   A single blog post. The measure is deliberately narrower than --ep-measure:
   posts are read start to finish, not scanned. */

.ep-article {
  max-width: 44rem;
  margin-inline: auto;
}

.ep-article__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ep-space-3);
  margin-bottom: var(--ep-space-3);
  color: var(--ep-text-muted);
  font-size: var(--ep-text-sm);
}

.ep-article h1 {
  margin-bottom: var(--ep-space-5);
}

.ep-article__lead {
  margin-bottom: var(--ep-space-6);
  color: var(--ep-text);
  font-size: var(--ep-text-lg);
  font-weight: 500;
}

.ep-article__figure {
  margin: 0 0 var(--ep-space-6);
}

.ep-article__figure img {
  width: 100%;
  height: auto;
  border-radius: var(--ep-radius-lg);
}

.ep-article__body > * + * {
  margin-top: var(--ep-space-4);
}

/* ---- Notice -------------------------------------------------------------- */

.ep-notice {
  padding: var(--ep-space-4) var(--ep-space-5);
  border-left: 4px solid var(--ep-warning);
  border-radius: var(--ep-radius-sm);
  background: #FFF8E6;
  color: var(--ep-warning-text);
}

.ep-prose > .ep-notice {
  margin-bottom: var(--ep-space-7);
}

/* Unresolved placeholder in draft copy. Deliberately loud: these must not
   reach production unnoticed. */
.ep-notice code,
.ep-todo {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: var(--ep-radius-sm);
  background: #FFF3CD;
  color: var(--ep-warning-text);          /* 5.93:1 on #FFF3CD */
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
  font-weight: 700;
}

/* ---- Split layout -------------------------------------------------------- */

.ep-split {
  display: grid;
  gap: var(--ep-space-6);
  align-items: start;
}

@media (min-width: 992px) {
  .ep-split {
    grid-template-columns: 1fr 1fr;
    gap: var(--ep-space-8);
  }

  .ep-split--media-first > :first-child {
    order: 2;
  }
}

/* ============================================================================
   Site chrome — header, footer, contact modal
   ========================================================================= */

/* ---- Header --------------------------------------------------------------
   Flat surface, not a gradient. The old horizontal gradient made link
   contrast depend on horizontal position: measured 6.63:1 at "Start" but
   3.43:1 at the last item, so the right-hand links failed WCAG AA on every
   page. A flat --ep-bg-inverse gives a uniform 11.12:1. */

.ep-header {
  min-height: var(--ep-nav-h);
  padding-block: var(--ep-space-2);
  background: #11333c;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.ep-header .navbar-brand {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--ep-space-3);
  padding-block: 0;
}

.ep-header .navbar-brand img {
  width: 44px;
  height: auto;
}

.ep-header .navbar-brand span {
  color: var(--ep-brand-logo);
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-xs);
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
}

.ep-header .navbar-nav {
  gap: var(--ep-space-1);
}

.ep-header .nav-link {
  padding: var(--ep-space-2) var(--ep-space-4);
  border-radius: var(--ep-radius-md);
  color: rgba(255, 255, 255, 0.88);
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-sm);
  font-weight: 500;
  transition: background var(--ep-duration-fast) var(--ep-ease),
              color var(--ep-duration-fast) var(--ep-ease);
}

.ep-header .nav-link:hover {
  background: rgba(255, 255, 255, 0.10);
  color: #fff;
}

.ep-header .nav-link.active {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  font-weight: 700;
}

.ep-header .navbar-toggler {
  border-color: rgba(255, 255, 255, 0.3);
}

/* ---- Language toggle ----------------------------------------------------- */

.ep-header .lang-toggle {
  border-color: rgba(255, 255, 255, 0.35);
}

.ep-header .ep-btn {
  background: var(--ep-bg-inverse);
}

.ep-header .ep-btn:hover {
  background: var(--ep-primary);
}

/* ---- Footer -------------------------------------------------------------- */

.ep-footer {
  padding-block: var(--ep-space-7) var(--ep-space-6);
  background: #11333c;
  color: #7E9BA3; /* darkened from --ep-text-inverse-muted, ~4.55:1 on #11333c (WCAG AA) */
}

.ep-footer__grid {
  display: grid;
  gap: var(--ep-space-6);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}

.ep-footer h2 {
  margin-bottom: var(--ep-space-3);
  color: #7E9BA3;
  font-family: var(--ep-font-body);
  font-size: var(--ep-text-sm);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.ep-footer p {
  margin: 0 0 var(--ep-space-2);
}

.ep-footer a {
  color: #7E9BA3;
  text-decoration: none;
}

.ep-footer a:hover {
  color: #fff;
  text-decoration: underline;
}

.ep-footer ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* WCAG 2.2 target size: the old footer links were 19px tall. */
.ep-footer li a {
  display: inline-block;
  padding-block: var(--ep-space-2);
  min-height: 24px;
}

.ep-footer__bottom {
  margin-top: var(--ep-space-7);
  padding-top: var(--ep-space-4);
  /* Reserves room for the fixed back-to-top circle (3rem wide, sitting
     var(--ep-space-5) from the edge) so right-aligned text never runs
     underneath it. */
  padding-right: 5rem;
  color: rgba(255, 255, 255, 0.55);
  font-size: var(--ep-text-sm);
  text-align: right;
}

/* First column of the footer row: the wordmark, sized to its own natural
   width rather than stretched to fill the grid track, and centred within
   the column's height — the Kontakt/Rechtliches columns are taller, so the
   grid row stretches this cell well past the logo's own height. */
.ep-footer__brand {
  display: flex;
  align-items: center;
  height: 100%;
}

.ep-footer__brand img {
  width: 100%;
  max-width: 18rem;
  height: auto;
}

/* Shorter bottom padding than the generic .ep-section rhythm: the BirdNET
   highlight is the last thing in this section, and the standard gap left
   too much dead space before the footer starts right after it. */
section:has(> .container > .ep-project-partner) {
  padding-bottom: var(--ep-space-7);
}

/* ---- Project partner highlight (home page, below the logo ticker) --------
   Same content that used to sit in the footer, moved onto the light
   --ep-section--card surface, so it needs dark-on-light tokens (the footer
   version above stays inverse for the footer's own dark background) and a
   ring around the logo's white disc so it doesn't disappear into the card
   background it now sits on. */
.ep-project-partner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--ep-space-4);
  margin-top: var(--ep-space-7);
  padding-top: var(--ep-space-6);
  border-top: 1px solid var(--ep-border);
}

/* The ring is drawn smaller than the logo itself (not clipped to it), so the
   branch the bird sits on pokes out past the circle on both sides instead of
   being cropped by it — matching BirdNET's own circular mark. */
.ep-project-partner__logo {
  position: relative;
  flex: none;
  display: block;
  width: 96px;
  height: 96px;
}

.ep-project-partner__logo img {
  display: block;
  width: 100%;
  height: 100%;
}

.ep-project-partner__logo::after {
  content: "";
  position: absolute;
  inset: 12px;
  border-radius: 50%;
  border: 1px solid #A9B0B6;
  pointer-events: none;
}

.ep-project-partner__text {
  margin: 0;
  color: var(--ep-text-secondary);
  font-size: var(--ep-text-base);
  font-weight: 600;
}

.ep-project-partner__more {
  display: inline;
  margin-left: var(--ep-space-2);
  border: none;
  background: none;
  padding: 0;
  color: var(--ep-text-secondary);
  font: inherit;
  font-size: var(--ep-text-sm);
  font-weight: 400;
  text-decoration: underline;
  cursor: pointer;
}

.ep-project-partner__more:hover {
  color: var(--ep-text);
}

/* ---- Contact modal ------------------------------------------------------- */

.ep-modal .modal-content {
  border: none;
  border-radius: var(--ep-radius-lg);
  overflow: hidden;
}

.ep-modal .modal-header {
  border-bottom: 1px solid var(--ep-border);
  background: var(--ep-bg-card);
}

.ep-modal .modal-title {
  margin: 0;
  font-size: var(--ep-h3);
}

.ep-modal .modal-body {
  background: var(--ep-bg-card);
  color: var(--ep-text-secondary);
}

.ep-modal .modal-footer {
  border-top: 1px solid var(--ep-border);
  background: var(--ep-bg-card);
}

/* Contact modal: redesigned layout */
.ep-contact-modal .modal-dialog {
  margin: auto;
}

.ep-contact-modal .modal-content {
  box-shadow: var(--ep-shadow-lg);
}

.ep-contact-modal .modal-header {
  padding: var(--ep-space-5) var(--ep-space-5) var(--ep-space-3);
  border-bottom: none;
}

.ep-contact-modal .modal-title {
  font-family: var(--ep-font-body);
  font-weight: 600;
}

.ep-contact-modal .modal-body {
  padding: var(--ep-space-3) var(--ep-space-5) var(--ep-space-5);
}

.ep-contact-intro {
  margin-bottom: var(--ep-space-5);
  color: var(--ep-text-secondary);
  font-size: var(--ep-text-lg);
  line-height: var(--ep-leading-relaxed);
}

.ep-contact-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--ep-space-3);
  margin-bottom: var(--ep-space-5);
}

@media (max-width: 575.98px) {
  .ep-contact-grid {
    grid-template-columns: 1fr;
  }
}

/* Static info tiles (email, phone) — not links, so no hover affordance that
   would suggest they respond to a click. */
.ep-contact-card {
  display: flex;
  flex-direction: column;
  gap: var(--ep-space-1);
  padding: var(--ep-space-4);
  border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-lg);
  background: var(--ep-bg-page);
}

.ep-contact-card__icon {
  display: grid;
  width: 2.5rem;
  height: 2.5rem;
  place-items: center;
  border-radius: var(--ep-radius-md);
  background: var(--ep-primary-faint);
  color: var(--ep-primary);
  margin-bottom: var(--ep-space-2);
}

.ep-contact-card__label {
  font-size: var(--ep-text-sm);
  font-weight: 600;
  color: var(--ep-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.ep-contact-card__value {
  font-size: var(--ep-text-lg);
  font-weight: 600;
  color: var(--ep-text);
  word-break: break-word;
}

.ep-contact-address {
  margin-bottom: var(--ep-space-4);
  padding: var(--ep-space-4);
  border-radius: var(--ep-radius-md);
  background: var(--ep-bg-page);
}

.ep-contact-address p {
  margin: 0;
  line-height: var(--ep-leading-snug);
}

.ep-contact-address__name {
  font-weight: 700;
  color: var(--ep-text);
}

.ep-contact-address__street,
.ep-contact-address__city {
  color: var(--ep-text-secondary);
}

.ep-contact-map {
  margin: 0;
}

.ep-contact-map img {
  width: 100%;
  height: auto;
  border-radius: var(--ep-radius-md);
}

.ep-contact-map figcaption {
  margin-top: var(--ep-space-2);
  font-size: var(--ep-text-sm);
  color: var(--ep-text-muted);
  text-align: center;
}

/* Legacy contact lines, kept for backward compatibility */
.ep-contact-line {
  display: flex;
  align-items: center;
  gap: var(--ep-space-3);
  margin-bottom: var(--ep-space-3);
  padding: var(--ep-space-3) var(--ep-space-4);
  border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-md);
  background: var(--ep-bg-page);
  color: var(--ep-text);
}

.ep-contact-line svg {
  flex: none;
  color: var(--ep-primary);
}

/* ---- Hero ----------------------------------------------------------------
   The old start page opened with a 715px photo and no words: the <h1> sat
   below the fold and, being inside a data-aos wrapper, rendered at
   opacity:0 until scroll. The headline now sits on the image and is visible
   on first paint without JavaScript. */

.ep-hero {
  position: relative;
  display: flex;
  align-items: center;
  background: var(--ep-bg-inverse);
  color: #fff;
  overflow: hidden;
}

.ep-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.ep-hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 35%;
}

/* A spacer keeps the hero's height tied to a fixed aspect ratio so
   the photograph fills the full frame on every screen size — no dark gap
   below the image when the text is shorter than the photo. */
.ep-hero__spacer {
  position: relative;
  z-index: 0;
  width: 100%;
  aspect-ratio: 7 / 5.8;
  pointer-events: none;
}

/* Scrim: the only gradient in the system, and only ever over a photograph so
   overlaid text keeps a guaranteed contrast floor.

   One gradient, horizontal only. The previous treatment stacked a second
   bottom-up gradient over the full frame and lifted the image with a
   brightness filter, and the combination read as an overall murk. The
   recorder and solar panel own the left of the photograph and the copy sits
   right, so the scrim is transparent for the left 40% — the product is
   genuinely untouched, not just less dark — and ramps to full strength only
   once it is under the text column. */
.ep-hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(
      to right,
      rgba(6, 26, 32, 0) 0%,
      rgba(6, 26, 32, 0) 42%,
      rgba(6, 26, 32, 0.68) 58%,
      rgba(6, 26, 32, 0.86) 78%,
      rgba(6, 26, 32, 0.88) 100%
    );
}

.ep-hero__inner {
  position: relative;
  z-index: 1;
  max-width: 34rem;
  margin-left: auto;   /* copy sits right; the device is on the left of the photo */
}

/* The hero container spans the full width so the text block can sit at the
   right edge of the viewport, not constrained by Bootstrap's .container.
   Extra right padding shifts the text block ~200px to the left of the edge. */
.ep-hero .container {
  max-width: none;
  padding-inline: var(--ep-space-7);
  padding-right: calc(var(--ep-space-7) + 200px);
}

@media (max-width: 1279.98px) {
  /* Below 1280px the recorder and the copy no longer both fit in one
     legible overlaid frame: the taller crop needed to keep the text
     column readable pushes the recorder out of shot, and pulling the
     visible window over to the recorder instead buries the text under it.
     So the two stop sharing a frame — the photo becomes its own
     full-width band, uncropped enough to show the whole device, and the
     copy moves into a solid block underneath in the hero's own background
     colour, reading as one continuous surface rather than a seam.

     This threshold is much higher than a typical "mobile" breakpoint
     because the overlap isn't a phone-only problem: the fixed 200px shift
     that pulls the desktop text column away from the right edge (see
     `.ep-hero .container` below) doesn't scale with viewport width, so the
     text column keeps encroaching on the device photo's position all the
     way through tablet portrait *and* landscape widths (checked at
     768–1150px, still overlapping as high as 1080px) before there's
     reliably enough room again. */
  .ep-hero {
    display: block;
  }

  .ep-hero__spacer {
    display: none;
  }

  .ep-hero__media {
    position: static;
    aspect-ratio: 3 / 2;
  }

  .ep-hero__media img {
    position: static;
    /* Centring (the desktop default) crops the recorder out of frame
       entirely at this band's width — shift the visible window left,
       toward the tree, to keep it in shot. */
    object-position: 0% 35%;
  }

  /* No text sits on the photo any more, so the contrast scrim is gone too. */
  .ep-hero__media::after {
    content: none;
  }

  .ep-hero__inner {
    margin-left: 0;
  }

  /* The 3:2 band above is tuned for phone widths (a 375px-wide band is a
     manageable ~250px tall); left alone it would keep growing linearly with
     width and turn into an ~850px slab of tree bark at tablet/laptop
     widths, pushing the headline below the fold. Past 768px the band gets
     a flat height instead, so it stops competing with the copy for
     vertical space. */
  @media (min-width: 768px) {
    .ep-hero__media {
      aspect-ratio: auto;
      height: 24rem;
    }
  }

  /* The extra 200px on .ep-hero .container that shifts the desktop text
     block away from the right edge has no room to work with on narrow
     viewports — left uncorrected it eats most of the width and forces the
     copy into a near-vertical column of single words. */
  .ep-hero .container {
    padding-inline: var(--ep-space-5);
    padding-block: var(--ep-space-6);
  }
}

.ep-hero .ep-eyebrow {
  color: var(--ep-primary-light);
}

.ep-hero h1 {
  margin-bottom: var(--ep-space-4);
  color: #fff;
  /* Wide enough for the English headline, which is longer than the German. */
  max-width: 22ch;
}

.ep-hero__lead {
  margin-bottom: var(--ep-space-6);
  max-width: 44ch;
  color: rgba(255, 255, 255, 0.92);
  font-size: var(--ep-text-lg);
}

.ep-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ep-space-3);
}

.ep-hero__actions .ep-btn--secondary {
  border-color: rgba(255, 255, 255, 0.7);
  color: #fff;
}

.ep-hero__actions .ep-btn--secondary:hover {
  background: #fff;
  color: var(--ep-bg-inverse);
}

.ep-hero__actions .ep-btn--ghost {
  color: rgba(255, 255, 255, 0.92);
  text-decoration: underline;
  text-decoration-color: rgba(255, 255, 255, 0.5);
  text-underline-offset: 3px;
}

.ep-hero__actions .ep-btn--ghost:hover {
  background: transparent;
  color: #fff;
  text-decoration-color: #fff;
}

/* ---- Back to top ----------------------------------------------------------
   On every page now, which is why it lives here rather than in the editorial
   page stylesheet it started in.

   Hidden until the reader is a screen or so down. A "back to top" offered while
   you are already at the top is furniture, and on a short page like the legal
   notice it would be the only thing floating over the text. visibility (not
   just opacity) so it leaves the tab order while hidden. */

.back-to-top {
  position: fixed;
  right: var(--ep-space-5);
  bottom: var(--ep-space-5);
  z-index: 900;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--ep-bg-inverse);
  color: #fff;
  font-family: var(--ep-font-body);
  font-size: 1.25rem;
  text-decoration: none;
  box-shadow: var(--ep-shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.5rem);
  transition: opacity var(--ep-duration-base) var(--ep-ease),
              transform var(--ep-duration-base) var(--ep-ease),
              visibility var(--ep-duration-base);
}

.back-to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.back-to-top:hover {
  background: var(--ep-primary);
  color: #fff;
}

/* Without JavaScript the button never appears. That is the one thing on the
   page that is pure convenience — the keyboard's Home key and the scrollbar
   both still work — so it is not worth a no-js class on <html> to recover. */

@media (prefers-reduced-motion: reduce) {
  .back-to-top {
    transition: opacity var(--ep-duration-base) var(--ep-ease),
                visibility var(--ep-duration-base);
    transform: none;
  }
}

@media (max-width: 575.98px) {
  .back-to-top {
    right: var(--ep-space-4);
    bottom: var(--ep-space-4);
    width: 2.75rem;
    height: 2.75rem;
  }
}
