/* ==========================================================================
   rolf-froehling.de — 2026 stylesheet
   Replaces Foundation 6 + jQuery-era CSS. No framework, no build step:
   this file is served as-is, so it must stay readable and hand-editable.

   Design intent: the photographs and the prose are the product. Everything
   here gets out of their way — generous space, one strong reading column,
   a restrained warm palette that works in both light and dark.
   ========================================================================== */

/* --------------------------------------------------------------- tokens -- */
:root {
  /* Warm neutrals rather than pure grey — this is a travel journal. */
  --bg:            #fbfaf8;
  --bg-sunken:     #f2efe9;
  --bg-raised:     #ffffff;
  --ink:           #1c1a17;
  --ink-soft:      #55504a;
  --ink-faint:     #8a837a;
  --rule:          #e2ddd4;
  /* A rule a reader is meant to SEE, as against --rule, which separates things
     without being noticed. Rolf, 2026-08-21: "die flexible Trennlinie schon
     eingebaut. Sie ist aber kaum zu erkennen. Schwarz wäre besser als grau."
     --rule against the page is 1.30:1, which is why. This is 4.54:1 - three
     and a half times the contrast, and still clearly a grey rather than the
     black he asked for, which on a warm off-white page would read as a bar
     rather than a pause. */
  --rule-strong:   #7a7268;
  /* Picked off the purple canoe in the Alaska hero. */
  --accent:        #6b4e9b;
  --accent-ink:    #ffffff;
  --accent-soft:   #f0ebf7;
  --accent-strong: #573f80;

  /* Aliases resolve lazily, so these follow the dark-mode overrides above
     without needing to be restated in each palette. */
  --muted:         var(--ink-soft);
  /* The map's land mass. The one token that needs a real value per palette. */
  --map-land:      #e6e0d5;

  --measure:       68ch;
  --page:          min(100% - 2.5rem, 78rem);
  --gap:           clamp(1rem, 2.5vw, 2rem);

  --radius:        10px;
  --shadow:        0 1px 2px rgb(28 26 23 / .05), 0 8px 24px -12px rgb(28 26 23 / .18);

  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:        #14130f;
    --bg-sunken: #1c1a16;
    --bg-raised: #211e1a;
    --ink:       #ece7de;
    --ink-soft:  #b3aca1;
    --ink-faint: #857e73;
    --rule:      #322e28;
    /* lighter, not darker - the same visible weight on a dark page: 4.58:1 */
    --rule-strong: #857d71;
    --accent:    #b79ae4;
    --accent-ink:#17130f;
    --accent-soft:#241d33;
  --accent-strong: #cbb4f0;
  --map-land:  #2b2822;
    --shadow:    0 1px 2px rgb(0 0 0 / .4), 0 8px 24px -12px rgb(0 0 0 / .7);
  }
}
:root[data-theme="dark"] {
  --bg:        #14130f;
  --bg-sunken: #1c1a16;
  --bg-raised: #211e1a;
  --ink:       #ece7de;
  --ink-soft:  #b3aca1;
  --ink-faint: #857e73;
  --rule:      #322e28;
  --rule-strong: #857d71;
  --accent:    #b79ae4;
  --accent-ink:#17130f;
  --accent-soft:#241d33;
  --accent-strong: #cbb4f0;
  --map-land:  #2b2822;
  --shadow:    0 1px 2px rgb(0 0 0 / .4), 0 8px 24px -12px rgb(0 0 0 / .7);
}

/* --------------------------------------------------------------- reset --- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(1.0625rem, 0.98rem + 0.36vw, 1.1875rem);
  line-height: 1.65;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img, picture, video, canvas { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-underline-offset: .18em; text-decoration-thickness: from-font; }
a:hover { text-decoration-color: var(--accent); }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 3px; }

/* Skip link — first thing a keyboard user meets. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--accent); color: var(--accent-ink);
  padding: .75rem 1.25rem; border-radius: 0 0 var(--radius) 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* ------------------------------------------------------------ typography - */
h1, h2, h3, h4 { font-family: var(--serif); line-height: 1.15; text-wrap: balance; margin: 0 0 .5em; font-weight: 600; }
h1 { font-size: clamp(1.9rem, 1.35rem + 2.4vw, 3.1rem); letter-spacing: -.015em; }
h2 { font-size: clamp(1.45rem, 1.15rem + 1.3vw, 2.1rem); margin-top: 1.8em; }
h3 { font-size: clamp(1.2rem, 1.05rem + .7vw, 1.5rem); margin-top: 1.6em; }
p  { margin: 0 0 1.15em; text-wrap: pretty; }
strong { font-weight: 650; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }

/* The 677 separators Rolf drew out of underscores, now real rules - see
   TidyViewHelper::rules for why. Narrower and quieter than a bare <hr>: his
   were 50 characters in a line that holds 86, so a full-width rule across the
   measure would be a heavier mark than the one he was making. Centred, at two
   thirds, it reads as the same pause. */
hr.rule {
  width: min(66%, 28rem);
  margin-inline: auto;
  margin-block: clamp(1.8rem, 4vw, 2.8rem);
  border-top-color: var(--rule-strong);
}

/* Two rules in a row become one.

   Rolf, 2026-08-21: the old site showed one line at the end of a reportage,
   not two. He is right, and it was my doing. Several reportages end
   line / empty element / line - and until yesterday that empty element
   rendered a 53px box, so the two lines read as two separated marks. Removing
   the empty boxes (which is what he asked for, from the other direction) let
   the two rules fall against each other.

   Two separators with nothing between them mean nothing, so the second one
   goes. Written as a sibling rule on the element wrappers rather than on the
   rules themselves: each rule sits in its own .elem > .text, so the two <hr>
   are not siblings and hr + hr would never match. Three in a row collapse to
   one for the same reason - the second and third both have a rule-only element
   in front of them. */
.prose > .elem:has(> .text > hr.rule:only-child)
      + .elem:has(> .text > hr.rule:only-child) { display: none; }

/* ---------------------------------------------------------------- shell -- */
.site { min-height: 100dvh; display: flex; flex-direction: column; }
.wrap { width: var(--page); margin-inline: auto; }

/* --------------------------------------------------------------- header -- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(10px);
  border-bottom: 1px solid var(--rule);
}
.site-header__inner {
  width: var(--page); margin-inline: auto;
  display: flex; align-items: center; gap: var(--gap);
  min-height: 4rem;
}
.brand { display: inline-flex; align-items: center; gap: .7rem; text-decoration: none; font-weight: 600; }
.brand img { width: 40px; height: 40px; border-radius: 50%; }
.brand__name {
  font-family: var(--serif); font-size: 1.05rem; letter-spacing: .01em;
  /* One line now, so it sits on the middle of the logo by itself. The
     strapline that used to hang under it is a menu item instead. */
  line-height: 1.1;
}

/* The menu sits next to the name, not at the far edge.

   This is the substance of Rolf's complaint - "die einzelnen Rubriken stehen zu
   weit auseinander" - and it was worse than spacing: the brand was pinned left,
   the menu pushed right by margin-left:auto, and the whole width of the screen
   in between. The search box takes over the job of filling that gap, which is
   what it was always for.
   
   It also makes the phrase work: "Rolf Fröhlings" and "Reisereportagen" have to
   be near enough to read as one line. */
.site-nav { margin-left: clamp(.9rem, 2.5vw, 2rem); }
.site-nav ul { list-style: none; display: flex; gap: clamp(.7rem, 1.6vw, 1.15rem); margin: 0; padding: 0; }
.site-nav a { text-decoration: none; font-size: .95rem; color: var(--ink-soft); padding: .4rem 0; border-bottom: 2px solid transparent; }
.site-nav a:hover { color: var(--ink); border-bottom-color: var(--accent); }
.site-nav .current > a, .site-nav .active > a { color: var(--ink); border-bottom-color: var(--accent); }

.nav-toggle { display: none; }

@media (max-width: 60rem) {
  .nav-toggle {
    display: inline-flex; align-items: center; gap: .5rem; margin-left: auto;
    background: none; border: 1px solid var(--rule); border-radius: var(--radius);
    color: var(--ink); font: inherit; font-size: .9rem; padding: .5rem .8rem; cursor: pointer;
  }
  .site-nav {
    position: fixed; inset: 4rem 0 auto 0; margin: 0;
    background: var(--bg-raised); border-bottom: 1px solid var(--rule);
    max-height: calc(100dvh - 4rem); overflow-y: auto;
    display: none; box-shadow: var(--shadow);
  }
  .site-nav[data-open="true"] { display: block; }
  .site-nav ul { flex-direction: column; gap: 0; width: var(--page); margin-inline: auto; padding: .5rem 0 1.5rem; }
  .site-nav li + li { border-top: 1px solid var(--rule); }
  .site-nav a { display: block; padding: .85rem 0; font-size: 1rem; border-bottom: 0; }
}

/* ----------------------------------------------------------------- hero -- */
/* Was max-height: calc(100vh - 200px) — two thirds of the screen before a
   word of text. A hero should frame the story, not replace it. */
.hero { position: relative; margin-bottom: clamp(1.5rem, 4vw, 3rem); background: var(--bg-sunken); }
/* The whole photograph inside the band, not a crop of it. The band keeps its
   height; see the comment in Banner.html for what the server used to cut off
   before the browser ever saw the file. */
.hero__media {
  position: relative; width: 100%;
  aspect-ratio: 21 / 9; max-height: min(52vh, 460px);
  overflow: hidden; background: var(--bg-sunken);
}
.hero__media picture { position: relative; display: block; height: 100%; }
.hero__media img { width: 100%; height: 100%; object-fit: contain; }

/* The blurred ground: the same picture, covering, scaled up a little because a
   28px blur samples past the edges and would otherwise show them. */
.hero__media .hero__wash {
  position: absolute; inset: 0;
  object-fit: cover;
  filter: blur(28px) brightness(.6) saturate(.9);
  transform: scale(1.15);
}
@media (max-width: 45rem) { .hero__media { aspect-ratio: 4 / 3; max-height: 42vh; } }

/* The old overlay was a full-width rgba(0,0,0,.7) slab across the picture.
   A single line of type, bottom-left, reads better and hides less. */
