/* Gewerbeverein Hage — all styling lives here.

   Design system "Hager Banden". The association's own logo drives it: yellow
   and blue horizontal bands, a black HAGE wordmark, the church silhouette.
   The old stylesheet greyed that identity out into a generic corporate blue,
   so the palette here goes back to the mark — blue carries text and dark
   surfaces, the logo yellow appears only as a shape or on dark ground, where
   it stays AA-legible. The stacked band (--band) is the recurring motif:
   header edge, section kickers, footer, cards.                              */

:root {
  --ink: #15202b;
  --ink-soft: #4f6070;
  --paper: #ffffff;
  --paper-alt: #f6f2ea;    /* warm sand — keeps the page off the cold grey default */
  --line: #e4dfd4;
  --line-cool: #d6e3ea;

  --brand: #005f87;        /* logo blue, darkened for AA text contrast (7.0:1 on white) */
  --brand-deep: #003a54;   /* dark surfaces: feature section, footer, hero card */
  --sky: #009fe1;          /* raw logo blue — shapes and dark-ground accents only */
  --yellow: #ffed01;       /* raw logo yellow — never text on light ground */
  --tint: #e4f1f8;
  --tint-soft: #f1f8fc;

  --band: linear-gradient(180deg,
      var(--yellow) 0 2px, var(--sky) 2px 4px, var(--yellow) 4px 6px);
  --band-2: linear-gradient(180deg, var(--yellow) 0 3px, var(--sky) 3px 6px);

  --radius-sm: 10px;
  --radius: 14px;
  --radius-lg: 22px;
  --wrap: 1180px;
  --pad: clamp(1.25rem, 4vw, 2.5rem);
  --gap: clamp(1rem, 2.5vw, 1.75rem);
  --section-y: clamp(3.5rem, 8vw, 6rem);
  --font: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --shadow: 0 1px 2px rgba(21, 32, 43, .05), 0 10px 28px -12px rgba(21, 32, 43, .18);
  --shadow-lift: 0 2px 4px rgba(21, 32, 43, .06), 0 18px 38px -16px rgba(21, 32, 43, .26);
}

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

/* app.js hides empty containers with [hidden]; author styles like
   `display: grid` would otherwise win over the UA sheet and leave the empty
   box (and its margin) in the flow. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  /* Every in-page link (nav, hero, CTAs) used to land with its heading tucked
     under the sticky header. 5.5rem = the header's 4.5rem row plus its 6px
     band plus a little air. */
  scroll-padding-top: 5.5rem;
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: clamp(1rem, .96rem + .2vw, 1.075rem);
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
  -webkit-text-size-adjust: 100%;
  /* A word longer than its column breaks instead of pushing the page sideways
     — the privacy policy's "https://policies.google.com/privacy" did exactly
     that at 320px, and member websites will be the next candidates. */
  overflow-wrap: break-word;
}

h1, h2, h3, h4 { line-height: 1.12; margin: 0 0 .5em; }
h1 { font-size: clamp(2.15rem, 1.35rem + 3.4vw, 3.6rem); font-weight: 800; letter-spacing: -.035em; }
h2 { font-size: clamp(1.7rem, 1.25rem + 2vw, 2.5rem); font-weight: 800; letter-spacing: -.03em; }
h3 { font-size: 1.125rem; font-weight: 700; letter-spacing: -.015em; }
h4 { font-size: 1.05rem; font-weight: 700; letter-spacing: -.015em; }
p { margin: 0 0 1em; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { max-width: 100%; }

a { color: var(--brand); }
a:hover { color: var(--brand-deep); }

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

.wrap { width: min(100% - 2 * var(--pad), var(--wrap)); margin-inline: auto; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: absolute; left: 50%; top: 0;
  transform: translate(-50%, -120%);
  background: var(--brand-deep); color: #fff;
  padding: .7rem 1.2rem; border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  z-index: 100; text-decoration: none;
}
.skip-link:focus-visible { transform: translate(-50%, 0); color: #fff; }

/* --- header ------------------------------------------------------------- */

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(255, 255, 255, .94);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  transition: box-shadow .25s ease;
}
/* Once the page scrolls, the header sits on top of content with nothing but a
   hairline to separate it. app.js sets this class past the first 24px. */
.site-header.is-stuck { box-shadow: 0 8px 24px -18px rgba(21, 32, 43, .55); }
/* the logo's band stack, six pixels of it, as the top edge of every page */
.site-header::before {
  content: ""; display: block; height: 6px; background: var(--band);
}

.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; min-height: 4.5rem;
}

.brand {
  display: inline-flex; align-items: center; gap: .7rem;
  min-width: 0; /* let it shrink below its text's natural width — without this,
                   a long brand name overflows the header and pushes the mobile
                   menu button off-screen instead of truncating. */
  text-decoration: none; color: var(--ink);
}
/* square, not a circle: the logo is a banded square and a circular crop cut
   its yellow/blue edges off */
.brand-mark {
  display: block;
  width: 2.5rem; height: 2.5rem;
  border-radius: 8px;
  object-fit: cover;
  flex: none;
  box-shadow: 0 0 0 1px rgba(21, 32, 43, .08);
}
.brand-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 700; letter-spacing: -.02em;
}

