/* ==========================================================================
   UI primitives — shared across sections.
   Layout wrappers, eyebrows, buttons, media frames, scroll reveals.
   ========================================================================== */

/* -------------------------------------------------------------- Layout -- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.container--narrow {
  max-width: var(--container-narrow);
}

.section {
  position: relative;
  padding-block: var(--section-y);
}

.section--deep {
  background: var(--c-cream-deep);
  /*
     The band is darker than the page, so muted text needs a shade more
     depth to hold 4.5:1. Same warm grey family, scoped to this surface.
  */
  --c-ink-muted: #6e6063;
}

/* For sections carrying an extra block — the intro with its map. */
.section--roomy {
  padding-block: var(--section-y-roomy);
}

/*
   Die Nahtmodifikatoren stehen hinter `--roomy`, weil sie es verfeinern
   dürfen: `--roomy` setzt die Kurzform `padding-block` und würde eine
   davor notierte einzelne Kante wieder überschreiben. So lässt sich ein
   luftiger Abschnitt oben weit und unten eng führen — siehe den Einstieg
   auf /wohnung/, der unmittelbar in die Ausstattung übergeht.
*/
.section--flush-top {
  padding-block-start: 0;
}

.section--tight-top {
  padding-block-start: var(--section-y-tight);
}

.section--tight-bottom {
  padding-block-end: var(--section-y-tight);
}

/* ------------------------------------------------------------ Headings -- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  color: var(--c-ink-muted);
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: var(--ls-eyebrow);
  /* Not 1 — cap height plus tracking needs a touch of room. */
  line-height: 1.3;
  text-transform: uppercase;
}

.eyebrow::before {
  content: '';
  width: 1.75rem;
  height: 1px;
  background: currentColor;
  opacity: 0.5;
}

.eyebrow--plain::before {
  display: none;
}

.section-title {
  font-size: var(--fs-section);
  margin-block-start: var(--sp-5);
}

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-6) var(--sp-8);
}

.section-head--center {
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/*
   No max-width here: headlines are pre-broken into masked lines, and
   .reveal-line clips overflow — constraining the width would cut glyphs off.
*/
.section-head__main {
  min-width: 0;
}

.section-head__aside {
  padding-block-end: 0.5rem;
  color: var(--c-ink-muted);
  font-size: var(--fs-meta);
}

/*
   Auf schmalen Fenstern stapelt sich der Abschnittskopf und rückt nach links.
   Nicht die zentrierte Fassung: die steht weiter oben mit derselben
   Spezifität, und diese Regel kommt danach — sie gewänne also und zöge die
   Augenbraue einer zentrierten Überschrift an den linken Rand, während der
   Text darunter mittig bliebe. Siehe .section-head--center.

   Bei 900 px und nicht bei 800: das ist die Stufe, auf der die Raster der
   Abschnitte von zwölf Spalten auf zwei Spuren wechseln (siehe die
   Bruchstellen in styles/tokens.css). Zwischen 800 und 900 stand die
   Beistellzeile sonst noch neben einer Überschrift, deren Abschnitt darunter
   längst umgebrochen war.
*/
@media (max-width: 900px) {
  .section-head:not(.section-head--center) {
    flex-direction: column;
    align-items: flex-start;
  }

  /*
     Das Polster richtete die Beistellzeile auf die Grundlinie der letzten
     Headline-Zeile aus. Untereinander gibt es keine gemeinsame Grundlinie
     mehr — es bliebe ein halber Zeilenabstand zu viel unter der Zeile.
  */
  .section-head:not(.section-head--center) .section-head__aside {
    padding-block-end: 0;
  }
}

.lead {
  font-size: var(--fs-lead);
  font-weight: 300;
  line-height: 1.62;
  color: var(--c-ink-soft);
}

/* Accent word inside a display heading */
.accent-italic {
  font-style: italic;
  color: var(--c-accent);
}

/* ------------------------------------------------------------- Buttons -- */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.75em;
  padding: 0.95rem 2.1rem;
  border-radius: var(--r-pill);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.02em;
  /* Buttons clip their hover wipe, so leading must clear every descender. */
  line-height: 1.25;
  white-space: nowrap;
  isolation: isolate;
  overflow: hidden;
  transition: transform var(--dur-hover) var(--ease-out-soft),
    box-shadow var(--dur-hover) var(--ease-out-soft),
    color var(--dur-hover) var(--ease-out-soft),
    border-color var(--dur-hover) var(--ease-out-soft);
}