.hero__caption {
  position: absolute; inset-inline: 0; bottom: 0;
  padding: clamp(1rem, 4vw, 2.5rem) 0;
  background: linear-gradient(to top, rgb(0 0 0 / .62), rgb(0 0 0 / .28) 45%, transparent);
  color: #fff;
}
.hero__caption .wrap { display: grid; gap: .25rem; }
.hero__kicker { font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; opacity: .9; }
.hero__caption h1 { color: #fff; margin: 0; text-shadow: 0 1px 18px rgb(0 0 0 / .45); }

/* ----------------------------------------------------------------- main -- */
main { flex: 1; }
.page-head { width: var(--page); margin: clamp(1.5rem, 4vw, 2.5rem) auto clamp(1rem, 3vw, 2rem); }
.page-head h1 { margin: 0; }

/* One reading column, images allowed to break out of it.

   minmax(0, 1fr), not 1fr. A track written "1fr" still refuses to shrink below
   the min-content of what sits in it, and these children carry
   max-width: 68ch - so on a phone the single column sized itself to 739px
   inside a 350px page and the whole document scrolled sideways. Measured at a
   390px viewport: document 768px against a 390px window, on a site whose
   visitors are a phone majority. The zero minimum is what lets it shrink. */
.prose { width: var(--page); margin-inline: auto; display: grid; grid-template-columns: minmax(0, 1fr); }

/* A guard, not a style. Content typed into the backend over twenty years holds
   things that have no break opportunity in them: bare URLs, and rows of fifty
   underscores standing in for a horizontal rule. One of those made the
   Vogelsbergkreis album 427px wide inside a 350px phone. break-word rather than
   anywhere, so this only takes effect when a word would otherwise overflow the
   line - it does not change how anything is measured. */
.prose .text, .prose .text p, .prose li { overflow-wrap: break-word; }
.prose > * { max-width: var(--measure); margin-inline: auto; width: 100%; }
.prose figure, .prose .imagegallery, .prose .banner, .prose .imgwrap { max-width: 100%; }

figure { margin: clamp(1.5rem, 4vw, 2.5rem) 0; }
figcaption, .imgcaption, .caption {
  font-size: .875rem; color: var(--ink-faint); margin-top: .6rem; line-height: 1.5;
}

/* --------------------------------------------------------------- images -- */
.imagegallery {
  display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
  margin: clamp(1.5rem, 4vw, 2.5rem) 0;
}
.imagegallery a { display: block; overflow: hidden; border-radius: var(--radius); background: var(--bg-sunken); }
.imagegallery img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; transition: transform .35s ease; }
.imagegallery a:hover img { transform: scale(1.03); }


/* Three across, because three is the only case this element has.

   themecontent_textwithimages holds 577 elements across the archive: 568 carry
   exactly three pictures, 3 carry two, 6 carry one. There is no fourth picture
   anywhere, which is why nothing here handles one.

   The old minimum track was 260px. The reading column on a reportage measures
   822px with a 32px gap, so three columns wanted 3x260 + 64 = 844 and missed by
   twenty-two pixels; auto-fit fell back to two and dropped the third picture
   onto a row of its own. 568 times - often enough to read as a layout decision
   rather than an accident.

   The minimum is a PERCENTAGE, not a pixel count, and that is the whole point.
   A first attempt used 240px, which fits three in 822px - and gave three in
   Chromium and Firefox and two in WebKit, because .prose > .elem is capped at a
   font-relative --measure and WebKit computes that column as 719px, not 822.
   A pixel floor encodes one engine's font metrics. 30% cannot: three tracks are
   90% plus two gaps and always fit, four are 120% and never do, whatever the
   container turns out to measure. Verified 3/3 engines at 1440, 900, 768, 500
   and 390px.

   The two media queries are the deliberate part - the point at which three
   thumbnails side by side stop being worth looking at. Below 48rem two, below
   30rem one, which is what was asked for. auto-fit still collapses the empty
   tracks, so the 3 elements holding two pictures and the 6 holding one need no
   rule of their own. */
/* Rolf, 2026-08-21: "Unter den Bilderreihen in den Reportagen sollte man eine
   Leerzeile einbauen. Das ist jetzt alles sehr eng beisammen."

   Measured on the Danzig page: an .imagegallery has 40px above and below, an
   .imgwrap had zero on both sides - the three pictures sat flush against the
   next paragraph. It was not a judgement about rhythm, it was a rule that had
   never been written.

   More below than above on purpose. A row of pictures belongs to the passage
   it follows and needs to come loose from the one it precedes, so the gap
   after it is the one a reader notices. 35px above, 48px below at 1280 - about
   a line and a half of body text, which is what he is asking for. */
.imgwrap {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(30%, 100%), 1fr));
  margin-block: clamp(1.4rem, 3.5vw, 2.2rem) clamp(2rem, 5vw, 3rem);
}
@media (max-width: 48rem) { .imgwrap { grid-template-columns: repeat(auto-fit, minmax(min(47%, 100%), 1fr)); } }
@media (max-width: 30rem) { .imgwrap { grid-template-columns: minmax(0, 1fr); } }

/* The pictures are clickable now, so they have to look it.

   Deliberately NOT the galleries' treatment. .imagegallery crops every
   thumbnail to a uniform 3:2 with object-fit, which makes a tidy grid - but 266
   of the 1749 pictures in these elements are portrait, and cropping a 4:3
   portrait to 3:2 throws away the outer 44% of its height. On a photo
   journalist's own archive that is a decision for him, not for a stylesheet.

   So: full frames, and align-items: start so a short picture sits at the top of
   its row instead of stretching to match a tall neighbour. The row is ragged at
   the bottom where the heights differ. That is the honest cost of not cutting
   anything, and it is reversible in four lines if he would rather have the
   crop. */
.imgwrap { align-items: start; }
.imgwrap figure { margin: 0; }
.imgwrap a { display: block; overflow: hidden; border-radius: var(--radius); background: var(--bg-sunken); cursor: zoom-in; }
.imgwrap a img { width: 100%; height: auto; display: block; transition: transform .35s ease; }
.imgwrap a:hover img, .imgwrap a:focus-visible img { transform: scale(1.03); }

/* --------------------------------------------------------------- audio --- */
/* 436 broadcast pieces — the thing no other travel site has. Give them presence. */
.audio {
  background: var(--accent-soft); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: clamp(1rem, 3vw, 1.5rem);
  margin: clamp(1.5rem, 4vw, 2.5rem) 0;
}
/* The label used to be a ::before on the whole box, so a block holding three
   recordings announced "Originalton" once. It belongs to each recording, next
   to that recording's running time. */
.audio audio { width: 100%; display: block; }
.audio__item + .audio__item { margin-top: 1.1rem; padding-top: 1.1rem; border-top: 1px solid var(--rule); }
.audio__meta { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin: 0 0 .45rem; }
.audio__label {
  font-size: .75rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent); font-weight: 600;
}
.audio__length { font-size: .82rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.audio__length::before { content: "· "; color: var(--rule); }

/* ---------------------------------------------------------------- cards -- */
.cards { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); padding: 0; list-style: none; }
.card { background: var(--bg-raised); border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
.card a { text-decoration: none; display: block; }
.card img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }
.card__body { padding: 1rem 1.1rem 1.2rem; }
.card__body h3 { margin: 0 0 .3rem; font-size: 1.1rem; }
.card__meta { font-size: .8rem; color: var(--ink-faint); }

/* --------------------------------------------------------------- footer -- */
.site-footer {
  margin-top: clamp(3rem, 8vw, 5rem);
  background: var(--bg-sunken); border-top: 1px solid var(--rule);
  padding: clamp(2rem, 5vw, 3.5rem) 0;
  font-size: .92rem; color: var(--ink-soft);
}
.site-footer__inner { width: var(--page); margin-inline: auto; display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); }
.site-footer h2 { font-family: var(--sans); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); margin: 0 0 .7rem; }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.site-footer a { color: var(--ink-soft); }
.site-footer a:hover { color: var(--ink); }
.social { display: flex; gap: 1rem; font-size: 1.25rem; }

/* --------------------------------------------------------------- utility - */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.button, button.startgal {
  display: inline-block; background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: var(--radius); padding: .6rem 1.1rem;
  font: inherit; font-size: .92rem; font-weight: 600; cursor: pointer; text-decoration: none;
}
.button:hover, button.startgal:hover { filter: brightness(1.08); }

/* --------------------------------------------------------------- lightbox */
dialog.lightbox { border: 0; padding: 0; background: transparent; max-width: 100vw; max-height: 100dvh; }
dialog.lightbox::backdrop { background: rgb(0 0 0 / .88); }
/* 78dvh, not 90: the bottom of the window now carries the caption and the
   counter, and at 90 the picture pushed the caption straight onto the counter.

   The width leaves 4.5rem clear on each side for the nav buttons. 96vw alone
   was not enough: at a 600px window it left 12px a side, so the button sat 48px
   deep on the photograph. Only where the buttons are actually at the sides -
   below 34rem they move to the bottom bar and the picture takes the full
   width. */
dialog.lightbox img {
  max-width: min(96vw, calc(100vw - 9rem)); max-height: 78dvh;
  width: auto; margin: auto; border-radius: 4px;
}
dialog.lightbox .lb-close {
  position: fixed; top: 1rem; right: 1rem; background: rgb(255 255 255 / .12);
  color: #fff; border: 0; border-radius: 50%; width: 2.75rem; height: 2.75rem;
  font-size: 1.4rem; cursor: pointer;
}

/* Forward and back in the enlarged view. Until now the only way through a
   gallery at full size was ArrowLeft/ArrowRight, which is nothing at all on a
   phone and undiscoverable on a desktop - so most readers closed the picture,
   aimed at the next thumbnail and opened it again, 223 times on the Alaska
   album. See the lightbox block in site.js.

   3rem targets, vertically centred at the edges, and they stay clear of the
   picture: the image is capped at 96vw, so on a phone a button would otherwise
   sit on top of the photograph rather than beside it. */
dialog.lightbox .lb-nav {
  position: fixed; top: 50%; translate: 0 -50%;
  display: grid; place-items: center;
  width: 3rem; height: 3rem; padding: 0;
  background: rgb(255 255 255 / .12); color: #fff;
  border: 0; border-radius: 50%;
  font-size: 2rem; line-height: 1; cursor: pointer;
  transition: background .15s ease;
}
dialog.lightbox .lb-nav span { display: block; margin-top: -.18em; }
dialog.lightbox .lb-nav:hover,
dialog.lightbox .lb-nav:focus-visible { background: rgb(255 255 255 / .28); }
dialog.lightbox .lb-prev { left: .6rem; }
dialog.lightbox .lb-next { right: .6rem; }
dialog.lightbox .lb-nav[hidden] { display: none; }

/* Which picture of how many. Without it the enlarged view does not look like a
   sequence, and there is no way to tell how much is left. */
dialog.lightbox .lb-count {
  position: fixed; left: 50%; translate: -50% 0; bottom: .9rem; margin: 0;
  color: rgb(255 255 255 / .8); font-size: .85rem;
  font-variant-numeric: tabular-nums;
}
dialog.lightbox .lb-count[hidden] { display: none; }

/* On a phone the picture uses the full width, so the buttons move off its
   sides and onto the bottom bar, beside the counter, where a thumb reaches
   them without covering the photograph. */
@media (max-width: 34rem) {
  dialog.lightbox .lb-nav { top: auto; bottom: .55rem; translate: none; }
  dialog.lightbox .lb-prev { left: 1.2rem; }
  dialog.lightbox .lb-next { right: 1.2rem; }
  dialog.lightbox img { max-width: 96vw; max-height: 62dvh; }
  /* The phone overrides for figcaption and .lb-count are NOT here: the general
     figcaption rule sits 700 lines further down, and with equal specificity the
     later one wins whatever the media query says. They live next to it
     instead. */
}

@media (prefers-reduced-motion: reduce) {
  dialog.lightbox .lb-nav { transition: none; }
}

/* Tables and iframes from RTE content shouldn't blow out the layout. */
.prose table { display: block; overflow-x: auto; max-width: 100%; border-collapse: collapse; }
.embed-container { position: relative; aspect-ratio: 16 / 9; }
.embed-container iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; border-radius: var(--radius); }

/* Click-to-load placeholder. The iframe sits behind it with no src, so nothing
   reaches Google until the button is pressed. */
