/*
 * obhut drafts: motion. Every animation, transition and keyframe of the site lives here, in the
 * order a reader meets it; values come from the motion block in tokens.css. Linked after site.css.
 *
 * Principles
 * 1. One entrance and two sequences, in reading order: the first hero arrives in light (F11:
 *    the night veil lifts, the hut's window lights, the words arrive without moving; see section
 *    1), then the request travels through the #warum figure, and the plan is drawn above the line
 *    of today. The hero's fog and the dawn's daybreak frame them; everything else is feedback.
 *    Stillness is part of the design after the entrance: no section reveals, no text moving with
 *    the scroll, and no content that only appears on scroll: every sentence is readable from its
 *    paint on; the sequences draw lines, they do not reveal words.
 * 2. Scroll-driven motion only where the scroll carries the meaning. It is linear (the scroll
 *    scrubs it 1:1), runs on one timeline per subject with fill none, and a hold keyframe from 0 %
 *    sets the pre-state while the subject is still off screen. Outside the range, in print, in
 *    full-page captures and without support the finished state shows.
 * 3. Scroll timelines animate only scale, translate, transform or opacity: clip-path or colour on
 *    one repaints the whole document on every scroll frame. A colour change is an opacity overlay.
 * 4. Time-based motion eases out and exits faster than it enters; nothing bounces or eases in. UI
 *    stays under 300 ms (press 140, hover 160, enter 220, drawer 280, exit 180); imagery fades over
 *    600 ms. Surfaces scale in from 0.96 or above, from where the motion comes from; a mark inside
 *    a control (the check) grows from 0.6.
 * 5. Base styles are the finished, readable state. Motion lives only in keyframes, @starting-style
 *    and the page script's WAAPI fades; without script or scroll timelines the page is complete.
 * 6. Reduced motion means no movement: no scale, translate, drift or draw. Opacity that carries
 *    meaning stays (image fades, the sheet fade, error fades, the daybreak, the hero's light
 *    entrance); every press also answers in colour (site.css). The hero's words then stand from
 *    the first paint. A time-based loop over 5 s runs only while its pause control is live, and
 *    rests while it is out of view: today only the fog.
 * 7. Keyboard-initiated and high-frequency changes never animate: focus rings, field focus, anchor
 *    jumps, font switches. Nothing moves as if in operation: no traffic on a planned path.
 * 8. New will-change only on a continuously animated element.
 */

/* =========================================================================
   Controls: feedback on hover and press (every section)
   ========================================================================= */

a {
  transition: text-decoration-color var(--duration-hover) ease;
}

.button {
  transition:
    transform var(--duration-press) var(--ease-out),
    background-color var(--duration-hover) ease,
    border-color var(--duration-hover) ease,
    color var(--duration-hover) ease;
}

/* .is-active: the static pressed state (component sheet); its tonal half is in site.css. */
@media (prefers-reduced-motion: no-preference) {
  .button:is(:active, .is-active):not(:disabled, [aria-disabled='true']) {
    transform: scale(var(--scale-press));
  }
}

.text-link {
  transition: text-decoration-color var(--duration-hover) ease;
}

.text-link .icon {
  transition: transform var(--duration-hover) var(--ease-out);
}

/* The arrow leans 2px towards where the link goes; fine pointers only. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .text-link:is(:hover, .is-hover) .icon {
    transform: translateX(2px);
  }
}

/* =========================================================================
   Nav: the bar and the phone sheet
   ========================================================================= */

.nav-link {
  transition:
    color var(--duration-hover) ease,
    text-decoration-color var(--duration-hover) ease;
}

.nav-toggle {
  transition:
    background-color var(--duration-hover) ease,
    transform var(--duration-press) var(--ease-out);
}

@media (prefers-reduced-motion: no-preference) {
  .nav-toggle:active {
    transform: scale(var(--scale-press));
  }
}

/*
 * Mobile sheet: native popover, full screen. It opens out of the bar (clip wipe, --ease-drawer)
 * and retracts into it, faster; the list and the pill travel 8px. Its head matches the bar, so the
 * × replaces the menu icon in place. Reduced motion keeps the opacity fade. Transitions retarget
 * from the current value, so an Escape mid-open reverses from where the sheet is.
 */
.nav-sheet {
  transition:
    opacity var(--duration-enter) var(--ease-out),
    display var(--duration-enter) allow-discrete,
    overlay var(--duration-enter) allow-discrete;
}