/* Sienna wash that wipes upward on hover */
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: translateY(101%);
  transition: transform var(--dur-base) var(--ease-out);
}

.btn:hover {
  transform: translateY(-2px);
}

.btn:active {
  transform: translateY(0);
  transition-duration: 140ms;
}

.btn:hover::before {
  transform: translateY(0);
}

.btn__arrow {
  display: inline-block;
  transition: transform var(--dur-base) var(--ease-out);
}

.btn:hover .btn__arrow {
  transform: translateX(4px);
}

/* Solid accent */
.btn--accent {
  background: var(--c-accent);
  color: var(--c-accent-on);
  box-shadow: var(--shadow-accent);
}

.btn--accent::before {
  background: var(--c-accent-deep);
}

.btn--accent:hover {
  box-shadow: 0 18px 40px -14px rgba(201, 35, 81, 0.55);
}

/* Solid ink */
.btn--ink {
  background: var(--c-ink);
  color: var(--c-cream);
  box-shadow: var(--shadow-sm);
}

.btn--ink::before {
  background: var(--c-accent);
}

/* Outline on light */
.btn--ghost {
  border: 1px solid var(--c-line-strong);
  color: var(--c-ink);
}

.btn--ghost::before {
  background: var(--c-ink);
}

.btn--ghost:hover {
  color: var(--c-cream);
  border-color: var(--c-ink);
}

/* Outline on imagery */
.btn--glass {
  border: 1px solid rgba(255, 255, 255, 0.55);
  color: #fff;
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(10px) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
}

.btn--glass::before {
  background: var(--c-cream);
}

.btn--glass:hover {
  color: var(--c-ink);
  border-color: var(--c-cream);
}

.btn--block {
  width: 100%;
}

.btn--sm {
  padding: 0.68rem 1.4rem;
  font-size: var(--fs-meta);
}

.btn--lg {
  padding: 1.15rem 2.4rem;
  font-size: 1rem;
}

/*
   Auf schmalen Telefonen reicht die Zeile für die längste Beschriftung nicht
   mehr: "Weiter zum Wunschzeitraum" braucht rund 273 px, der Knopf bekommt in
   der Anfragekarte eines 320-px-Schirms aber nur 228 px. `overflow: hidden`
   oben trägt die Hover-Blende und kann nicht weg — der Knopf schnitt seine
   eigene Beschriftung deshalb wortlos ab, auf jedem Gerät unter rund 400 px.

   Zwei Zeilen sind besser als ein halbes Wort. Nur hier gelöst und nicht
   generell, weil die Pillenform auf breiteren Schirmen einzeilig bleiben soll.
*/
@media (max-width: 26rem) {
  .btn {
    white-space: normal;
    text-align: center;
  }
}

/* Quiet text link with an underline that grows from the left */
.link-underline {
  position: relative;
  display: inline-block;
  padding-block-end: 3px;
  color: var(--c-ink);
  font-size: var(--fs-small);
  font-weight: 500;
}

.link-underline::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform-origin: left;
  transform: scaleX(1);
  transition: transform var(--dur-base) var(--ease-in-out);
}

.link-underline:hover::after {
  transform-origin: right;
  animation: link-wipe var(--dur-base) var(--ease-in-out);
}

@keyframes link-wipe {
  0% {
    transform: scaleX(1);
    transform-origin: right;
  }
  49% {
    transform: scaleX(0);
    transform-origin: right;
  }
  50% {
    transform: scaleX(0);
    transform-origin: left;
  }
  100% {
    transform: scaleX(1);
    transform-origin: left;
  }
}

/* ----------------------------------------------------------- Hint dot -- */
/*
   Der pulsende Punkt vor einem Bedienhinweis ("Unterkunft auswählen für
   Details" auf der Startseite, "Bild auswählen für die große Ansicht" auf
   den Unterseiten).

   Steht hier und nicht in einem der beiden Abschnitts-Stylesheets: die
   beiden liegen nicht auf denselben Seiten, und ein Punkt, der je nach
   Seite pulst oder stillsteht, wäre kein Hinweis, sondern ein Zufall.
*/