.embed-consent {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .55rem; text-align: center;
  padding: clamp(1rem, 4vw, 2rem);
  background: var(--bg-sunken);
  border: 1px solid var(--rule); border-radius: var(--radius);
}
.embed-consent__lead { margin: 0; font-weight: 600; letter-spacing: .02em; }
.embed-consent__note {
  margin: 0; max-width: 46ch; font-size: .85rem; line-height: 1.5; color: var(--muted);
}
.embed-consent__btn {
  border: 0; border-radius: 999px; cursor: pointer;
  padding: .6rem 1.4rem; font: inherit; font-weight: 600;
  background: var(--accent); color: #fff;
}
.embed-consent__btn:hover { background: var(--accent-strong); }
.embed-consent__link { margin: 0; font-size: .8rem; }
.embed-consent__link a { color: var(--muted); }

/* Full-bleed children of the reading column.
   The hero and the wide galleries escape the 68ch measure and run edge to edge;
   everything else stays in the column. */
.prose > .hero,
.prose > .elem > .hero {
  width: 100vw; max-width: none;
  margin-inline: calc(50% - 50vw);
}
.prose > .elem { max-width: var(--measure); margin-inline: auto; width: 100%; }
.prose > .elem > .imagegallery,
.prose > .elem > .imgwrap {
  width: min(100vw - 2.5rem, 78rem);
  max-width: none;
  margin-inline: calc(50% - min(50vw - 1.25rem, 39rem));
}
@media (max-width: 60rem) {
  .prose > .elem > .imagegallery,
  .prose > .elem > .imgwrap { width: 100%; margin-inline: 0; }
}

/* ------------------------------------------------------------- galleries --
   Every picture is now a real thumbnail (see Textimg.html). A plain auto-fill
   grid; a lone picture keeps its own proportions and stays in the measure. */
.imagegallery {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
  margin: clamp(1.5rem, 4vw, 2.5rem) 0;
}
.imagegallery figure { margin: 0; }
.imagegallery a { display: block; border-radius: var(--radius); overflow: hidden; }
.imagegallery img {
  display: block; width: 100%; height: 100%;
  aspect-ratio: 4 / 3; object-fit: cover;
  transition: transform .35s ease;
}
.imagegallery a:hover img { transform: scale(1.03); }
.imagegallery figcaption { font-size: .85rem; color: var(--muted); margin-top: .45rem; line-height: 1.45; }
.imagegallery.single { grid-template-columns: 1fr; }
.imagegallery.single img { aspect-ratio: auto; height: auto; }
.prose > .elem > .imagegallery.single { width: 100%; margin-inline: 0; }
@media (prefers-reduced-motion: reduce) { .imagegallery a:hover img { transform: none; } }

/* =========================================================== reportage index
   Replaces the two hand-typed listing pages. The map, the filters and the
   cards are one component; without JavaScript the cards alone are the page,
   which is why nothing here depends on script to be readable. */

.prose--wide { --measure: 100%; }
.prose--wide > * { max-width: none; }

.reportagen__intro { text-align: center; margin-block: clamp(1rem, 4vw, 2.5rem) 1.5rem; }
.reportagen__intro h1 { margin-bottom: .4rem; }
.reportagen__stats { color: var(--muted); font-size: 1.02rem; }
.reportagen__stats strong { color: var(--ink); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- the map -- */
.worldmap { margin: 0 0 clamp(1rem, 3vw, 2rem); }
.worldmap__canvas {
  position: relative; width: 100%; aspect-ratio: 1000 / 420;
  background: var(--accent-soft); border: 1px solid var(--rule);
  border-radius: var(--radius); overflow: hidden;
}
.worldmap__base { position: absolute; inset: 0; width: 100%; height: 100%; }
.worldmap__base path { fill: var(--map-land); stroke: none; }

.worldmap__markers { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
.worldmap__marker { position: absolute; transform: translate(-50%, -50%); }
.worldmap__marker a { display: block; line-height: 0; }
.worldmap__dot {
  display: block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); border: 1.5px solid var(--bg);
  transition: transform .18s ease;
}
.worldmap__marker:hover .worldmap__dot,
.worldmap__marker a:focus-visible .worldmap__dot { transform: scale(1.6); }
.worldmap__marker[hidden] { display: none; }

/* the newest journeys pulse, so the eye lands on them first */
.worldmap__marker.is-recent .worldmap__dot { background: var(--accent-strong, var(--accent)); }
.worldmap__marker.is-recent .worldmap__dot::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 2px solid var(--accent); animation: rf-pulse 2.8s ease-out infinite;
}
@keyframes rf-pulse {
  0%   { transform: scale(.6); opacity: .9; }
  70%  { transform: scale(2.4); opacity: 0; }
  100% { transform: scale(2.4); opacity: 0; }
}

.worldmap__label {
  position: absolute; left: 50%; bottom: 145%; transform: translateX(-50%);
  background: var(--ink); color: var(--bg); font-size: .78rem; line-height: 1.2;
  padding: .3rem .55rem; border-radius: 4px; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .15s ease; z-index: 3;
}
.worldmap__marker:hover .worldmap__label,
.worldmap__marker a:focus-visible .worldmap__label { opacity: 1; }

.worldmap__legend {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: center;
  margin-top: .6rem; font-size: .85rem; color: var(--muted);
}
.worldmap__key { display: inline-flex; align-items: center; gap: .45rem; position: relative; }
.worldmap__key .worldmap__dot { position: relative; }

/* --------------------------------------------------------- time + filters -- */
.reportagen__time {
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  margin-bottom: 1rem; font-size: .92rem;
}
.reportagen__time input[type="range"] { flex: 1 1 14rem; max-width: 26rem; accent-color: var(--accent); }
.reportagen__time output { font-weight: 600; min-width: 7rem; }

.reportagen__controls {
  display: grid; gap: var(--gap); align-items: end;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  margin-bottom: clamp(1rem, 3vw, 1.75rem);
}
.reportagen__controls .field { display: grid; gap: .3rem; }
.reportagen__controls label { font-size: .82rem; color: var(--muted); font-weight: 600; }
.reportagen__controls input[type="search"],
.reportagen__controls select {
  font: inherit; padding: .55rem .7rem; border: 1px solid var(--rule);
  border-radius: var(--radius); background: var(--bg); color: var(--ink); width: 100%;
}
.reportagen__controls input[type="search"]:focus-visible,
.reportagen__controls select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.reportagen__result { align-self: end; margin: 0; color: var(--muted); font-size: .88rem; }