.nav-toggle {
  display: none; align-items: center; justify-content: center;
  flex-shrink: 0;
  width: 2.75rem; height: 2.75rem;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--ink); cursor: pointer;
}
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
  display: block; width: 20px; height: 2px; background: currentColor; border-radius: 2px;
}
.nav-toggle-bars { position: relative; transition: background-color .2s ease; }
.nav-toggle-bars::before, .nav-toggle-bars::after {
  content: ""; position: absolute; left: 0;
  transition: transform .25s ease, top .2s ease;
}
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after { top: 6px; }
/* open state: the three bars fold into a cross, so the button says what it
   does now instead of only in its screen-reader label */
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { top: 0; transform: rotate(-45deg); }

.site-nav { display: flex; align-items: center; gap: clamp(1rem, 2vw, 1.75rem); }
.nav-list { display: flex; gap: clamp(.9rem, 1.8vw, 1.6rem); }
.nav-list a {
  position: relative;
  display: inline-block; padding-block: .25rem;
  color: var(--ink-soft); text-decoration: none; font-weight: 550;
}
/* yellow underline on hover — the band motif at link scale */
.nav-list a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px; border-radius: 2px; background: var(--yellow);
  transform: scaleX(0); transform-origin: left;
  transition: transform .18s ease;
}
.nav-list a:hover { color: var(--brand); }
.nav-list a:hover::after, .nav-list a:focus-visible::after { transform: scaleX(1); }
/* The homepage is one long page; without this the nav never says where you
   are. app.js sets aria-current on the section in view, and on sub-pages the
   markup carries aria-current="page". */
.nav-list a[aria-current] { color: var(--brand); }
.nav-list a[aria-current]::after { transform: scaleX(1); }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  padding: .75rem 1.4rem; border-radius: 999px;
  font-weight: 650; text-decoration: none;
  border: 1px solid transparent;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease,
              box-shadow .15s ease, transform .15s ease;
}
.btn-primary {
  background: var(--brand); color: #fff;
  box-shadow: 0 2px 8px rgba(0, 58, 84, .25);
}
.btn-primary:hover {
  background: var(--brand-deep); color: #fff;
  transform: translateY(-1px); box-shadow: 0 6px 16px rgba(0, 58, 84, .3);
}
.btn-primary:active { transform: translateY(0); }
.btn-ghost { border-color: var(--line-cool); color: var(--ink); background: var(--paper); }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); }

.nav-cta .icon {
  width: 1.05rem; height: 1.05rem;
  margin: 0; padding: 0; background: none; color: inherit;
}

.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-bottom: 2.5rem; }

/* --- sections ----------------------------------------------------------- */

.section { padding-block: var(--section-y); }
.section-alt { background: var(--paper-alt); }

/* One left column, label over headline over lead, each with its own measure.
   The rule underneath runs the full width so the section still reads as a
   block — the text does not have to fill the width to look deliberate. */
.section-head {
  margin-bottom: clamp(2rem, 4vw, 3rem);
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--line);
}
.section-head > h1, .section-head > h2 { max-width: 24ch; }
/* A section that is only a head (e.g. "45. Hager Ortsfest", which has no
   programme yet) used to end in a rule with nothing under it. */
.wrap > .section-head:last-child {
  margin-bottom: 0; padding-bottom: 0; border-bottom: 0;
}
.section-head > .lead { max-width: 58ch; }
.section-head > :last-child { margin-bottom: 0; }

.kicker {
  display: flex; align-items: center; gap: .65rem;
  margin: 0 0 .75rem;
  font-size: .78rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--brand);
}
/* the band, shrunk to a label marker */
.kicker::before {
  content: ""; flex: none;
  width: 1.5rem; height: 6px; border-radius: 1px;
  background: var(--band-2);
}

.lead { max-width: 60ch; font-size: 1.1em; color: var(--ink-soft); }
/* loose body paragraphs (e.g. the partner text) otherwise run the full
   1180px and become unreadable at ~110 characters a line */
.section > .wrap > p, .section-cta > .wrap > p { max-width: 68ch; }
.prose { max-width: 68ch; }
/* Impressum, Datenschutz and the 404 page carry both classes on one element
   (`class="wrap prose"`). Capping that element centres the whole column, so
   its text started ~220px right of the brand, the footer and every other
   page's content. The measure belongs on the children instead. */
.wrap.prose { max-width: none; }
.wrap.prose > * { max-width: 68ch; }
/* Long-form legal pages (Impressum, Datenschutz) run on the same stylesheet.
   The display scale above would shout at them, so the prose column keeps its
   own, quieter heading sizes. */
.prose h1 { font-size: clamp(1.9rem, 1.5rem + 1.6vw, 2.6rem); }
.prose h2 {
  margin-top: 2.5rem; font-size: clamp(1.2rem, 1.05rem + .6vw, 1.45rem);
  letter-spacing: -.015em;
}
.prose h2:first-of-type { margin-top: 2rem; }
.note { color: var(--ink-soft); font-size: .95em; }
/* the privacy policy names the storage key it writes */
code {
  padding: .1em .35em; border-radius: 4px;
  background: var(--tint); color: var(--brand-deep);
  font-size: .9em;
}

/* Internal placeholder text ("TODO: ...") must never be mistaken for finished
   copy, no matter which component it lands in — heading, stat, link, badge.
   The !important is deliberate: this state always overrides local styling. */
.todo-flag {
  color: var(--ink-soft) !important;
  font-style: italic;
  font-weight: 400 !important;
  font-size: .85em;
  text-decoration: underline dashed;
  text-underline-offset: .2em;
}
/* the dashed-border note card already signals "placeholder" on its own —
   skip the extra underline/italic so the two treatments don't stack */