.hint-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-accent);
  animation: hint-pulse 2.8s var(--ease-in-out) infinite;
}

@keyframes hint-pulse {
  0%,
  100% {
    opacity: 0.35;
    transform: scale(0.8);
  }
  50% {
    opacity: 1;
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hint-dot {
    animation: none;
  }
}

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

.card {
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: var(--r-xl);
  background: var(--c-cream-raised);
  box-shadow: var(--shadow-md);
}

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

.media {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-xl);
  background: var(--c-sand);
}

.media > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Slow zoom-in on hover for interactive imagery */
.media--zoom > img {
  transition: transform var(--dur-slower) var(--ease-out);
}

.media--zoom:hover > img {
  transform: scale(1.045);
}

/* Over-sized inner layer that drifts against the scroll */
.media--parallax .media__layer {
  position: absolute;
  inset: -7% 0;
  transform: translate3d(0, var(--parallax-y, 0%), 0);
}

.media--parallax .media__layer img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ------------------------------------------------------ Scroll reveals -- */
/*
   Elements opt in with [data-reveal]. The observer in js/lib/reveal.js adds
   .is-revealed once they enter the viewport. Stagger is driven by the
   --reveal-delay custom property so markup stays declarative.
*/

[data-reveal] {
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

[data-reveal='fade'] {
  transform: none;
}

[data-reveal='up'] {
  transform: translate3d(0, var(--reveal-shift), 0);
}

[data-reveal='up-lg'] {
  transform: translate3d(0, calc(var(--reveal-shift) * 2.2), 0);
}

[data-reveal='scale'] {
  transform: scale(0.965);
}

/* Enters from off to the left. Pairs with data-reveal-repeat so the element
   travels back out the same way when it leaves the viewport. */
[data-reveal='left'] {
  transform: translate3d(calc(var(--reveal-shift-x) * -1), 0, 0);
}

/*
   Image frames unmask upward while easing out of a slight scale.
   The rounded clip box belongs to THIS variant only — it is applied to
   picture frames, never to copy. A rounded clip over a text block shaves
   glyphs off the corners, and the box is invisible, so the text simply
   looks broken.
*/
[data-reveal='mask'] {
  clip-path: inset(14% 0 0 0 round var(--r-xl));
  transform: scale(1.03);
  transition: opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out),
    clip-path var(--dur-slower) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

[data-reveal='mask'].is-revealed {
  clip-path: inset(0 0 0 0 round var(--r-xl));
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* Line-by-line masked text reveal (hero + section headlines) */
/*
   The mask clips on every side, so it is inset by the slack it needs and
   pulled back out again with negative margins — the line keeps its exact
   typographic position while no glyph is ever shaved off.
*/
.reveal-line {
  display: block;
  overflow: hidden;
  padding: var(--mask-pad-top) 0.1em var(--mask-pad-bottom);
  margin: calc(var(--mask-pad-top) * -1) -0.1em
    calc(var(--mask-pad-bottom) * -1);
}

.reveal-line > span {
  display: block;
  /* Travels past the extra footroom so nothing peeks before it animates. */
  transform: translate3d(0, var(--mask-travel), 0);
  transition: transform var(--dur-slower) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform;
}

.is-revealed .reveal-line > span,
.reveal-line.is-revealed > span {
  transform: translate3d(0, 0, 0);
}

/* ---------------------------------------------------------------- Rose -- */
/*
   The house's one ornament: the bloom cut out of the hand-painted door sign.
   The source is 88 px wide, so it is never shown much above that — beyond it
   the brush strokes go soft.
*/

.rose {
  width: 1.4rem;
  height: auto;
}

/* A hairline that carries a rose at its centre. */
.rose-rule {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 1.9rem);
}

.rose-rule__line {
  flex: 1;
  height: 1px;
  background: var(--c-line);
}

.rose-rule__mark {
  flex: none;
  width: 1.35rem;
}

/* A hairline that draws itself across the section */
.rule {
  height: 1px;
  border: 0;
  background: var(--c-line);
}

[data-reveal='rule'] {
  opacity: 1;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-cinematic) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

[data-reveal='rule'].is-revealed {
  transform: scaleX(1);
}