/* ------------------------------------------------------------------ cards -- */
.reportagen__list { grid-template-columns: repeat(auto-fill, minmax(min(270px, 100%), 1fr)); }
.card { list-style: none; }
.card[hidden] { display: none; }
.card__link {
  display: flex; flex-direction: column; height: 100%; text-decoration: none; color: inherit;
  border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
  background: var(--bg); transition: transform .2s ease, box-shadow .2s ease;
}
.card__link:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgb(0 0 0 / .10); }
.card__media { aspect-ratio: 3 / 2; overflow: hidden; }
.card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card__media--empty { background: var(--accent-soft); }
.card__body { padding: .9rem 1rem 1.1rem; display: grid; gap: .45rem; align-content: start; }
.card__title { font-size: 1.1rem; margin: 0; line-height: 1.25; }
.card__meta { margin: 0; display: flex; gap: .4rem; flex-wrap: wrap; }
.chip {
  font-size: .72rem; font-weight: 600; letter-spacing: .02em;
  background: var(--accent-soft); color: var(--accent); border-radius: 999px; padding: .15rem .55rem;
}
.card__teaser {
  margin: 0; font-size: .9rem; color: var(--muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.card__facts { margin: 0; font-size: .8rem; color: var(--muted); display: flex; gap: .75rem; flex-wrap: wrap; }
.card__facts span { white-space: nowrap; }
.card__facts span + span::before { content: "·"; margin-right: .75rem; color: var(--rule); }

.reportagen__empty { text-align: center; color: var(--muted); padding: 2rem 0; }

@media (prefers-reduced-motion: reduce) {
  .worldmap__marker.is-recent .worldmap__dot::after { animation: none; opacity: .5; }
  .card__link:hover { transform: none; }
}

/* ------------------------------------------------- orientation, first visit
   Two seconds to say what this is. The audio number leads because it is the
   one thing a travel blog cannot claim; the promise list says what the site
   is not, which for a working journalist is the more persuasive half. */

.reportagen__kicker {
  text-transform: uppercase; letter-spacing: .12em; font-size: .76rem;
  font-weight: 700; color: var(--accent); margin: 0 0 .5rem;
}
.reportagen__intro h1 {
  font-size: clamp(1.7rem, 4.6vw, 3rem); line-height: 1.12;
  margin: 0 auto .9rem; max-width: 20ch; text-wrap: balance;
}

ul.reportagen__stats {
  list-style: none; margin: 0 0 1.1rem; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: .4rem 2rem;
}
ul.reportagen__stats li { display: flex; flex-direction: column; line-height: 1.15; }
ul.reportagen__stats strong {
  font-size: clamp(1.25rem, 3vw, 1.7rem); font-variant-numeric: tabular-nums; color: var(--ink);
}
ul.reportagen__stats span { font-size: .78rem; color: var(--muted); }

.reportagen__promise {
  list-style: none; margin: 0 auto; padding: 0; max-width: 62ch;
  display: grid; gap: .3rem; font-size: .92rem; text-align: left;
}
.reportagen__promise li { padding-left: 1.6rem; position: relative; color: var(--ink-soft); }
.reportagen__promise li::before {
  position: absolute; left: 0; top: 0; font-weight: 700;
}
.reportagen__promise .is-yes::before { content: "✓"; color: #2f7d4f; }
.reportagen__promise .is-no::before  { content: "✕"; color: #a8443a; }

/* Returning visitors have already read the pitch. */
.reportagen__intro.is-compact .reportagen__promise,
.reportagen__intro.is-compact ul.reportagen__stats { display: none; }
.reportagen__intro.is-compact h1 { font-size: clamp(1.3rem, 3vw, 1.8rem); margin-bottom: .3rem; }

.reportagen__welcome-back {
  text-align: center; margin: 0 0 1.2rem; font-size: .95rem;
  background: var(--accent-soft); border-radius: var(--radius); padding: .7rem 1rem;
}
.reportagen__welcome-back a { font-weight: 600; }
.reportagen__welcome-back[hidden] { display: none; }

/* a card whose map marker is being hovered */
.card.is-linked .card__link { box-shadow: 0 0 0 2px var(--accent); }

/* -------------------------------------------------- story facts + chapters
   Rendered before the hero in the source, placed after it by order, because
   the banner and the body live in the same content column and the template
   cannot get between them. */

.prose > .hero       { order: -2; }
.prose > .storyfacts { order: -1; }

.storyfacts { margin: 0 0 clamp(1.25rem, 3vw, 2rem); }

.storyfacts__line {
  display: flex; flex-wrap: wrap; gap: .5rem 1.25rem;
  margin: 0 0 1rem; font-size: .88rem; color: var(--muted);
}
.storyfacts__line span { white-space: nowrap; }
.storyfacts__line span + span::before { content: "·"; margin-right: 1.25rem; color: var(--rule); }

.chapters {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--bg-sunken); padding: clamp(.9rem, 2.5vw, 1.4rem);
}
.chapters__title {
  font-family: var(--sans); font-size: .78rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 .7rem;
}
.chapters__list { margin: 0; padding: 0; list-style: none; counter-reset: none; }
.chapters__list li + li { border-top: 1px solid var(--rule); }
.chapters__list a {
  display: flex; gap: .8rem; align-items: baseline;
  padding: .55rem 0; text-decoration: none; line-height: 1.35;
}
.chapters__list a:hover { color: var(--accent); }
.chapters__no {
  font-variant-numeric: tabular-nums; font-size: .78rem; font-weight: 700;
  color: var(--accent); min-width: 1.4rem;
}

/* the chapter headings the merge produced */
.prose h2.chapter,
.prose h2[id^="kapitel-"] { scroll-margin-top: 5rem; }
.prose .standfirst {
  font-family: var(--serif); font-size: 1.08rem; font-style: italic;
  color: var(--muted); margin: -.4em 0 1.2em;
}

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

/* auto, so the search takes the slack the menu used to. */
.site-search { position: relative; margin-left: auto; }
.site-search input[type="search"] {
  font: inherit; font-size: .9rem; padding: .45rem .8rem; width: 12rem;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--bg-raised); color: var(--ink);
}
.site-search input[type="search"]:focus { width: 16rem; outline: 2px solid var(--accent); outline-offset: 1px; }
/* ------------------------------------------- the search on a phone --------
   flex-basis: 100% used to be here, meant to drop the search onto a row of its
   own. It never could: .site-header__inner is flex-wrap: nowrap, so a basis of
   100% only made the search claim the whole row and squeeze everything else.
   The brand was the loser - 82px wide holding 122px of content - and
   "Rolf Fröhlings" ran 24px straight through the Menü button. Measured at 320,
   360, 390, 412 and 430px; the overlap was there at every one of them.

   Thomas's suggestion, and it is the better answer than wrapping to two rows:
   the field collapses to a magnifier and opens across the header when tapped.
   The header stays one 64px row, the brand never shrinks, and the search gets
   the full width when it is actually being used - which is more room than the
   169px it had before.

   :focus-within does the work, so this behaves with scripting off too; site.js
   adds .is-open as well, only so the field stays open while the results panel
   is being tapped. */
@media (max-width: 60rem) {
  .site-header__inner { position: relative; }

  /* Never shrink the name. It is the one thing on the page that has to be
     readable before anything else. */
  .brand { flex: none; }

  .site-search {
    order: 3; flex: none; margin-left: .6rem;
  }
  .site-search input[type="search"] {
    width: 2.75rem; padding-inline: 0; cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23726b60' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 1.15rem;
    transition: width .18s ease;
  }
  /* The value is kept, not cleared - a reader who closes the panel and reopens
     it should find the word still there - but it must not spill over the icon
     while the field is a circle. */
  .site-search:not(.is-open) input[type="search"] { color: transparent; }
  .site-search:not(.is-open) input[type="search"]::placeholder { color: transparent; }

  .site-search:focus-within,
  .site-search.is-open {
    position: absolute; inset-inline: 0; top: 50%; translate: 0 -50%;
    margin: 0; z-index: 200;
  }
  .site-search:focus-within input[type="search"],
  .site-search.is-open input[type="search"] {
    width: 100%; color: var(--ink); cursor: auto;
    padding-inline: 2.6rem .8rem;
    background-position: .85rem center;
  }
  .site-search:focus-within input[type="search"]::placeholder,
  .site-search.is-open input[type="search"]::placeholder { color: var(--muted); }
}

@media (max-width: 60rem) and (prefers-reduced-motion: reduce) {
  .site-search input[type="search"] { transition: none; }
}

/* The smallest phones still in the statistics. With the brand no longer
   allowed to shrink, three fixed-width things have to fit a 280px row:
   158 + 67 + 44 plus two 16px gaps is 301, and it overflowed by 30. The gap is
   the part with no meaning attached, so that is what gives - along with the
   padding around Menü, which was set for a mouse rather than a thumb.

   Measured rather than calculated, twice: .5rem gaps still left 3px over the
   edge because the button comes out 61px wide, not the 55 I had assumed. The
   magnifier drops to 40px as well, which is still a comfortable thumb target
   and buys the margin back. 276 of 280 used. */
@media (max-width: 24rem) {
  .site-header__inner { gap: .4rem; }
  .nav-toggle { padding: .5rem .6rem; }
  .site-search { margin-left: .2rem; }
  .site-search input[type="search"] { width: 2.5rem; }
}

.site-search__panel {
  position: absolute; right: 0; top: calc(100% + .5rem); z-index: 150;
  width: min(30rem, calc(100vw - 2rem));
  max-height: min(70vh, 34rem); overflow-y: auto;
  background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: .8rem;
}
.site-search__panel[hidden] { display: none; }
.site-search__status { margin: 0 0 .5rem; font-size: .8rem; color: var(--muted); }
.site-search__group + .site-search__group { margin-top: .9rem; border-top: 1px solid var(--rule); padding-top: .8rem; }
.site-search__group h2 {
  font-family: var(--sans); font-size: .72rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 .35rem;
}
.site-search__group ul { list-style: none; margin: 0; padding: 0; }
.site-search__group a {
  display: block; padding: .4rem .5rem; border-radius: 6px;
  text-decoration: none; font-size: .9rem; line-height: 1.4;
}
.site-search__group a:hover, .site-search__group a:focus-visible { background: var(--accent-soft); }
.site-search__snippet, .site-search__in { display: block; font-size: .82rem; color: var(--muted); }
.site-search mark { background: var(--accent-soft); color: inherit; font-weight: 600; padding: 0 .1em; border-radius: 2px; }

/* ------------------------------------------------------- map, second pass --
   [hidden] loses to display:flex, which is why the time filter showed up even
   with no years to offer. State first, then layout. */
.reportagen__time[hidden],
.worldmap__marker[hidden] { display: none !important; }

.worldmap__viewport {
  position: relative; width: 100%; aspect-ratio: 1000 / 389;
  background: var(--accent-soft); border: 1px solid var(--rule);
  border-radius: var(--radius); overflow: hidden; touch-action: none;
}
.worldmap__viewport[data-zoomed="true"] { cursor: grab; }
.worldmap__viewport[data-zoomed="true"]:active { cursor: grabbing; }

.worldmap__canvas {
  position: absolute; inset: 0; transform-origin: 0 0;
  aspect-ratio: auto; background: none; border: 0; border-radius: 0; overflow: visible;
  --z: 1;
}
/* the dot keeps its screen size however far in you zoom */
.worldmap__marker { transform: translate(-50%, -50%) scale(calc(1 / var(--z, 1))); }
.worldmap__label { transform: translateX(-50%); }

.worldmap__zoom {
  position: absolute; right: .6rem; bottom: .6rem; z-index: 5;
  display: flex; flex-wrap: wrap; gap: .3rem; justify-content: flex-end; max-width: 60%;
}
.worldmap__zoom button {
  font: inherit; font-size: .82rem; line-height: 1;
  min-width: 2rem; height: 2rem; padding: 0 .5rem;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  color: var(--ink); border: 1px solid var(--rule); border-radius: 6px; cursor: pointer;
}
.worldmap__zoom button:hover { background: var(--bg); border-color: var(--accent); }
.worldmap__zoom .is-wide { min-width: auto; }

.reportagen__time-end { font-variant-numeric: tabular-nums; font-size: .82rem; color: var(--muted); }

/* ------------------------------------------------------ two-handle range --
   A single slider filled purple to its left reads as "everything up to here
   is selected", which was never what it meant. Two handles say plainly that a
   span is being chosen, and the fill sits between them where it belongs. */

.reportagen__time { align-items: center; gap: .6rem 1rem; }
.reportagen__time-label { font-size: .82rem; color: var(--muted); font-weight: 600; }
.reportagen__time output { font-variant-numeric: tabular-nums; }

.rangeslider { position: relative; flex: 1 1 15rem; max-width: 26rem; height: 1.75rem; }
.rangeslider__track,
.rangeslider__fill {
  position: absolute; top: 50%; height: 4px; transform: translateY(-50%);
  border-radius: 999px; pointer-events: none;
}
.rangeslider__track { left: 0; right: 0; background: var(--rule); }
.rangeslider__fill { background: var(--accent); left: 0; width: 100%; }

.rangeslider input[type="range"] {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  margin: 0; padding: 0; background: none; border: 0;
  accent-color: auto;                 /* kills the browser's own left-hand fill */
  -webkit-appearance: none; appearance: none;
  pointer-events: none;               /* only the thumbs are grabbable */
}
.rangeslider input[type="range"]:focus-visible { outline: none; }
.rangeslider input[type="range"]::-webkit-slider-runnable-track { background: transparent; height: 100%; }
.rangeslider input[type="range"]::-moz-range-track { background: transparent; height: 100%; }
.rangeslider input[type="range"]::-moz-range-progress { background: transparent; }

.rangeslider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto;
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
  box-shadow: 0 1px 3px rgb(0 0 0 / .3); cursor: grab;
}
.rangeslider input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
  box-shadow: 0 1px 3px rgb(0 0 0 / .3); cursor: grab;
}
.rangeslider input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 3px solid var(--accent); outline-offset: 2px; }
.rangeslider input[type="range"]:focus-visible::-moz-range-thumb { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The legend swatch has to behave like the thing it explains, so the pulse
   rule keys off the dot rather than off the marker it happens to sit in. */
.worldmap__key.is-recent .worldmap__dot { background: var(--accent-strong, var(--accent)); }
.worldmap__key.is-recent .worldmap__dot::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 2px solid var(--accent); animation: rf-pulse 2.8s ease-out infinite;
}
.worldmap__key .worldmap__dot { position: relative; }
@media (prefers-reduced-motion: reduce) {
  .worldmap__key.is-recent .worldmap__dot::after { animation: none; opacity: .5; }
}

/* ====================================================== the story layout ===
   A reportage is now a whole journey - some run half an hour - so the summary
   travels with the reader instead of scrolling away at the top.

   The hero, the summary and the body are siblings: the banner and the body
   share colPos 1 and the template cannot get between them. Grid places them. */

@media (min-width: 62rem) {
  /* An album page uses the same two columns for its world overview. It is
     .prose--album and NOT .prose--story on purpose: the slider rules further
     down hang off .prose--story, and on an album the pictures are the page -
     they stay a grid. Sharing the class would have quietly undone that. */
  .prose--story, .prose--album {
    grid-template-columns: minmax(13rem, 17rem) minmax(0, 1fr);
    column-gap: clamp(1.5rem, 4vw, 3rem);
    align-items: start;
  }
  .prose--story > *, .prose--album > *     { grid-column: 2; max-width: var(--measure); }
  .prose--story > .hero, .prose--album > .hero { grid-column: 1 / -1; max-width: none; }
  .prose--album > .worldnav,
  .prose--story > .storyfacts {
    grid-column: 1;
    /* Span the rows rather than occupying one. Every content element is its
       own grid row, so a summary confined to row 2 pushed the entire story
       below its own height - and because the first element after the hero is
       an empty one, nothing readable landed beside it at all. Spanning lets
       the prose flow up alongside, which also gives sticky something to stick
       against. */
    grid-row: 2 / span 9999;
    position: sticky; top: 5rem; align-self: start;
    max-height: calc(100dvh - 7rem); overflow-y: auto; overscroll-behavior: contain;
    max-width: none;
  }
  /* the wide breakout assumed one centred column; in two it would escape sideways */
  .prose--story > .elem > .imagegallery,
  .prose--album > .elem > .imagegallery,
  .prose--story > .elem > .imgwrap,
  .prose--album > .elem > .imgwrap { width: 100%; margin-inline: 0; }
}