.nav-sheet:not(:popover-open) {
  opacity: 0;
  transition-duration: var(--duration-exit);
}

@starting-style {
  .nav-sheet:popover-open {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .nav-sheet {
    clip-path: inset(0);
    transition:
      clip-path var(--duration-drawer) var(--ease-drawer),
      display var(--duration-drawer) allow-discrete,
      overlay var(--duration-drawer) allow-discrete;
  }
  .nav-sheet:not(:popover-open) {
    opacity: 1;
    clip-path: inset(0 0 calc(100% - var(--nav-height)) 0);
    transition-duration: var(--duration-exit);
    transition-timing-function: var(--ease-out);
  }
  @starting-style {
    .nav-sheet:popover-open {
      opacity: 1;
      clip-path: inset(0 0 calc(100% - var(--nav-height)) 0);
    }
  }
  .nav-sheet :is(.nav-sheet__list, .nav-sheet__cta) {
    transition: transform var(--duration-enter) var(--ease-out);
  }
  @starting-style {
    .nav-sheet:popover-open :is(.nav-sheet__list, .nav-sheet__cta) {
      transform: translateY(calc(-1 * var(--travel-md)));
    }
  }
}

.nav-sheet__list a {
  transition: background-color var(--duration-hover) ease;
}

/* Press instant, release fades. */
.nav-sheet__list a:active {
  transition-duration: 0s;
}

/* =========================================================================
   1. Hero: the arrival in light, then the fog drifts, the pause control answers
   Late scenes and images fade up in the page script (600 ms, --ease-image, opacity only).
   ========================================================================= */

/*
 * The hero entrance (F11, 2026-09-20, Licht and Titel): the night veil lifts from the first
 * paint over 1.8 s (the dawn's ::before at the other end of the page is its twin), the hut's
 * window lights at 1.1 s, and the words arrive in reading order: the eyebrow now, the wordplay
 * on its own beat at 300 ms, lead and action tightly after; everything is fully visible at
 * 980 ms. The words never move. Text is off under reduced motion (then it stands from the first
 * paint); veil, pane and window are opacity only and stay, they carry the night-to-lit turn.
 * Pure CSS: the base states are the finished scene (site.css); without support, in print and
 * after the run the hero stands.
 */
.hero-photo--fold::before {
  animation: fold-veil 1800ms var(--ease-image);
}

.hero-photo--fold .hero-photo__pane {
  animation: fold-pane 900ms var(--ease-image) 1100ms backwards;
}

.hero-photo--fold .hero-photo__window {
  animation: fold-window 900ms var(--ease-image) 1100ms backwards;
}

@media (prefers-reduced-motion: no-preference) {
  .hero-photo--fold :is(.eyebrow, .hero-title__line, .lead, .cluster) {
    animation: fold-title 500ms var(--ease-image) backwards;
  }
  .hero-photo--fold .hero-title__line:first-of-type {
    animation-delay: 60ms;
  }
  /* The wordplay lands on its own beat. */
  .hero-photo--fold .hero-title__line:last-of-type {
    animation-delay: 300ms;
  }
  .hero-photo--fold .lead {
    animation-delay: 420ms;
  }
  .hero-photo--fold .cluster {
    animation-delay: 480ms;
  }
}

@keyframes fold-veil {
  from {
    opacity: 0.85;
  }
}
@keyframes fold-pane {
  from {
    opacity: 1;
  }
}
@keyframes fold-window {
  from {
    opacity: 0;
  }
}
@keyframes fold-title {
  from {
    opacity: 0;
  }
}

/*
 * The fog drifts only while its pause control is live (the page script reveals it): without script
 * it stands, like the rest of the finished page. One loop is one painting width: 140 s for the
 * landscape, 63 s for the portrait painting (1080 of 2400 at the same scale), so the same wind
 * crosses the same valley on every screen. Longhands: the shorthand would reset the play state
 * that the pause rule below sets with a lower specificity.
 */
@media (prefers-reduced-motion: no-preference) {
  .hero-photo:has(> .hero-photo__pause:not([hidden])) .hero-photo__fog {
    animation-name: fog-drift;
    animation-duration: 140s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    will-change: transform;
  }
  @media (max-width: 767.98px) {
    .hero-photo:has(> .hero-photo__pause:not([hidden])) .hero-photo__fog {
      animation-duration: 63s;
    }
  }
}

/* One period of the two-period tile: the loop has no seam. */
@keyframes fog-drift {
  to {
    transform: translateX(-50%);
  }
}

/*
 * The pause control stops the drift instantly (WCAG 2.2.2); it is display none under reduced
 * motion. Out of view (.is-away, page script) the drift rests, so a long read costs no frames; a
 * paused animation keeps its place, so it resumes without a jump.
 */
.hero-photo:is(.is-still, .is-away) .hero-photo__fog {
  animation-play-state: paused;
}

.hero-photo__pause {
  transition:
    color var(--duration-hover) ease,
    transform var(--duration-press) var(--ease-out);
}

.hero-photo__pause:active {
  transform: scale(var(--scale-press));
}

/* =========================================================================
   2. Warum: the request travels (the focal sequence)
   ========================================================================= */

/*
 * One scroll timeline on the figure; keyframe offsets are its cover progress, set in the keyframes
 * because keyframe selectors cannot take custom properties. Leg 1 draws 18→32 %; arriving, it sets
 * „Hier lesbar“ (31→34 %) and flips the law cell to ink (at 32 %); a beat, then leg 2 draws
 * 36→50 %, so the figure is complete when it stands centred, where a reader stops. Each leg label
 * fades in as the line reaches it: mid-leg from 768, near the leg's end on phones, where the path
 * runs down past the facts. fill none with a hold from 0 %: outside the range, in print, in
 * full-page captures and without support the finished figure stands.
 * --route-from is the line's collapsed scale: down on phones (1 0), left to right from 768 (0 1).
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .route--draw {
      --route-from: 1 0;
      view-timeline: --route block;
    }
    .route--draw .route__station::after {
      transform-origin: 50% 0;
    }
    /* The `animation` shorthand resets the timeline, so animation-timeline always follows it. */
    .route--draw .route__station:nth-child(1)::after {
      animation: route-leg-1 auto linear none;
      animation-timeline: --route;
    }
    .route--draw .route__station:nth-child(2)::after {
      animation: route-leg-2 auto linear none;
      animation-timeline: --route;
    }
    .route--draw .route__station:nth-child(1) .route__leg {
      animation: route-label-1-down auto linear none;
      animation-timeline: --route;
    }
    .route--draw .route__station:nth-child(2) .route__leg {
      animation: route-label-2-down auto linear none;
      animation-timeline: --route;
    }
    .route--draw .route__marker {
      animation: route-arrive auto linear none;
      animation-timeline: --route;
    }
    /*
     * Before the arrival a white difference layer turns the ink cell into the tonal step (about
     * 10:1). It is square on purpose: inside the isolated flag its corners blend with nothing and
     * stay paper, while a rounded layer would leave the ink's anti-aliased corners showing.
     */
    .route--draw .route__flag {
      position: relative;
      isolation: isolate;
    }
    .route--draw .route__flag::after {
      content: '';
      position: absolute;
      inset: 0;
      background: var(--color-on-primary);
      mix-blend-mode: difference;
      pointer-events: none;
      opacity: 0;
      animation: route-flip auto linear none;
      animation-timeline: --route;
    }
    @media (min-width: 768px) {
      .route--draw {
        --route-from: 0 1;
      }
      .route--draw .route__station::after {
        transform-origin: 0 50%;
      }
      .route--draw .route__station:nth-child(1) .route__leg {
        animation-name: route-label-1;
      }
      .route--draw .route__station:nth-child(2) .route__leg {
        animation-name: route-label-2;
      }
    }
  }
}