.placeholder-note.todo-flag {
  font-style: normal; font-size: 1em; text-decoration: none;
}
/* on dark ground the muted grey would fall below AA */
.hero-card .todo-flag,
.section-feature .todo-flag { color: rgba(255, 255, 255, .8) !important; }

/* --- hero --------------------------------------------------------------- */

.hero {
  position: relative;
  padding-block: clamp(3rem, 7vw, 5rem) clamp(3.5rem, 8vw, 5.5rem);
  background:
    radial-gradient(48rem 26rem at 88% -20%, rgba(0, 159, 225, .16), transparent 70%),
    linear-gradient(180deg, var(--tint-soft), var(--paper) 72%);
  border-bottom: 1px solid var(--line);
}
.hero h1 { max-width: 15ch; }
.hero .lead { margin-bottom: 2rem; }
.hero .btn-row { margin-bottom: 0; }

/* homepage: copy left, the association's "Ortsschild" right */
.hero-grid { display: grid; gap: clamp(2.25rem, 5vw, 4rem); align-items: center; }
@media (min-width: 62em) {
  .hero-grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); }
}

.hero-card {
  position: relative; overflow: hidden;
  padding: clamp(1.6rem, 3.5vw, 2.4rem);
  border-radius: var(--radius-lg);
  background: linear-gradient(155deg, var(--brand-deep), #00587e 75%, #006f96);
  color: #fff;
  box-shadow: 0 24px 50px -24px rgba(0, 58, 84, .65);
}
.hero-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 6px; background: var(--band);
}
.hero-card-mark {
  display: block; width: clamp(5rem, 12vw, 6.5rem); height: auto;
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 20px -10px rgba(0, 0, 0, .6);
}
.hero-card-text {
  margin: 1.25rem 0 0; max-width: 32ch;
  color: rgba(255, 255, 255, .82); font-size: .95rem;
}
.hero-card-text strong {
  display: block;
  margin-bottom: .15em;
  color: #fff; font-size: 1.2rem; font-weight: 700; letter-spacing: -.025em;
}

/* default (sub-page) hero: single column, with the band motif filling the
   otherwise empty right half on wide screens */