.storyfacts { font-size: .92rem; }
.storyfacts__place { display: flex; gap: .35rem; flex-wrap: wrap; margin: 0 0 .8rem; }
.storyfacts__list {
  list-style: none; margin: 0 0 1.2rem; padding: 0;
  display: grid; gap: .3rem; color: var(--muted); font-size: .86rem;
}
.storyfacts__list strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.storyfacts__source {
  margin: 1rem 0 0; padding-top: .8rem; border-top: 1px solid var(--rule);
  font-size: .78rem; color: var(--muted);
}
.chapters__list a[aria-current="true"] { color: var(--accent); font-weight: 600; }
.chapters__list a[aria-current="true"] .chapters__no { text-decoration: underline; }

@media (max-width: 61.999rem) {
  /* below the two-column breakpoint it is a plain block above the story */
  .storyfacts { border: 1px solid var(--rule); border-radius: var(--radius);
                background: var(--bg-sunken); padding: clamp(.9rem, 3vw, 1.3rem); }
  .storyfacts .chapters { border: 0; background: none; padding: 0; }
}

/* ------------------------------------------------- galleries as a slider --
   A journey carries a lot of pictures - Alaska has 229 - and as a grid they
   pushed the text apart by whole screens. One row that scrolls keeps the
   reading rhythm; clicking still opens the near-fullscreen viewer. */

.prose--story .imagegallery:not(.single), .strip {
  display: flex; flex-wrap: nowrap; gap: .7rem;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-behavior: smooth;
  padding-bottom: 1.1rem;
}

/* This bar is the main way through 61 pictures, so it is a control, not
   decoration - 22px tall with a thumb you cannot miss.

   The two blocks below must NOT be combined. Chrome supports the standard
   scrollbar-width/scrollbar-color, and the moment either is set on an element
   it discards every ::-webkit-scrollbar rule for it. Setting both, as I first
   did, silently cancelled the custom height and left the default bar. So the
   webkit rules stand alone, and the standard properties are applied only where
   ::-webkit-scrollbar does not exist - which is Firefox. */
.prose--story .imagegallery:not(.single)::-webkit-scrollbar,
.strip::-webkit-scrollbar { height: 22px; }
.prose--story .imagegallery:not(.single)::-webkit-scrollbar-track,
.strip::-webkit-scrollbar-track {
  background: var(--bg-sunken); border-radius: 999px;
  border: 4px solid var(--bg);
  background-clip: padding-box;
}
.prose--story .imagegallery:not(.single)::-webkit-scrollbar-thumb,
.strip::-webkit-scrollbar-thumb {
  background: var(--accent); border-radius: 999px;
  /* the border only insets the paint; the grab area stays the full 22px */
  border: 4px solid var(--bg);
  background-clip: padding-box;
  min-width: 4rem;
}
.prose--story .imagegallery:not(.single)::-webkit-scrollbar-thumb:hover,
.prose--story .imagegallery:not(.single)::-webkit-scrollbar-thumb:active,
.strip::-webkit-scrollbar-thumb:hover,
.strip::-webkit-scrollbar-thumb:active {
  background: var(--accent-strong);
  border-width: 3px;
  background-clip: padding-box;
}

@supports not selector(::-webkit-scrollbar) {
  /* Firefox: scrollbar-width takes only auto/thin/none, so the height cannot
     be set - auto is as wide as it gets. */
  .prose--story .imagegallery:not(.single), .strip {
    scrollbar-width: auto;
    scrollbar-color: var(--accent) var(--bg-sunken);
  }
}
.prose--story .imagegallery:not(.single) > figure,
.strip > figure {
  flex: 0 0 auto; width: clamp(190px, 42%, 300px); scroll-snap-align: start; margin: 0;
}
.prose--story .imagegallery:not(.single) img,
.strip img { aspect-ratio: 4 / 3; object-fit: cover; }

.gallery { position: relative; margin: clamp(1.5rem, 4vw, 2.5rem) 0; }
.gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 2.4rem; height: 2.4rem; border-radius: 50%;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  border: 1px solid var(--rule); color: var(--ink);
  font: inherit; font-size: 1.1rem; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
}
.gallery__nav:hover { border-color: var(--accent); }
.gallery__nav[hidden] { display: none; }
.gallery__nav--prev { left: -.6rem; }
.gallery__nav--next { right: -.6rem; }
.gallery__count {
  font-size: .78rem; color: var(--muted); margin: .35rem 0 0;
  font-variant-numeric: tabular-nums;
}
@media (hover: none) { .gallery__nav { display: none; } }

/* Der Anker des Vorspanns. Er braucht denselben Scroll-Abstand wie eine
   Kapitelüberschrift, damit die klebende Kopfzeile ihn nicht verdeckt.

   Bis zum 26.08.2026 war er ein leeres <span> im Text und trug deshalb
   display:block und height:0 - ohne die hätte ein leeres Inline-Element den
   Zeilenumbruch gestört. Er sitzt jetzt auf dem ersten Absatz selbst, weil
   CKEditor 5 leere Inline-Elemente beim Speichern entfernt: Rolf hätte den
   Anker durch einmaliges Öffnen und Speichern verloren, auf 76 Reportagen,
   ohne dass es ihm aufgefallen wäre.

   Die beiden alten Eigenschaften mussten dabei weg. Auf einen Absatz
   übertragen hätten sie ihn auf null Höhe gesetzt - der Vorspann wäre
   verschwunden. scroll-margin-top wirkt auf beiden Formen. */
.lead-anchor { scroll-margin-top: 5rem; }
.chapters__list .is-lead a { font-style: italic; }
.chapters__list .is-lead .chapters__no { color: var(--muted); }

/* On a story the hero is an establishing shot, not the destination - the
   reader came for the text. Shorter than the index hero so the opening
   paragraph is on screen when the page lands. */
.prose--story > .hero,
.prose--album > .hero { margin-bottom: clamp(1rem, 2.5vw, 1.75rem); }
.prose--story > .hero .hero__media,
.prose--album > .hero .hero__media { aspect-ratio: 24 / 9; max-height: min(38vh, 340px); }
@media (max-width: 45rem) {
  .prose--story > .hero .hero__media,
  .prose--album > .hero .hero__media { aspect-ratio: 16 / 9; max-height: 30vh; }
}

/* The full-bleed hero is 100vw wide, and 100vw counts the vertical scrollbar -
   so every page with one scrolled sideways by that many pixels. clip rather
   than hidden: hidden would make body a scroll container and break the sticky
   header and the sticky story summary. */
body { overflow-x: clip; }

/* The enlarged view carries the caption too - it is the one place a reader
   actually needs to know what the photograph shows. */
dialog.lightbox figure { margin: 0; display: grid; place-items: center; gap: .6rem; }

/* The caption is pinned to the bottom of the window rather than hung under the
   picture, because the counter lives down there too and the two collided: at
   1280x800 the caption ran 759-780 and the counter 763-786, seventeen pixels
   of one printed over the other. Two things fixed to the same edge cannot be
   left to chance about which one wins, so both get a place: caption above,
   counter below, and the picture gives up the height to make room. */
dialog.lightbox figcaption {
  position: fixed; left: 50%; translate: -50% 0; bottom: 2.7rem;
  color: #fff; font-size: .92rem; line-height: 1.45; text-align: center;
  max-width: min(72ch, 90vw); margin: 0;
  text-shadow: 0 1px 3px rgb(0 0 0 / .8);
}
dialog.lightbox figcaption[hidden] { display: none; }

/* On a phone the two nav buttons are in the bottom bar as well, so the caption
   has to clear those on top of the counter. 5.4rem rather than 4.6, because
   most of Rolf's captions wrap to two lines at 390px and a two-line caption
   still came down three pixels onto the counter.

   This block has to stand HERE, after the figcaption rule above it, not up with
   the rest of the phone lightbox styles: a media query adds no specificity, so
   the general rule 700 lines below would simply have won. It did, and the fix
   measured as having changed nothing at all. */
@media (max-width: 34rem) {
  dialog.lightbox figcaption { bottom: 5.4rem; }
  dialog.lightbox .lb-count { bottom: 1.5rem; }
}

/* ------------------------------------------------- two journeys up front --
   The opening block sat in a narrow column with a lot of nothing either side
   of it on a wide screen. These fill it with the thing the site is made of.
   Only from 78rem, where that space actually exists. */

.reportagen__top { display: grid; gap: var(--gap); }
.flankcard { display: none; }

@media (min-width: 78rem) {
  .reportagen__top {
    grid-template-columns: minmax(0, 1fr) minmax(0, 2.2fr) minmax(0, 1fr);
    align-items: center;
    column-gap: clamp(1.5rem, 3vw, 3rem);
  }
  .flankcard {
    display: block; text-decoration: none; color: inherit;
    border: 1px solid var(--rule); border-radius: var(--radius);
    background: var(--bg-raised); overflow: hidden;
    transition: transform .2s ease, box-shadow .2s ease;
  }
  /* Name the column each one belongs to. `order` only changes the sequence,
     not the placement - with both cards ahead of the intro in the source, one
     card took the middle column and the intro was pushed to the third. */
  .flankcard--left                      { grid-column: 1; grid-row: 1; }
  .reportagen__top > .reportagen__intro { grid-column: 2; grid-row: 1; }
  .flankcard--right                     { grid-column: 3; grid-row: 1; }

  .flankcard:hover  { transform: translateY(-3px); box-shadow: 0 8px 24px rgb(0 0 0 / .10); }
}

.flankcard__media { display: block; aspect-ratio: 7 / 5; overflow: hidden; }
.flankcard__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.flankcard__body { display: block; padding: .8rem .9rem 1rem; }
.flankcard__kicker {
  display: block; font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); font-weight: 700; margin-bottom: .25rem;
}
.flankcard__title {
  display: block; font-family: var(--serif); font-size: 1.02rem; line-height: 1.25;
  margin-bottom: .3rem;
}
.flankcard__facts { display: block; font-size: .78rem; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .flankcard:hover { transform: none; } }

/* ------------------------------------------------------- read this aloud --
   The prose is spoken and the broadcast recordings play where they sit in the
   text. The passage being read is marked, because on a half-hour story a
   listener who looks away needs to find their place again. */

.storyfacts__read { margin: 0 0 1.1rem; }
.button--read { width: 100%; justify-content: center; display: inline-flex; gap: .5rem; align-items: center; }
.button--read[hidden] { display: none; }
.button--read.is-playing { background: var(--accent-strong); }
.button__icon { font-size: .8em; line-height: 1; }

.prose--story .is-reading,
.prose .is-reading {
  background: var(--accent-soft);
  box-shadow: -0.6rem 0 0 var(--accent-soft), 0.6rem 0 0 var(--accent-soft);
  border-radius: 2px;
}
@media (prefers-reduced-motion: reduce) { .is-reading { transition: none; } }

/* The voice picker sits under the read button. Only shown when the machine
   actually has more than one German voice to choose between. */