@keyframes route-leg-1 {
  0%,
  18% {
    scale: var(--route-from);
  }
  32% {
    scale: 1;
  }
}

@keyframes route-leg-2 {
  0%,
  36% {
    scale: var(--route-from);
  }
  50% {
    scale: 1;
  }
}

/* From 768 the labels sit mid-leg. */
@keyframes route-label-1 {
  0%,
  23% {
    opacity: 0;
  }
  29% {
    opacity: 1;
  }
}

@keyframes route-label-2 {
  0%,
  41% {
    opacity: 0;
  }
  47% {
    opacity: 1;
  }
}

/* Phones: the labels sit at about 80 % and 90 % of their legs. */
@keyframes route-label-1-down {
  0%,
  27% {
    opacity: 0;
  }
  31% {
    opacity: 1;
  }
}

@keyframes route-label-2-down {
  0%,
  47% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
}

@keyframes route-arrive {
  0%,
  31% {
    opacity: 0;
  }
  34% {
    opacity: 1;
  }
}

/* Stepped on purpose: the text never passes through mid-grey on mid-grey. */
@keyframes route-flip {
  0% {
    opacity: 1;
    animation-timing-function: steps(1, end);
  }
  32% {
    opacity: 0;
  }
}

/* Forced colours: the flag keeps its system colours, so there is no overlay; the marker still fades. */
@media (forced-colors: active) {
  .route__flag::after {
    display: none;
  }
}