@media (min-width: 62em) {
  .hero-plain { overflow: hidden; }
  /* .hero-inner also carries .wrap on these pages — capping its width here
     would centre the column instead of narrowing it */
  .hero-plain .hero-inner { position: relative; z-index: 1; }
  .hero-plain::after {
    content: ""; position: absolute; pointer-events: none;
    right: -3rem; top: 50%; translate: 0 -50%;
    width: min(26rem, 30vw); aspect-ratio: 1 / 1;
    /* Wider bands and more air between them: at 12px the stripes were so fine
       that the block read as noise instead of as the logo's band stack. */
    background: repeating-linear-gradient(180deg,
      var(--yellow) 0 20px, transparent 20px 46px, var(--sky) 46px 66px, transparent 66px 92px);
    opacity: .3;
    -webkit-mask-image: radial-gradient(circle at 50% 50%, #000, transparent 68%);
            mask-image: radial-gradient(circle at 50% 50%, #000, transparent 68%);
  }
}

.hero-stats {
  display: flex; flex-wrap: wrap; gap: clamp(1.5rem, 5vw, 3.5rem);
  margin: 2rem 0 0; padding-top: 1.75rem; border-top: 1px solid var(--line);
}
.stat { display: flex; flex-direction: column-reverse; }
.stat-value { margin: 0; font-size: 1.7rem; font-weight: 800; letter-spacing: -.02em; color: var(--brand); }
.stat-label { font-size: .85rem; color: var(--ink-soft); }

/* inside the dark card the same list becomes a stacked fact rail */
.hero-card .hero-stats {
  display: grid; gap: 0;
  margin-top: 1.75rem; padding-top: 0;
  border-top: 1px solid rgba(255, 255, 255, .22);
}
.hero-card .stat {
  padding-block: .9rem;
  border-bottom: 1px solid rgba(255, 255, 255, .14);
}
.hero-card .stat:last-child { border-bottom: 0; padding-bottom: 0; }
.hero-card .stat-value { color: var(--yellow); font-size: 1.55rem; }
.hero-card .stat-label { color: rgba(255, 255, 255, .8); }

/* --- timeline (Über uns: history as short scannable steps, not paragraphs) - */

.timeline {
  position: relative;
  margin: 0 0 clamp(3rem, 7vw, 4.75rem);
  padding-left: 1.85rem;
  display: grid; gap: 2rem;
  max-width: 52rem;
}
.timeline::before {
  content: ""; position: absolute;
  left: .3rem; top: .4rem; bottom: .4rem; width: 2px;
  background: linear-gradient(180deg, var(--sky), var(--line-cool));
}
.timeline-item { position: relative; }
.timeline-marker {
  position: absolute; left: -1.85rem; top: .35rem;
  width: .7rem; height: .7rem; border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 4px var(--paper-alt);
}
.section:not(.section-alt) .timeline-marker { box-shadow: 0 0 0 4px var(--paper); }
.timeline-item h3 {
  margin-bottom: .3em;
  font-size: 1.15rem; color: var(--brand-deep);
}
.timeline-item p { margin: 0; max-width: 34ch; color: var(--ink-soft); }

/* Three short phases side by side under one rail — as a stacked list they
   read as another paragraph block, which is what made this section a wall. */
@media (min-width: 52em) {
  .timeline {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(1.75rem, 4vw, 3.5rem);
    max-width: none;
    padding: 2.75rem 0 0;
  }
  .timeline::before {
    left: 0; right: 0; top: .4rem; bottom: auto;
    width: auto; height: 2px;
    background: linear-gradient(90deg, var(--sky), var(--line-cool));
  }
  .timeline-marker { left: 0; top: -2.35rem; }
}

/* --- placeholder note (internal TODOs that must not read as real copy) --- */

/* sits at the end of its section now, so the spacing moved to the top */
.placeholder-note {
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0; max-width: 52rem;
  padding: .85rem 1.15rem;
  border: 1px dashed var(--line); border-radius: var(--radius);
  background: rgba(255, 255, 255, .6);
  color: var(--ink-soft); font-size: .9rem;
}

/* --- photo grid (real photos once the association provides them; empty
   frames until then, so the section reads as "filling up", not broken) --- */

/* Fixed column counts instead of auto-fit: the homepage shows 6 photos and the
   Ortsfest page 12 for 2025, and both divide evenly into 2 and 3 columns —
   auto-fit went to 4 columns on desktop and left a half-empty last row. Two
   columns even on a phone, so a 12-photo gallery is not a dozen full screens.
   .photo-grid-lead is for a set of three (Ortsfest 2026): one row from 52em,
   below that the first photo spans both columns instead of leaving a gap. */
.photo-grid {
  display: grid; gap: 1.5rem 1rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.photo-grid-lead > .photo-tile:first-child { grid-column: 1 / -1; }
@media (min-width: 52em) {
  .photo-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .photo-grid-lead > .photo-tile:first-child { grid-column: auto; }
}
/* Placeholder frames carry no information, so they stay small: stacked
   full-width 4:3 boxes put ~900px of empty space between the hero and the
   first real section on a phone. app.js sets this class while items is empty. */
.photo-grid-empty {
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  max-width: 44rem;   /* three modest frames, not three full-width ones */
}
.photo-grid-empty .photo-tile { aspect-ratio: 3 / 2; }
/* The photos are 3:2 camera frames; showing them at 3:2 keeps the
   photographer's watermark in the bottom corner inside the crop. */
.photo-tile figure { margin: 0; }
.photo-tile img {
  display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: var(--radius); background: var(--tint-soft);
}
.photo-tile figcaption { margin-top: .5rem; color: var(--ink-soft); font-size: .9rem; line-height: 1.4; }
.photo-heading { margin: 2.5rem 0 1rem; }
.photo-credit { margin: 1.25rem 0 0; color: var(--ink-soft); font-size: .875rem; }
.photo-more { margin-top: 1.75rem; }
.photo-tile-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .6rem; border: 1px dashed var(--line-cool); border-radius: var(--radius);
  background:
    repeating-linear-gradient(135deg,
      rgba(0, 95, 135, .035) 0 8px, transparent 8px 18px),
    var(--tint-soft);
  color: var(--ink-soft); font-size: .9rem;
}
.photo-tile-empty .icon {
  width: 1.75rem; height: 1.75rem; margin: 0;
  padding: 0; background: none; color: var(--brand);
}

/* --- values ------------------------------------------------------------- */

.value-list {
  display: grid; gap: clamp(1.75rem, 4vw, 3rem);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
/* the band motif again, this time as a short rule over each pillar */
.value-item::before {
  content: ""; display: block;
  width: 2.75rem; height: 6px; border-radius: 1px;
  margin-bottom: 1.1rem;
  background: var(--band-2);
}
.value-item h3, .value-item h4, .value-item h5 {
  margin-bottom: .35em;
  font-size: clamp(1.15rem, 1rem + .55vw, 1.45rem);
  color: var(--brand-deep);
}
.value-item p { margin: 0; max-width: 32ch; color: var(--ink-soft); }

/* --- cards -------------------------------------------------------------- */

.card-grid {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.card {
  padding: 1.6rem; border-radius: var(--radius);
  background: var(--paper); border: 1px solid var(--line);
  box-shadow: var(--shadow);
  transition: transform .18s ease, box-shadow .18s ease;
}
.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.card p { margin: 0; color: var(--ink-soft); }
.card h3 { margin-bottom: .35em; }

.icon { width: 1.75rem; height: 1.75rem; color: var(--brand); margin-bottom: .75rem; }
/* icon as a tinted badge — gives the card grid a consistent anchor point */
.card .icon {
  width: 2.6rem; height: 2.6rem; padding: .6rem;
  border-radius: var(--radius-sm);
  background: var(--tint); color: var(--brand);
  margin-bottom: 1rem;
}

/* --- members ------------------------------------------------------------ */

.member-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
}
.member {
  padding: 1.15rem 1.3rem; border-radius: var(--radius);
  background: var(--paper); border: 1px solid var(--line);
}
/* only the ones that actually lead somewhere react to the pointer */
.member:has(a) {
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.member:has(a):hover {
  transform: translateY(-2px);
  border-color: var(--line-cool); box-shadow: var(--shadow);
}
.member-name { margin: 0 0 .2em; font-size: 1rem; }
.member-name a { text-decoration: none; }
.member-name a:hover { text-decoration: underline; }
.member-meta { margin: 0; font-size: .9rem; color: var(--ink-soft); }

.member-empty {
  grid-column: 1 / -1;
  padding: 2rem 1.75rem; text-align: center;
  border: 1px dashed var(--line-cool); border-radius: var(--radius);
  background: var(--tint-soft); color: var(--ink-soft);
}

/* --- events ------------------------------------------------------------- */

.event-list { display: grid; gap: 1rem; }
.event {
  display: grid; gap: .35rem 1.75rem; align-items: baseline;
  padding: 1.35rem 1.6rem; border-radius: var(--radius);
  background: var(--paper); border: 1px solid var(--line);
  border-left: 4px solid var(--sky);
}
.event-date {
  font-weight: 700; letter-spacing: -.01em; color: var(--brand);
}
.event-body h3 { margin-bottom: .2em; }
.event-body p { margin: 0; color: var(--ink-soft); }
.event-place { font-size: .9rem; }
.event-empty { color: var(--ink-soft); border-left-color: var(--line); }

@media (min-width: 40em) {
  .event { grid-template-columns: 11rem 1fr; }
}

/* --- steps -------------------------------------------------------------- */

.step-list {
  position: relative; max-width: 38rem;
  display: grid; gap: 2rem; margin: 0 0 2rem;
}
.step-list::before {
  content: ""; position: absolute;
  left: 1.125rem; top: 1.125rem; bottom: 1.125rem; width: 2px;
  background: var(--line-cool);
}
.step { position: relative; display: flex; gap: 1.25rem; align-items: flex-start; }
.step-num {
  position: relative; flex: none; display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem; border-radius: 50%;
  background: var(--brand); color: #fff; font-weight: 700;
}
.step h3 { margin-bottom: .2em; }
.step p { margin: 0; color: var(--ink-soft); }

/* --- conversion section (Mitglied werden) ------------------------------- */

/* the one section that asks for something gets a panel instead of another
   flat band, so it doesn't read like the eight informational ones */
.section-cta {
  background:
    radial-gradient(44rem 26rem at 88% 0%, rgba(0, 159, 225, .13), transparent 70%),
    var(--tint-soft);
}
.section-cta .section-head { border-bottom-color: var(--line-cool); }

/* --- contact -------------------------------------------------------------
   Map as a wide banner above the info card — both full width, same as
   every other full-bleed section on the page. */
.contact-layout {
  display: flex; flex-direction: column; gap: 1.75rem;
}
#kontakt-card.fact-panel { max-width: none; margin-bottom: 0; }

@media (min-width: 52em) {
  .contact-layout {
    display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    gap: var(--gap); align-items: stretch;
  }
}

.contact-person { font-weight: 650; }
.contact-role { display: block; font-weight: 400; font-size: .9rem; color: var(--ink-soft); }

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

.site-footer {
  position: relative;
  padding-block: 2.75rem;
  background: var(--brand-deep); color: rgba(255, 255, 255, .8);
  font-size: .95rem;
}
.site-footer::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 6px; background: var(--band);
}
.footer-inner { display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: center; justify-content: space-between; }
.site-footer p { margin: 0; }
.site-footer a { color: #fff; }
.site-footer a:hover { color: var(--yellow); }
.site-footer :focus-visible { outline-color: var(--yellow); }
/* wraps: with the consent entry there are three items, and on a 320px screen
   they no longer fit on one line */
.footer-links { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }

/* --- responsive nav ----------------------------------------------------- */

@media (max-width: 52em) {
  .nav-toggle { display: inline-flex; }
  /* Not display:none/flex — the panel fades and slides, and `visibility`
     keeps the closed menu out of the tab order while it animates. */
  .site-nav {
    position: absolute; inset: 100% 0 auto;
    display: flex; flex-direction: column; align-items: stretch;
    gap: 1rem; padding: 1.25rem var(--pad) 1.75rem;
    background: var(--paper); border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow);
    visibility: hidden; opacity: 0; transform: translateY(-.5rem);
    /* visibility switches at 0s — on the way out it waits for the fade, on the
       way in it happens at once. Interpolating visibility itself leaves the
       panel in a half-state that some engines paint and others do not. */
    transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s;
  }
  .site-nav[data-open="true"] {
    visibility: visible; opacity: 1; transform: none;
    transition: opacity .2s ease, transform .2s ease, visibility 0s;
  }
  .nav-list { flex-direction: column; gap: .25rem; }
  .nav-list a { display: block; padding: .7rem 0; }
  /* the underline marker is a desktop affordance; in the stacked menu the
     current item gets a band on its left edge instead */
  .nav-list a::after { display: none; }
  .nav-list a[aria-current] {
    margin-left: -.85rem; padding-left: .6rem;
    border-left: 3px solid var(--yellow);
  }
  .header-inner { position: relative; }
}

/* --- reveal on scroll ----------------------------------------------------
   Every rule here is behind .js-reveal, which setupReveal() in app.js only
   adds when IntersectionObserver exists and the visitor has not asked for
   reduced motion. Without scripting nothing is ever hidden. The .reveal class
   is handed out by app.js too, so the list of animated components lives in
   one place (REVEAL_GROUPS) instead of being repeated here. */

/* `translate`, not `transform`: .card and .member use transform for their
   hover lift, and a transform here would win over it once revealed. */
.js-reveal .reveal {
  opacity: 0; translate: 0 14px;
  transition: opacity .55s ease, translate .55s ease;
}
.js-reveal .reveal.is-visible { opacity: 1; translate: none; }

/* The hero card is above the fold, so it is not tied to the scroll observer —
   app.js starts it once the card actually carries its content, otherwise the
   animation would play on an empty box while config.json is still loading. */
.hero-card.is-in { animation: rise-in .6s ease both; }
@keyframes rise-in {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* --- back to top ----------------------------------------------------------
   The homepage runs to eleven sections; on a phone that is a long way back to
   the menu. Created by app.js, so it never appears as a dead control. */

.to-top {
  position: fixed; z-index: 60;
  right: clamp(1rem, 3vw, 2rem);
  bottom: calc(clamp(1rem, 3vw, 2rem) + env(safe-area-inset-bottom, 0px));
  display: grid; place-items: center;
  width: 2.9rem; height: 2.9rem;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--brand-deep); color: #fff;
  box-shadow: 0 12px 26px -12px rgba(0, 58, 84, .7);
  visibility: hidden; opacity: 0; transform: translateY(12px);
  transition: opacity .25s ease, transform .25s ease, visibility .25s,
              background-color .15s ease;
}
.to-top.is-visible { visibility: visible; opacity: 1; transform: none; }
.to-top:hover { background: var(--brand); }
.to-top .icon {
  width: 1.3rem; height: 1.3rem;
  margin: 0; padding: 0; background: none; color: currentColor;
}

/* the rail draws itself in the direction it runs */
.js-reveal .timeline::before {
  transform: scaleY(0); transform-origin: top;
  transition: transform .9s ease;
}
.js-reveal .timeline.is-visible::before { transform: none; }
@media (min-width: 52em) {
  .js-reveal .timeline::before { transform: scaleX(0); transform-origin: left; }
}

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

/* --- feature section (Hager Ortsfest) -------------------------------------
   The association's flagship event carried the same white-on-grey treatment as
   every other section. It is now the page's one dark anchor, in the logo's
   blue with the logo's yellow on top. */

.section-feature {
  position: relative; overflow: hidden;
  background:
    radial-gradient(40rem 26rem at 88% 8%, rgba(0, 159, 225, .32), transparent 68%),
    var(--brand-deep);
  color: #fff;
}
.section-feature::after {
  content: ""; position: absolute; pointer-events: none;
  right: -6rem; bottom: -8rem;
  width: min(34rem, 42vw); aspect-ratio: 1 / 1;
  /* same correction as .hero-plain::after: fewer, wider bands, so the shape
     is legible as the logo's motif instead of looking like a screen artefact */
  background: repeating-linear-gradient(180deg,
    var(--yellow) 0 22px, transparent 22px 52px, var(--sky) 52px 74px, transparent 74px 104px);
  /* the mask has to reach zero before the section clips the shape, otherwise
     the stripes end in a hard vertical edge at the right margin */
  opacity: .16;
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000, transparent 52%);
          mask-image: radial-gradient(circle at 50% 50%, #000, transparent 52%);
}
.section-feature .wrap { position: relative; z-index: 1; }
.section-feature .kicker { color: var(--yellow); }
.section-feature .kicker::before { background: var(--yellow); }
.section-feature .lead { color: rgba(255, 255, 255, .85); }
.section-feature .section-head { border-bottom-color: rgba(255, 255, 255, .2); }
.section-feature .prose { margin-bottom: 1.75rem; }
.section-feature :focus-visible { outline-color: var(--yellow); }
/* on the dark ground the primary button becomes the logo yellow */
.section-feature .btn-primary {
  background: var(--yellow); color: var(--ink);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .3);
}
.section-feature .btn-primary:hover { background: #fff; color: var(--ink); }
.section-feature .btn-ghost {
  background: transparent; color: #fff; border-color: rgba(255, 255, 255, .45);
}
.section-feature .btn-ghost:hover { border-color: var(--yellow); color: var(--yellow); }

/* one consistent panel for date, highlights and the volunteer credit — same
   row shape throughout instead of mixing a pill, a loose list and a card */
.fact-panel {
  max-width: 36rem; margin: 0 0 2rem; padding: 0 1.6rem;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  font-style: normal; /* reset in case this lands on an <address> */
}
.fact-row {
  display: flex; align-items: flex-start; gap: 1rem;
  padding: 1.05rem 0; border-bottom: 1px solid var(--line);
}
.fact-row:last-child { border-bottom: none; }
.fact-row .icon {
  width: 1.35rem; height: 1.35rem; margin: .2rem 0 0;
  padding: 0; background: none; color: var(--brand); flex: none;
}
.fact-row span { font-weight: 600; }
.fact-row-muted span { font-weight: 400; color: var(--ink-soft); }

/* label + detail on two lines, instead of one bold line (e.g. "Festmeile" /
   "Samstag und Sonntag ab 10:00 Uhr") */
.fact-row-detail span { font-weight: 400; color: var(--ink-soft); }
.fact-row-detail strong { display: block; font-weight: 600; color: var(--ink); margin-bottom: .15em; }

/* the same panel on the dark feature ground, across the full section width */
.section-feature .fact-panel {
  max-width: 44rem;
  background: rgba(255, 255, 255, .07);
  border-color: rgba(255, 255, 255, .2);
  backdrop-filter: blur(4px);
}
.section-feature .fact-row { border-bottom-color: rgba(255, 255, 255, .14); }
.section-feature .fact-row .icon { color: var(--yellow); }
.section-feature .fact-row-muted span { color: rgba(255, 255, 255, .78); }
.section-feature .fact-row-detail span { color: rgba(255, 255, 255, .78); }
.section-feature .fact-row-detail strong { color: #fff; }

/* --- social links ------------------------------------------------------- */

.fact-row-social { gap: 1.5rem; flex-wrap: wrap; align-items: center; }
.social-line {
  display: inline-flex; align-items: center; gap: .5rem;
  color: var(--brand); text-decoration: none; font-weight: 600;
}
.social-line:hover { color: var(--brand-deep); }
.social-line .icon {
  width: 1.35rem; height: 1.35rem; margin: 0;
  padding: 0; background: none; color: var(--brand); flex: none;
}

/* --- program tables ----------------------------------------------------- */

/* Both days as a matched pair of cards instead of two tables loose in the
   page — same "paired panels" idea used for the contact card + map. */
.program-days {
  display: grid; gap: var(--gap); margin: 0 0 2.5rem;
}
@media (min-width: 46em) {
  .program-days { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.program-day {
  /* a grid item will not shrink below its content's minimum width on its own,
     so the 24rem-wide programme table pushed the whole page sideways on a
     phone (454px of content in a 375px viewport) instead of scrolling inside
     its own box */
  min-width: 0;
  padding: 1.5rem; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--paper);
  box-shadow: var(--shadow);
}
.program-day h4 { margin-bottom: 1rem; }

/* The stage programme is three columns wide and scrolls sideways on a phone.
   Two things were missing: a sign that it scrolls at all, and a way to do it
   without a pointer — the container is focusable now (see the tabindex in the
   markup), so it can be scrolled with the arrow keys.
   The fade pairs are painted with background-attachment: local (they travel
   with the content) against shadows that stay put, the standard
   scroll-shadow trick. */
.table-scroll {
  overflow-x: auto;
  background:
    linear-gradient(90deg, var(--paper) 40%, rgba(255, 255, 255, 0)) left center / 2.5rem 100% no-repeat local,
    linear-gradient(90deg, rgba(255, 255, 255, 0), var(--paper) 60%) right center / 2.5rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(21, 32, 43, .16), rgba(21, 32, 43, 0)) left center / .85rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(21, 32, 43, .16), rgba(21, 32, 43, 0)) right center / .85rem 100% no-repeat scroll;
}
.table-scroll:focus-visible { outline-offset: 4px; }

.program {
  width: 100%; border-collapse: collapse;
  min-width: 24rem; font-size: .9rem;
}
.program th, .program td {
  padding: .7rem .9rem; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.program th {
  background: var(--tint); color: var(--brand-deep);
  font-size: .8rem; letter-spacing: .04em; text-transform: uppercase;
}
.program tr:last-child td { border-bottom: none; }
.program-time { display: block; font-weight: 700; color: var(--brand); }

/* On a phone the three stages sat in a 24rem table inside a ~19rem card, so
   the programme could only be read by dragging it sideways. Tighter type,
   tighter padding and hyphenated stage names let the same three columns fit
   from ~360px up; the scroll container stays as the fallback below that. */
@media (max-width: 30em) {
  .program { min-width: 17rem; font-size: .8rem; }
  .program th, .program td { padding: .55rem .45rem; }
  .program th { font-size: .72rem; letter-spacing: .02em; hyphens: auto; }
}

.value-item h4 { margin: 0 0 .25em; font-size: 1.05rem; }

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

.faq { max-width: 70ch; }
.faq dt { font-weight: 700; margin-top: 1.5rem; letter-spacing: -.01em; }
.faq dt:first-child { margin-top: 0; }
.faq dd { margin: .35em 0 0; color: var(--ink-soft); }

/* --- forms -------------------------------------------------------------- */

.form { max-width: 42rem; }

.form fieldset {
  margin: 0 0 2rem; padding: 1.6rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow);
}
.form legend {
  padding: 0 .55rem; font-weight: 700; letter-spacing: -.01em;
  color: var(--brand-deep);
}

.field { margin: 0 0 1.25rem; }
.field:last-child { margin-bottom: 0; }
.field label { display: block; margin-bottom: .35rem; font-weight: 550; }

.field input[type="text"], .field input[type="email"], .field input[type="tel"],
.field input[type="url"], .field input[type="month"], .field textarea {
  width: 100%; padding: .7rem .85rem;
  font: inherit; color: inherit;
  background: var(--paper);
  border: 1px solid var(--line-cool); border-radius: var(--radius-sm);
}
.field input:focus-visible, .field textarea:focus-visible { border-color: var(--brand); }
.field textarea { resize: vertical; }

.field-hint { display: block; margin-top: .3rem; font-size: .85rem; color: var(--ink-soft); }

.field-split { display: grid; gap: 1rem; grid-template-columns: minmax(6rem, 1fr) 2fr; }

.field-check {
  display: grid; grid-template-columns: auto 1fr; gap: .7rem;
  align-items: start;
}
.field-check input { margin-top: .3rem; width: 1.05rem; height: 1.05rem; }
.field-check label { margin: 0; font-weight: 400; }

/* --- partner invitation ---------------------------------------------------
   The section used to be a loose paragraph with a button under it and, until
   partners are confirmed, nothing else. Invitation and button now sit in one
   panel, with what the support actually pays for beside them, so the button
   belongs to something. */

.partner-invite {
  display: grid; gap: clamp(1.75rem, 4vw, 3rem);
  padding: clamp(1.6rem, 4vw, 2.5rem);
  border: 1px solid var(--line-cool); border-radius: var(--radius-lg);
  background: var(--paper);
  box-shadow: var(--shadow);
}
@media (min-width: 52em) {
  .partner-invite {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
  }
  /* a hairline between the halves instead of a second border */
  .partner-invite-aside {
    padding-left: clamp(1.75rem, 4vw, 3rem);
    border-left: 1px solid var(--line);
  }
}
.partner-invite-main p { max-width: 36ch; margin-bottom: 1.5rem; }

.partner-list-label {
  margin: 0 0 1.1rem;
  font-size: .78rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft);
}
.partner-list { display: grid; gap: 1.1rem; }
.partner-list li { display: flex; align-items: flex-start; gap: .85rem; }
.partner-list .icon {
  width: 1.3rem; height: 1.3rem; margin: .15rem 0 0;
  padding: 0; background: none; color: var(--brand); flex: none;
}
.partner-list strong {
  display: block; margin-bottom: .1em;
  color: var(--brand-deep); font-weight: 700; letter-spacing: -.01em;
}
.partner-list span { color: var(--ink-soft); font-size: .95rem; }

/* --- logo strip (sponsors / partners) ----------------------------------- */

.logo-strip {
  display: flex; flex-wrap: wrap; gap: .6rem;
  margin-bottom: 2rem;
}
.logo-strip li {
  padding: .55rem 1.1rem; border-radius: 999px;
  background: var(--paper); border: 1px solid var(--line);
  font-weight: 500; font-size: .95rem;
}

/* --- map ------------------------------------------------------------------
   Matches the fact-panel's border/radius so the two boxes read as a matched
   pair. Holds either the Google Maps iframe or, until the visitor consents,
   the .map-blocked placeholder at the end of this file. */
.map-embed {
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; aspect-ratio: 4 / 3;
  background: var(--tint-soft);
}
.map-embed iframe { display: block; width: 100%; height: 100%; border: 0; }

@media (min-width: 30em) {
  .map-embed { aspect-ratio: 16 / 9; }
}
@media (min-width: 52em) {
  /* sits next to the contact card now: match its height instead of holding
     a ratio, so the two boxes line up top and bottom */
  .map-embed { aspect-ratio: auto; min-height: 22rem; }
}

/* --- consent -------------------------------------------------------------
   Built by app.js (see the consent section there). Bottom-anchored, above the
   back-to-top button, and not a full-screen wall: it blocks the Google Maps
   request, not the page. */

.consent {
  position: fixed; z-index: 80;
  left: 0; right: 0; bottom: 0;
  padding: 1.25rem 0 calc(1.25rem + env(safe-area-inset-bottom, 0px));
  background: var(--paper);
  box-shadow: 0 -12px 40px -18px rgba(21, 32, 43, .45);
  opacity: 0; transform: translateY(100%);
  transition: opacity .25s ease, transform .3s ease;
}
.consent.is-in { opacity: 1; transform: none; }
/* the logo's band as the panel's top edge, like the header and the footer */
.consent::before {
  content: ""; position: absolute; inset: 0 0 auto 0;
  height: 6px; background: var(--band);
}

.consent-inner {
  display: grid; gap: 1.25rem 2.5rem;
  align-items: center;
}
@media (min-width: 58em) {
  .consent-inner { grid-template-columns: minmax(0, 1fr) auto; }
}

.consent-title {
  margin: 0 0 .4em;
  font-size: 1.05rem; font-weight: 700; letter-spacing: -.015em;
  color: var(--brand-deep);
}
.consent-text {
  margin: 0; max-width: 70ch;
  font-size: .92rem; color: var(--ink-soft);
}
.consent-state {
  margin: .6rem 0 0;
  font-size: .85rem; font-weight: 600; color: var(--brand);
}

/* Accept and reject sit side by side, same size, one click each — a reject
   that is harder to reach than accept is not a free choice. */
/* Stacked below 58em: side by side, the reject button ended up narrower than
   accept because it had to share its row with the policy link. Each gets the
   full width and its own row instead. */
.consent-actions {
  display: grid; gap: .75rem; justify-items: stretch;
}
.consent-actions .btn { justify-content: center; }
.consent-link { font-size: .9rem; justify-self: center; }

@media (min-width: 58em) {
  .consent-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1rem;
  }
  .consent-link { justify-self: auto; white-space: nowrap; }
}

/* the back-to-top button would sit in the banner's lap */
body[data-consent-open="true"] .to-top { visibility: hidden; opacity: 0; }

/* --- map placeholder while consent is missing --------------------------- */

.map-blocked {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .5rem; padding: 2rem 1.5rem; text-align: center;
  background: var(--tint-soft);
}
.map-blocked .icon {
  width: 2rem; height: 2rem; margin: 0 0 .25rem;
  padding: 0; background: none; color: var(--brand);
}
.map-blocked-title {
  margin: 0; font-weight: 700; letter-spacing: -.015em; color: var(--brand-deep);
}
.map-blocked-text {
  margin: 0; max-width: 42ch;
  font-size: .9rem; color: var(--ink-soft);
}
.map-blocked .btn { margin-top: .75rem; }
.map-blocked-link { font-size: .85rem; }

/* --- footer: the consent entry is a button, not a link ------------------ */

.footer-link-button {
  padding: 0; border: 0; background: none;
  font: inherit; color: #fff; text-decoration: underline;
  cursor: pointer;
}
.footer-link-button:hover { color: var(--yellow); }