.storyfacts__voice {
  display: block; width: 100%; margin-top: .45rem;
  font: inherit; font-size: .8rem; padding: .35rem .5rem;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--bg); color: var(--muted);
}
.storyfacts__voice[hidden] { display: none; }

/* --------------------------------------------------------------- profil --
   A portrait page for the reporter, with his own horizons behind it. */

.profil { display: block; }

/* Ein Band, eine Hoehe - auf der Profilseite dieselbe wie auf der Startseite.

   Bis zum 06.09.2026 war das Profilband hoeher, min-height clamp(340px, 56vh,
   560px), weil es Zeilenvorsatz, Ueberschrift, vier Zeilen Vorspann und eine
   Reihe aus vier Zahlen tragen musste. Rolf: "Dann koennte man das Banner auch
   so schmal halten wie auf der Startseite." Seit dort nur noch Ueberschrift und
   ein Satz stehen, gibt es keinen Grund mehr fuer zwei Hoehen.

   Die Hoehe haengt an der Breite, nicht an der Fensterhoehe.

   Solange das Band beschnitt, war das gleichgueltig: cover fuellt jede Form.
   Seit das ganze Foto hineinpasst, entscheidet die Form darueber, wieviel vom
   Band das Foto ist und wieviel Unschaerfe daneben. Bei 306px Hoehe und 1248px
   Breite blieben von einem 4:3-Bild 419px - ein Drittel Foto, zwei Drittel
   Grund. Auf der Aufnahme, der Rolf zugestimmt hat, war es rund die Haelfte.

   21:9 mit Deckel, darunter 4:3 - dieselben zwei Werte wie bei .hero__media,
   damit dieses Band und die 104 Reportagebanner gleich gebaut sind. Auf einem
   Telefon fuellt ein 4:3-Foto damit die volle Breite und es bleibt gar kein
   Rand. Der Deckel haelt auf der Startseite den Satz "34 Jahre unterwegs. 105
   Reisen." oberhalb der Falz - er steht unter dem Band und sagt, was die Seite
   ist.

   width: 100% ist nicht ueberfluessig. Ohne die Angabe ist die Breite "auto",
   und ein Kasten mit aspect-ratio, dem die max-height in den Weg kommt, gibt
   dann BREITE ab, um das Verhaeltnis zu halten: bei 1920 stand das Band mit
   966 statt 1248 Punkten da, schmaler als die Seite darunter. Gemessen, nicht
   vermutet. Mit fester Breite deckelt max-height nur noch die Hoehe, und
   overflow: hidden schneidet den Rest ab - genauso wie bei .hero__media. */
.profil-hero {
  position: relative; isolation: isolate;
  width: 100%;
  aspect-ratio: 21 / 9;
  max-height: min(46vh, 420px);
  display: flex; align-items: flex-end;
  border-radius: var(--radius); overflow: hidden;
  margin-bottom: clamp(2rem, 5vw, 3.5rem);
  background: var(--bg-sunken);
}
@media (max-width: 45rem) {
  .profil-hero { aspect-ratio: 4 / 3; max-height: 42vh; }
}
.profil-hero__stage { position: absolute; inset: 0; z-index: 0; }
.profil-hero__layer {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity .9s ease;
}
.profil-hero__layer.is-on { opacity: 1; }

/* Das ganze Foto im Band, nicht ein Ausschnitt davon - dieselbe Bauweise wie
   .hero__media auf den Reportageseiten, und aus demselben Grund.

   Vorher stand hier object-fit: cover. Das Band ist so breit wie das Fenster
   und bleibt fast gleich hoch, seine Form aendert sich also mit jedem Monitor;
   cover fuellt sie und schneidet den Rest weg. Auf einem breiten Bildschirm
   blieb vom Hochformat des Berges ein Querstreifen aus der Mitte, auf einem
   Telefon fast das ganze Bild. Rolf sah nur seinen eigenen Monitor und hielt
   das fuer eine Eigenschaft seiner Fotos.

   contain gibt Breite ab statt Hoehe. Was daneben frei bleibt, fuellt dasselbe
   Foto noch einmal, weichgezeichnet und abgedunkelt - graue Balken wuerden den
   Kompromiss ausrufen, das hier faellt nicht auf. Die Werte sind die aus
   .hero__media .hero__wash, damit die beiden Baender wie ein System aussehen:
   scale(1.15), weil ein Weichzeichner von 28px ueber die Kanten hinausgreift
   und sie sonst zu sehen waeren. */
.profil-hero__layer img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; display: block;
}
.profil-hero__layer .profil-hero__pic { object-fit: contain; }
.profil-hero__layer .profil-hero__wash {
  object-fit: cover;
  filter: blur(28px) brightness(.6) saturate(.9);
  transform: scale(1.15);
}

/* The text sits on photographs of every possible brightness, so it needs its
   own ground rather than a text-shadow.

   The first version was one vertical gradient across the full width, 82% black
   along the whole bottom edge. But the text only occupies the bottom LEFT, so
   that darkened the bottom right for nothing - Rolf's complaint, and a fair
   one: "auch da, wo kein Text steht, sodass man zu wenig erkennen kann."

   Now it shades where the words are. The strong pass runs left to right and is
   gone by two thirds across; a much lighter one along the foot keeps the row of
   numbers readable. The right-hand side of the photograph is left alone. */