/* =========================================================================
   3. Plattform: the plan is drawn
   ========================================================================= */

/*
 * Each dashed rule of the plan draws left to right as it crosses the lower half of the screen:
 * a paper strip over the rule shrinks towards the right. The rules are drawn, the text never moves.
 * One view timeline per rule (the strip itself), fill none with a hold from 0 %: the base state is
 * the finished rule. The line of today, the rock, the six principles and the cut to white stand
 * still; foundations do not move.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .profile__plan .layer + .layer {
      position: relative;
    }
    .profile__plan .layer + .layer::before {
      content: '';
      position: absolute;
      inset: 0 0 auto;
      height: 1px;
      background: var(--color-canvas);
      transform-origin: 100% 50%;
      scale: 0 1;
      animation: plan-draw auto linear none;
      animation-timeline: view(block);
    }
  }
}

/* The strip's cover progress: it draws between 85 % and 55 % of the screen height. */
@keyframes plan-draw {
  0%,
  15% {
    scale: 1 1;
  }
  45% {
    scale: 0 1;
  }
}

/* Forced colours draw the rule as a border, which the strip would not cover: no strip. */
@media (forced-colors: active) {
  .profile__plan .layer + .layer::before {
    display: none;
  }
}

/* =========================================================================
   6. Ziel: daybreak
   ========================================================================= */

/*
 * The night veil holds while the dark sky enters, lifts as the lit peaks come in and is gone when
 * the band fills the screen. Opacity only, linear, fill none: the base state is the bright dawn.
 * Nothing moves, so it stays under reduced motion: it carries the night-to-dawn turn of the page.
 * The goal figure inside does not move: a planned path carries no traffic.
 */
@supports (animation-timeline: view()) {
  .hero-photo--dawn {
    view-timeline: --dawn block;
  }
  .hero-photo--dawn::before {
    animation: daybreak auto linear none;
    animation-timeline: --dawn;
    animation-range: entry 0% cover 50%;
  }
}

@keyframes daybreak {
  0%,
  35% {
    opacity: 0.7;
  }
  100% {
    opacity: 0;
  }
}

/* =========================================================================
   7. Kontakt: the form
   ========================================================================= */

/* Autofill keeps the paper field and its rule: the UA tint is postponed for a day, so it never arrives. */
.field__control:autofill {
  -webkit-text-fill-color: var(--color-ink);
  transition: background-color 0s 86400s;
}

/*
 * A message appears out of the rule it annotates, only when it first appears (display none → flex),
 * not when its wording changes. The live region announces at once; only the visual fades. The
 * fields below move by the message height instantly: height never animates. Field focus does not
 * animate (keyboard-frequent).
 */
.field__error {
  transition:
    opacity var(--duration-enter) var(--ease-out),
    translate var(--duration-enter) var(--ease-out);
}

@starting-style {
  .field__error:not(:empty) {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: no-preference) {
  @starting-style {
    .field__error:not(:empty) {
      translate: 0 calc(-1 * var(--travel-sm));
    }
  }
}

.check__input {
  transition:
    background-color var(--duration-hover) ease,
    border-color var(--duration-hover) ease;
}

/* The mark grows from 0.6 as the box fills; under reduced motion it only fades. */
.check__input::before {
  transition: opacity var(--duration-press) var(--ease-out);
}

@media (prefers-reduced-motion: no-preference) {
  .check__input::before {
    transform: scale(0.6);
    transition:
      opacity var(--duration-press) var(--ease-out),
      transform var(--duration-press) var(--ease-out);
  }
  .check__input:checked::before {
    transform: none;
  }
}

/* FAQ (component sheet): the plus turns and the answer opens by its height, the accordion exception. */
@media (prefers-reduced-motion: no-preference) {
  .faq-item > summary .icon-plus-bar {
    transition: transform var(--duration-enter) var(--ease-out);
  }
  .faq-item::details-content {
    transition:
      height var(--duration-enter) var(--ease-out),
      content-visibility var(--duration-enter) allow-discrete;
  }
}

/* =========================================================================
   Print: the finished page. Every animation's base state is its finished state.
   ========================================================================= */

@media print {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}