.profil-hero::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  /* A corner, not a column. The horizontal pass shaded the whole left edge
     evenly from top to bottom, so the upper left was as dark as the words at
     the foot while carrying no words at all - Rolf noticed the right-hand side
     was clear and asked why the top left was not.

     An ellipse anchored at the bottom-left corner puts the darkness where the
     text actually is and lets it fall away in both directions: to the right,
     as before, and now upwards as well.

     The radii are 62% x 78% and both matter. The first version used 125% x
     105%, which is larger than the box, so the ellipse still reached the top
     edge and the "corner" was really the same left-hand band as before - which
     is what Rolf drew a line through. Kept under 100% it stops short of the
     top and short of the middle, and the sky stays visible.

     There is no second gradient along the foot any more: that one ran the full
     width and re-darkened the bottom right. The type carries its own shadow
     instead, which is what makes so light a wash possible at all. */
  /* Vertical fade for the shading itself, horizontal fade as a MASK. Stacked
     background layers add up, so two linear gradients would have darkened the
     whole foot and the whole left edge. A mask multiplies instead: the wash is
     only visible where both are present, which is the lower left and nothing
     else - a flat top edge and a straight right edge, which is the shape Rolf
     drew and the one shape a radial gradient cannot make. */
  background: linear-gradient(to top,
    rgba(0,0,0,.86) 0%, rgba(0,0,0,.74) 28%, rgba(0,0,0,.44) 50%,
    rgba(0,0,0,.14) 62%, rgba(0,0,0,0) 70%);
  -webkit-mask-image: linear-gradient(to right,
    #000 0%, #000 28%, rgba(0,0,0,.55) 40%, rgba(0,0,0,.15) 47%, transparent 53%);
  mask-image: linear-gradient(to right,
    #000 0%, #000 28%, rgba(0,0,0,.55) 40%, rgba(0,0,0,.15) 47%, transparent 53%);
}

/* On a narrow screen the text block is the full width, so there is no clear
   side to protect - back to a plain vertical wash, and a lighter one than the
   original. */
@media (max-width: 47.99rem) {
  .profil-hero::after {
    background: linear-gradient(to top,
      rgba(0,0,0,.72) 0%, rgba(0,0,0,.52) 40%, rgba(0,0,0,.12) 75%, rgba(0,0,0,0) 100%);
  }
}
/* The picture designation under the rotating hero - a working aid for Rolf's
   first pass, so he can name a photograph he wants out. Deliberately plain and
   small: it is scaffolding, and it should look like scaffolding rather than
   like a caption the site means to keep. */
.profil-hero__label {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  margin: 0; padding: .45rem clamp(1rem, 4vw, 2.5rem);
  background: rgb(0 0 0 / .55);
  color: rgb(255 255 255 / .92);
  font-size: .78rem; line-height: 1.35;
  font-variant-numeric: tabular-nums;
  text-align: center;
  overflow-wrap: anywhere;
}
.profil-hero__label:empty { display: none; }

/* Dasselbe Band auf der Startseite, nur ohne Text darauf.

   Deshalb faellt die Abschattung weg: .profil-hero::after dunkelt die untere
   linke Ecke ab, damit die Ueberschrift auf jedem Foto lesbar bleibt. Hier
   stehen dort keine Worte, und Rolf hat genau diesen Fall schon einmal
   bemerkt - "auch da, wo kein Text steht, sodass man zu wenig erkennen kann."
   Die Beschriftung braucht die Abschattung nicht, sie bringt ihren eigenen
   dunklen Streifen mit.

   Die Hoehe steht seit dem 06.09.2026 an .profil-hero selbst; beide Baender
   sind gleich hoch, und hier bleibt nur noch der Unterschied, der einer ist. */
.profil-hero--bare::after { content: none; }

/* Pause, back, forward - the other half of the same working aid. Sitting just
   above the designation, so the picture, its name and the way to the next one
   are one block rather than three. */
.profil-hero__controls {
  position: absolute; z-index: 3;
  right: clamp(1rem, 4vw, 2.5rem); bottom: 2.6rem;
  display: flex; gap: .4rem;
}
.profil-hero__controls[hidden] { display: none; }
.profil-hero__controls button {
  min-width: 2.4rem; height: 2.4rem; padding: 0 .7rem;
  display: grid; place-items: center;
  background: rgb(0 0 0 / .55); color: #fff;
  border: 1px solid rgb(255 255 255 / .25); border-radius: 999px;
  font: inherit; font-size: .8rem; line-height: 1; cursor: pointer;
}
.profil-hero__controls button:hover,
.profil-hero__controls button:focus-visible { background: rgb(0 0 0 / .78); }
.profil-hero__controls [data-hero-prev] span,
.profil-hero__controls [data-hero-next] span { font-size: 1.4rem; margin-top: -.12em; }

/* Auf dem Telefon in die obere rechte Ecke. Seit das Profilband so niedrig ist
   wie das der Startseite (06.09.2026), teilen sich dort unten Satz, Knoepfe und
   Beschriftung 263 Punkte Hoehe: Bei 390 Punkten Breite lagen die drei Knoepfe
   ueber der rechten Haelfte des Satzes und die Beschriftungsleiste ueber seiner
   letzten Zeile - gemessen, siehe band-vorschau.py. Der Satz braucht den
   unteren Rand, weil dort die Abschattung ist; die Knoepfe brauchen nur eine
   Ecke ohne Worte. Oben rechts ist auf beiden Baendern eine.

   Die Luft unter dem Text steht bei .profil-hero__lead, hinter der Regel, die
   sie sonst ueberschreiben wuerde. */
@media (max-width: 45rem) {
  .profil-hero__controls { top: .8rem; right: .8rem; bottom: auto; }
}

.profil-hero__body {
  position: relative; z-index: 2; color: #fff;
  padding: clamp(1.4rem, 4vw, 2.8rem);
  max-width: 60ch;
  /* The wash is now a corner rather than a column, which clears the upper left
     of the photograph - and puts the heading and the lead on whatever happens
     to be there. Over the Arenal picture that is bright sky and lit foliage. So
     the type carries its own ground: a wide soft shadow that reads as depth
     rather than as a box, plus a tight one for the edges. This is what lets the
     gradient stay light enough to see the picture through. */
  text-shadow: 0 1px 2px rgba(0,0,0,.55), 0 2px 18px rgba(0,0,0,.65);
}
.profil-hero__body h1 {
  margin: 0 0 .6rem; color: #fff;
  font-size: clamp(2rem, 6vw, 3.4rem); line-height: 1.05;
}
/* Nur noch Ueberschrift und ein Satz. Rolf am 06.09.2026: "im Banner oben zu
   viele Informationen" - Zeilenvorsatz und die Reihe aus vier Zahlen sind aus
   Profil.html heraus, und ihre Regeln (.profil-hero__kicker, .profil-hero__stats)
   hier mit. Der Satz ist das letzte Element, also kein Abstand mehr darunter. */
.profil-hero__lead {
  margin: 0; color: rgba(255,255,255,.94);
  font-size: clamp(1rem, 2.2vw, 1.15rem); line-height: 1.55;
}
/* 2.6rem Luft unter dem Text auf dem Telefon: die Beschriftungsleiste am Fuss
   des Bandes ist rund 31 Punkte hoch, die Polsterung des Textkoerpers dort nur
   22 - die letzte Zeile des Satzes lag 9 Punkte unter der Leiste. Auf breiten
   Bildschirmen sind es 45 Punkte Polsterung, da reicht es ohne Zutun.

   Steht HINTER .profil-hero__body, nicht bei den Knoepfen: dessen
   padding-Kurzform wuerde eine fruehere padding-bottom-Angabe gleicher
   Spezifitaet stumm ueberschreiben. Der erste Versuch stand vorn und hat
   nichts bewirkt - gemessen. */
@media (max-width: 45rem) {
  .profil-hero__body { padding-bottom: 2.6rem; }
}

.profil-grid {
  display: grid; gap: clamp(1.6rem, 4vw, 3rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 62rem) {
  .profil-grid { grid-template-columns: minmax(0, 1fr) 20rem; }
  .profil-side { position: sticky; top: 5.5rem; }
}
.profil-main h2 {
  margin: 2.2rem 0 .7rem; font-size: clamp(1.15rem, 2.6vw, 1.5rem);
}
.profil-main h2:first-child { margin-top: 0; }
.profil-lead { font-size: 1.08rem; }

/* Die Auszeichnungen kommen jetzt aus dem Editor, also ohne die Klassen, die
   das Template vergeben hatte. Angesprochen wird deshalb der Ort statt der
   Klasse: eine Liste im Prosateil der Profilseite. Auf dieser Seite ist das
   die Auszeichnungsliste, und eine zweite Liste, die Rolf dort anlegt, saehe
   genauso aus - was richtig ist, nicht zufaellig.

   Die Klassennamen bleiben stehen: sollte der Abschnitt je wieder aus dem
   Template kommen, greift die Regel weiter. */
.profil-awards,
.profil-main .elem ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .9rem; }
.profil-awards li,
.profil-main .elem ul li {
  border-left: 3px solid var(--accent); padding: .1rem 0 .1rem .9rem;
}
.profil-awards__what { margin: 0; }
.profil-awards__which { margin: .15rem 0 0; font-size: .92rem; }
/* erster Absatz im Listenpunkt = die Auszeichnung, zweiter = die Reportage */
.profil-main .elem ul li p { margin: 0; }
.profil-main .elem ul li p + p { margin-top: .15rem; font-size: .92rem; }

.profil-side { display: grid; gap: 1rem; }
.profil-card {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--bg-sunken); padding: clamp(.9rem, 3vw, 1.3rem);
}
.profil-card__title {
  margin: 0 0 .7rem; font-size: .8rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em; color: var(--muted);
}
.profil-facts { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: .35rem .8rem; }
.profil-facts dt { font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.profil-facts dd { margin: 0; font-size: .92rem; }

/* Der Steckbrief kommt jetzt als Tabelle aus dem Editor - das ist die einzige
   zweispaltige Form, die Rolf dort ohne HTML bauen und aendern kann. Sie wird
   hier so gesetzt, dass sie aussieht wie die Definitionsliste vorher: linke
   Spalte klein und versal als Beschriftung, rechte Spalte der Wert.

   Der Elementkopf traegt die Ueberschrift, damit auch "Steckbrief" aenderbar
   ist und nicht als einziges Wort im Template zurueckbleibt. */
/* Leere Spalte, keine Karte - sonst bliebe ein leerer Kasten stehen, wenn Rolf
   das Element loescht. :has statt einer Bedingung im Template, weil die
   Bedingung dort einen 500 verursacht hat: v:content.render vertraegt keine
   f:variable. */
.profil-card--steckbrief:not(:has(.elem)) { display: none; }
.profil-card--steckbrief .elem { margin: 0; }
.profil-card--steckbrief .text > header h2,
.profil-card--steckbrief h2 {
  font-family: var(--sans); font-size: .8rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 .7rem;
}
.profil-card--steckbrief table { width: 100%; border-collapse: collapse; }
.profil-card--steckbrief td { padding: 0 0 .35rem; vertical-align: baseline; font-size: .92rem; }
.profil-card--steckbrief tr > td:first-child {
  font-size: .78rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: .06em; padding-right: .8rem; white-space: nowrap;
}
/* Wenn Rolf statt einer Tabelle einfach Absaetze tippt, sieht das immer noch
   ordentlich aus statt kaputt. */
.profil-card--steckbrief p { margin: 0 0 .35rem; font-size: .92rem; }
.profil-card--go p { font-size: .92rem; }
.profil-card--go .btn { display: block; text-align: center; margin-top: .55rem; }

@media (prefers-reduced-motion: reduce) {
  /* A background changing every second is precisely what this setting exists
     to stop. One photograph, no transition. */
  .profil-hero__layer { transition: none; }
}

/* Buttons that are links. There was no such class before - every call to
   action on the old site was a bare underlined link. */
.btn {
  display: inline-block; border-radius: 999px; text-decoration: none;
  padding: .55rem 1.3rem; font-weight: 600; font-size: .95rem;
  background: var(--accent); color: #fff; border: 1px solid var(--accent);
}
.btn:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }
.btn--quiet { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--quiet:hover { background: transparent; border-color: var(--accent); color: var(--accent); }


/* ------------------------------------------- world / country overview --
   The sideways move out of an album. It sits in the same sticky left column a
   reportage gives its summary, and scrolls inside itself - all 79 places are
   open at once, because a reader who wants the next village should not have to
   guess which country to unfold first. Below 62rem it is a plain block above
   the pictures. */

.worldnav { font-size: .9rem; }
@media (max-width: 61.99rem) {
  .worldnav {
    border: 1px solid var(--rule); border-radius: var(--radius);
    background: var(--bg-sunken); padding: clamp(.9rem, 3vw, 1.3rem);
    margin-bottom: 1.5rem;
    /* on a phone the full list is most of a screen - cap it and let it scroll */
    max-height: 22rem; overflow-y: auto; overscroll-behavior: contain;
  }
}
.worldnav__title {
  margin: 0 0 .9rem; font-size: .75rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em; color: var(--muted);
  position: sticky; top: 0; background: var(--bg); padding-bottom: .3rem;
}
@media (max-width: 61.99rem) { .worldnav__title { background: var(--bg-sunken); } }

.worldnav__continent { margin-bottom: 1rem; }
.worldnav__continent-name {
  margin: 0 0 .4rem; font-weight: 700; font-size: .82rem;
  text-transform: uppercase; letter-spacing: .08em;
  border-bottom: 1px solid var(--rule); padding-bottom: .2rem;
}
.worldnav__continent.is-here .worldnav__continent-name { color: var(--accent); }

.worldnav__land { margin: 0 0 .55rem; }
.worldnav__land-name {
  margin: 0 0 .1rem; font-size: .82rem; font-weight: 600; color: var(--muted);
}
.worldnav__land.is-here .worldnav__land-name { color: var(--ink); }

.worldnav__places { list-style: none; margin: 0; padding: 0 0 0 .1rem; display: grid; }
.worldnav__place a {
  display: flex; justify-content: space-between; gap: .6rem; align-items: baseline;
  padding: .16rem .4rem; border-radius: calc(var(--radius) / 2);
  text-decoration: none; color: var(--ink); line-height: 1.35;
}
.worldnav__place a:hover { background: var(--bg-sunken); color: var(--accent); }
.worldnav__place-name { font-size: .86rem; }
.worldnav__n { font-size: .74rem; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Where you are. Marked with weight and a bar rather than colour alone, so it
   still reads when the page is printed or the reader cannot separate the two
   greens. */
.worldnav__place.is-current a {
  background: var(--bg-sunken); font-weight: 700;
  box-shadow: inset 3px 0 0 var(--accent);
}
.worldnav__place.is-current .worldnav__n { color: var(--ink); }

.worldnav__all { margin: .5rem 0 0; font-size: .82rem; }

/* ---------------------------------------------------- the English page --
   One page, so it borrows the index's kicker and stat rules and adds only
   what it needs of its own. */

.english__lead {
  font-size: clamp(1.02rem, 2.2vw, 1.18rem); line-height: 1.6;
  max-width: var(--measure);
}
.english__intro { margin-bottom: clamp(1.5rem, 4vw, 2.5rem); }
.english__intro h1 { max-width: 22ch; }

/* The one thing an English visitor must not miss. Marked with a rule and a
   ground rather than a colour alone, so it survives printing. */
.english__notice {
  border: 1px solid var(--rule); border-left: 4px solid var(--accent);
  border-radius: var(--radius); background: var(--bg-sunken);
  padding: clamp(1rem, 3vw, 1.5rem);
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
  max-width: var(--measure);
}
.english__notice-title { margin: 0 0 .5rem; font-weight: 700; font-size: 1.05rem; }
.english__notice p { margin: 0 0 .7rem; }
.english__notice ul { margin: 0 0 .7rem; padding-left: 1.1rem; display: grid; gap: .45rem; }
.english__hint { font-size: .9rem; color: var(--muted); margin-bottom: 0 !important; }

.english__go {
  display: flex; flex-wrap: wrap; gap: .6rem;
  margin: clamp(1.2rem, 3vw, 2rem) 0;
}
.english__contact { font-size: .9rem; color: var(--muted); max-width: var(--measure); }

/* Rolf himself, under "Zur Person" - inside the text column since 07.09.2026,
   where he asked for it, not across the full width at the foot any more. The
   row itself is .strip; only the lead line needs a rule of its own. */
.profil-unterwegs__lead { margin: 0 0 .6rem; color: var(--muted); font-size: .95rem; }

/* The three exhibition shots. A plain row, not a slider - three pictures do
   not need controls. */
.profil-exhibition {
  list-style: none; margin: 1rem 0 0; padding: 0;
  display: grid; gap: .7rem;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.profil-exhibition img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius);
}

/* ------------------------------------------------------------ Blocksatz --
   Rolf wants the reportages justified: "vielleicht gerade nicht modern, sieht
   aber einfach viiiiel schöner aus." Fair - it is what the pieces looked like
   in print, which is where most of them started.

   Applied to the story body rather than to the .text-justify class the old
   editor left behind. Only 1,608 of the 4,823 content elements carry that
   class - on Yukon, 22 paragraphs of 68 - so styling the class alone would
   justify a paragraph, leave the next ragged, and look like a mistake rather
   than a decision.

   hyphens is not optional here. German runs long compounds through a 65ch
   measure, and justified text without hyphenation opens rivers of white down
   the column - the exact thing that gives Blocksatz its bad name. The document
   is lang="de", so the browser has the right patterns to work from.

   hyphenate-limit-chars keeps it from breaking a six-letter word or leaving
   two characters stranded on a line. Chrome, Safari and Firefox all honour it;
   where it is unknown the text is merely hyphenated a little more eagerly. */
/* Hyphenation is on at every width - it improves ragged text too, by evening
   out the right edge. Justification is not; see the media query below. */
.prose--story .elem p,
/* "Zur Person" auf der Profilseite - Rolf, 2026-08-21. Dieselbe Regel wie in
   den Reportagen, also auch dieselbe Breitengrenze: erst ab 48rem. */
.profil-main p,
.text-justify {
  hyphens: auto;
  -webkit-hyphens: auto;
  hyphenate-limit-chars: 7 4 3;
  /* a URL or a Rechtschreibungsungetüm that still will not fit must break
     rather than push the column sideways */
  overflow-wrap: break-word;
}

/* Justified only where the column is wide enough to carry it.
   The measure is 68ch, but on a phone the column is whatever the screen is -
   around 35 characters. Justifying 35 characters of German means three or four
   words sharing all the slack on the line, and no amount of hyphenation saves
   that; it is the single most common way Blocksatz on the web ends up looking
   worse than ragged right. Newspapers justify columns that narrow only because
   a typesetter is adjusting the letter spacing by hand.

   48rem is where the text column stops being screen-width and starts being a
   measure. Below it, ragged. */
@media (min-width: 48rem) {
  .prose--story .elem p,
  .profil-main p,
  .text-justify { text-align: justify; }
}

/* Not everything in .elem is body copy. A caption is short enough that
   justifying it produces one gappy line, and the summary column is far too
   narrow for it to work at all. */
.prose--story .elem figcaption,
.prose--story .elem .card__meta,
.storyfacts p,
.storyfacts li {
  text-align: left;
  hyphens: manual;
}

/* There was a JavaScript probe here that measured whether the browser could
   hyphenate German and dropped back to ragged right when it could not. It has
   been removed. At the 68ch measure this site uses, justification without
   hyphenation is merely a little loose - not the river-riddled mess it becomes
   in a narrow column - and the narrow case is already handled by the 48rem
   breakpoint above. Silently overriding an explicit preference of the site's
   owner to guard against "a little loose" was the wrong trade. */

/* ------------------------------------------------------------------ links --
   634 addresses grouped by journey, with three views over them. */

/* The intro block is shared with the front page, which centres it - that suits
   a two-line headline and reads poorly over two full paragraphs. Keep the block
   centred, set the prose left. */
.links__lead { max-width: var(--measure); margin-inline: auto; text-align: left; }
.links__filter {
  display: flex; flex-wrap: wrap; gap: .5rem;
  margin: clamp(1.2rem, 3vw, 2rem) 0 1.5rem;
}
.links__filter button {
  font: inherit; font-size: .9rem; cursor: pointer;
  padding: .45rem 1rem; border-radius: 999px;
  border: 1px solid var(--rule); background: transparent; color: var(--ink-soft);
}
.links__filter button:hover { border-color: var(--accent); color: var(--accent); }
.links__filter button.is-on {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.links__n { font-variant-numeric: tabular-nums; opacity: .75; margin-left: .15rem; }

.links__group { margin-bottom: clamp(1.5rem, 4vw, 2.5rem); }
.links__group[hidden] { display: none; }
.links__group-title {
  font-size: clamp(1rem, 2.2vw, 1.2rem); margin: 0 0 .6rem;
  padding-bottom: .3rem; border-bottom: 1px solid var(--rule);
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
}
.links__year { font-size: .8rem; font-weight: 400; color: var(--muted); }

.links__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: .35rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
}
/* min-width: 0 on both, because a flex item refuses to shrink below its own
   min-content and a domain has no break opportunity in it.
   www.destinationmontreuilloisencotedopale.com is 410px of unbreakable text; it
   pushed the whole document to 430px inside a 390px phone and the page scrolled
   sideways. overflow-wrap: anywhere lets it break mid-word - ugly on a domain,
   but less ugly than a page that slides. */
.links__item { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; min-width: 0; }
.links__item[hidden] { display: none; }
.links__item a { text-decoration: none; display: inline-flex; align-items: baseline; gap: .45rem; flex-wrap: wrap; min-width: 0; }
.links__title { text-decoration: underline; text-underline-offset: .18em; overflow-wrap: anywhere; }
.links__host { font-size: .78rem; color: var(--muted); }

/* A link that still works and points back is the useful one - it gets the
   only colour on the page. */
.links__badge {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
  padding: .1rem .45rem; border-radius: 999px;
  background: var(--accent); color: #fff; white-space: nowrap;
}

/* Dead links stay readable rather than being struck through - Rolf keeps them
   because they are part of the trip's record, not as an error list. */
.links__item.is-gone .links__title { text-decoration: none; color: var(--muted); }
.links__gone { font-size: .72rem; color: var(--muted); font-style: italic; }

.links__checked { margin-top: 2rem; font-size: .82rem; color: var(--muted); }
.links__empty { color: var(--muted); }
.links__pending {
  margin: clamp(1.2rem, 3vw, 2rem) 0 1.5rem; font-size: .9rem; color: var(--muted);
}
/* ------------------------------------------------ Reisefotos, gruppiert --
   Continent, country, albums. Two levels of heading over the same card grid
   the reportage index uses, so nothing about a card changes - only where it
   sits and what stands above it.

   The continent rule is the loud one and the country heading the quiet one:
   there are six continents at most and up to 43 countries, so if the country
   headings shouted the page would be a list of headings with pictures between
   them, which is the fault this replaces from the other side. */
.alben__kontinent + .alben__kontinent { margin-top: clamp(2.5rem, 6vw, 4rem); }
.alben__kontinent[hidden], .alben__land[hidden] { display: none; }

.alben__kontinent-name {
  font-size: clamp(1.4rem, 3.2vw, 1.9rem); line-height: 1.15;
  margin: 0 0 1.2rem; padding-bottom: .5rem;
  border-bottom: 2px solid var(--accent);
  letter-spacing: .01em;
}

.alben__land { margin-bottom: clamp(1.5rem, 4vw, 2.2rem); }
.alben__land-name {
  font-size: .82rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted);
  margin: 0 0 .7rem;
}

/* The third level, where one exists: the 25 Hessian districts under
   Deutschland. Quieter again than the country, and set in the ink colour
   rather than uppercase, so the eye reads continent > country > region as
   three steps down rather than three shouts. */
.alben__region + .alben__region { margin-top: 1.4rem; }
.alben__region[hidden] { display: none; }
.alben__region-name {
  font-size: .95rem; font-weight: 600; color: var(--ink);
  margin: 0 0 .6rem; padding-left: .1rem;
}

/* The tree is not itself a grid - each region holds its own .cards grid, so a
   country with two albums gets two cards side by side rather than two cards
   stretched across the full width.

   Smaller cards than the reportage index, 210px against 270px. Eleven of the
   fifteen countries hold one or two albums, so at the old size the page was a
   column of headings each followed by a single card and a lot of white; and
   Deutschland's 43 need to be scannable in a screen or two, not eleven. The
   album name and a photograph count is all a card carries now, so it does not
   need the width. */
.alben__tree { display: block; }
.alben__region .cards {
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(210px, 100%), 1fr));
}
.alben__region .card__title { font-size: 1rem; }
.alben__region .card__facts { font-size: .8rem; }

.alben__note {
  margin-top: clamp(1.5rem, 4vw, 2.5rem); padding-top: 1rem;
  border-top: 1px solid var(--rule); font-size: .85rem; color: var(--muted);
}

/* ------------------------------------------------- the lead paragraph --
   41 of the 104 reportages open with a paragraph wholly in <strong> - 53
   content elements in all. That is not a mistake: setting the Vorspann bold
   is the German newspaper convention, and Rolf was reproducing what his pieces
   looked like in print.

   On screen it does not carry: twelve lines of bold is heavy, and the denser
   letterforms make the justification gaps more conspicuous. So the signal is
   kept and the rendering changed - slightly larger, a touch darker, normal
   weight, which is the modern form of exactly the same convention.

   Done in CSS rather than by stripping <strong> from 53 elements, because the
   markup is what says "this paragraph is the lead". Delete these four rules and
   the old appearance comes back. */
.prose--story .elem > p:first-child > strong:only-child,
.prose--story .elem > p:first-child > b:only-child {
  font-weight: 400;
  font-size: 1.06em;
  color: var(--ink);
}
/* a lead that also has a line break inside it is still one <strong> */
.prose--story .elem > p:first-child > strong:only-child br,
.prose--story .elem > p:first-child > b:only-child br { line-height: inherit; }

/* ------------------------------------------- search hits on the page --
   Marked after arriving from a search result. Yellow is the convention and
   worth keeping - a reader knows immediately that this is a search hit and
   not the author's emphasis, which is what <mark> means everywhere else. */
.hit {
  background: #ffe9a8;
  color: inherit;
  border-radius: .15em;
  padding: 0 .08em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.hit.is-first {
  background: #ffd24d;
  box-shadow: 0 0 0 3px #ffd24d;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hit { background: #7a5c00; color: #fff; }
  :root:not([data-theme="light"]) .hit.is-first { background: #a87c00; box-shadow: 0 0 0 3px #a87c00; }
}
:root[data-theme="dark"] .hit { background: #7a5c00; color: #fff; }
:root[data-theme="dark"] .hit.is-first { background: #a87c00; box-shadow: 0 0 0 3px #a87c00; }

/* The one picture a search result pointed at. A ring rather than a tint, so it
   marks the photograph without altering how the photograph looks. */
.hit-img { position: relative; }
.hit-img img { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: var(--radius); }

/* ------------------------------------------- Volltext unter den Karten --
   Shown when someone reaches the index with ?q= - that is, typed a word and
   pressed Enter rather than picking from the dropdown. The card grid above
   matches only names and places; this is everything else the archive knows. */
.fulltext {
  margin-top: clamp(2rem, 5vw, 3rem);
  padding-top: clamp(1.2rem, 3vw, 2rem);
  border-top: 1px solid var(--rule);
}
.fulltext[hidden] { display: none; }
.fulltext__lead { margin: 0 0 1.2rem; color: var(--muted); font-size: .92rem; }
.fulltext__title {
  margin: 1.4rem 0 .5rem; font-size: .8rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; color: var(--muted);
}
.fulltext__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.fulltext__list a {
  display: block; text-decoration: none; color: inherit;
  border-left: 3px solid var(--rule); padding: .15rem 0 .15rem .8rem;
}
.fulltext__list a:hover { border-left-color: var(--accent); }
.fulltext__list strong { display: block; font-size: .95rem; }
.fulltext__snippet,
.fulltext__in { display: block; font-size: .85rem; color: var(--muted); line-height: 1.5; }
/* a search that genuinely found nothing says so, rather than showing a blank */
.fulltext__none { margin: 0; }
