/* RÖRELSEN PÅ GÄSTSIDAN
   Referensfilen (invito-gastsida.html, raderna 74 till 125) är specifikationen. Keyframes och intervall har
   exakt referensens värden, med prefixet ew- (tests/motion.test.js läser referensen och jämför).

   Tre sorters rörelse:
     SCROLL    scrollstyrt, som i referensen. Varje element har sin egen view-timeline, inledningen scroll(root).
               sections.js sätter data-rorelse med referensens namn (rise, rise2 till rise5, settle, settle-s,
               grow, wipe, read, read2, read3, readlight, dot, left, right, left-w, right-w). Orden i berättelsens
               rubrik (.ew-ord), programmets linje (.schedule::before) och inledningen väljs med klasser.
     INTRO     hero-etiketten och hero-titeln stiger in en gång när inbjudan laddats (body.is-ready).
     HANDLING  på tid, som svar på det gästen gör (tacket, bocken, öppnad fråga, ny gäst i listan).

   Villkor:
     1. SCROLL, den klistrade inledningen, den klistrade programrubriken och INTRO finns bara med stöd för
        animation-timeline och utan reducerad rörelse. Utan stöd står sidan still i slutläget och är läsbar.
     2. Reducerad rörelse: ingenting rör sig. Ingen scrollstyrd rörelse, ingen klistrad inledning, inget intro,
        ingen mjuk scroll, inga toningar vid handling (slutläget visas direkt) och inget scale vid tryck.
     3. Slutläget är alltid elementets vanliga stil i guest.css, så kontrasten är densamma som utan rörelse.
     4. Ett element nära sidans slut som aldrig kan scrollas till slutet av sitt intervall får klassen
        ew-framme (js/framme.js) och står då i slutläget. Samma klass får det som har tangentbordsfokus, så
        länge fokus är kvar där, så att fokusmarkeringen och texten gästen skriver alltid syns.
     5. Utskrift: inga animationer.

   Likvärdiga former (samma bild på skärmen som referensen):
     - Egenskaperna translate och scale i stället för transform, så att statisk transform i guest.css aldrig
       skrivs över. Ordningen blir densamma: translate före scale.
     - heroout: bilden skalas 1 till 1.25 som i referensen. I stället för filter: brightness(1 till .45) tonas ett
       svart lager ovanpå bilden från 0 till 0.55 (ew-heroout-lager), som ger samma färg (varje kanal gånger 0,45)
       utan filter, som är tungt på äldre telefoner.
     - settle, read och readlight: referensens fasta färger uttryckta mot temat med samma andel. settle från
       textfärgen 19 % (CFC7BB), read från textfärgen 22 % (C9C1B5), readlight från texten på stark yta 37 %
       (8F9A80) till full färg. Samma ljusgrå start i varje palett, också på mjuk yta och i mörka paletter.

   Så får en ny sektionstyp sin rörelse: bygg den med shell() i sections.js, så får etiketten och rubriken
   rörelsen. Sätt data-rorelse med ett av referensens namn på delarna, i referensens trappa. */

:root {
  /* Kurvor och tider för INTRO och HANDLING. Det scrollstyrda är linjärt, som i referensen. */
  --ew-kurva-ut: cubic-bezier(0.23, 1, 0.32, 1);
  --ew-kurva-glid: cubic-bezier(0.77, 0, 0.175, 1);
  --ew-tid-tryck: 160ms;
  --ew-tid-kort: 240ms;
  --ew-tid-intro: 640ms;
  --ew-steg-1: 6px;
  --ew-steg-2: 16px;
}

/* ---------- Tryckkänsla (scale tas bort vid reducerad rörelse längre ner) ---------- */

.btn,
.stepper-btn,
.choice-body {
  transition:
    transform var(--ew-tid-tryck) var(--ew-kurva-ut),
    background-color 150ms ease,
    border-color 150ms ease,
    color 150ms ease;
}

.btn:active:not(:disabled),
.stepper-btn:active:not(:disabled),
.choice:active .choice-body {
  transform: scale(0.97);
}

.faq-q {
  transition: background-color 150ms ease;
}

.faq-q:active {
  background-color: var(--tint);
}

.btn-submit .btn-label {
  transition: opacity 150ms ease;
}

.btn-submit.is-loading .btn-label {
  opacity: 0.7;
}

@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover:not(:disabled) {
    background: rgb(var(--primary) / 0.9);
  }

  .btn-ghost:hover:not(:disabled),
  .btn-outline:hover:not(:disabled) {
    background: var(--tint);
  }

  .choice:hover .choice-body {
    border-color: rgb(var(--primary) / 0.6);
  }

  .faq-q:hover {
    background-color: var(--tint);
  }

  .topnav a {
    transition: color 150ms ease;
  }

  .topnav a:hover {
    color: rgb(var(--ink));
  }
}

/* ================================================================================================
   SCROLL och INTRO: bara utan reducerad rörelse OCH med stöd för animation-timeline.
   ================================================================================================ */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    /* ---------- Sidled ----------
       left, right och settle skjuter ut element i sidled. clip klipper utan att skapa en scrollbehållare, så att
       klistrade element fungerar som vanligt. */
    .s,
    .footer {
      overflow-x: clip;
    }

    /* ---------- INTRO: hero-etiketten och hero-titeln, en gång vid laddning ---------- */

    body:not(.is-ready) :is(.hero-eyebrow, .hero-title) {
      opacity: 0;
    }

    .is-ready .hero-eyebrow,
    .is-ready .hero-title {
      --ew-steg: var(--ew-steg-2);
      animation: ew-stig var(--ew-tid-intro) var(--ew-kurva-ut) backwards;
    }

    .is-ready .hero-eyebrow + .hero-title {
      animation-delay: 60ms;
    }

    /* ---------- Inledningen: alla fem layouter ----------
       Bilden zoomar och mörknar under första skärmhöjden, texten tonas bort uppåt under första halva, som
       referensens .hero img och .hero-text. Ramen runt bilden klipper, så att zoomen stannar i den. */

    .s-hero .hero-media {
      overflow: hidden;
    }

    /* Sidoställd, centrerad och utan layout: bilden har rundade hörn, så ramen får samma. Delad och helskärm har
       redan en positionerad ram. */
    .s-hero:not(.hero-layout-helskarm):not(.hero-layout-delad) .hero-media {
      position: relative;
      border-radius: var(--radius);
    }

    /* Intervallet börjar där inledningen klistras fast, alltså när sidan scrollats förbi toppraden ovanför den.
       I referensen står inledningen still från första bildpunkten, eftersom den ligger överst på sidan. Här är
       --ew-hero-start avståndet från sidans topp till inledningen, och utan den skulle bilden och texten röra sig
       med sidan under toppradens höjd medan intervallet redan löpte.
       Både variabeln och klassen hero-rorelse sätts av sections.js, i den ordningen: Chrome löser intervallet när
       animationen skapas och räknar inte om det när variabeln ändras efteråt, så animationerna får inte finnas
       innan måttet är satt. Klassen bär därför animation-name, och sections.js lägger på den först efter mätningen.
       Utan JS finns ingen rörelse i inledningen, och den står i sitt utgångsläge som utan stöd för rörelsen. */
    .s-hero.hero-rorelse .hero-media img,
    .s-hero.hero-rorelse .hero-media::before {
      animation-timeline: scroll(root);
      animation-range: var(--ew-hero-start, 0px) calc(var(--ew-hero-start, 0px) + 100vh);
      animation-fill-mode: both;
      animation-timing-function: linear;
    }

    .s-hero.hero-rorelse .hero-media img {
      animation-name: ew-heroout;
    }

    /* Det svarta lagret som ersätter referensens brightness(.45). Lagret finns alltid, genomskinligt, och mörknar
       bara när rörelsen är påslagen. */
    .s-hero .hero-media::before {
      content: '';
      position: absolute;
      z-index: 1;
      inset: 0;
      background: #000;
      opacity: 0;
      pointer-events: none;
    }

    .s-hero.hero-rorelse .hero-media::before {
      animation-name: ew-heroout-lager;
    }

    .s-hero.hero-rorelse .hero-copy {
      animation-name: ew-fadeup;
      animation-timeline: scroll(root);
      animation-range: var(--ew-hero-start, 0px) calc(var(--ew-hero-start, 0px) + 55vh);
      animation-fill-mode: both;
      animation-timing-function: linear;
    }

    /* ---------- Klistrad inledning som innehållet glider över ----------
       Som referensens .hero{position:sticky;top:0}, men bara när inledningen ryms i fönstret (hero-ryms från
       sections.js). En högre inledning scrollar med sidan, annars kunde knapparna aldrig nås. */

    .s-hero.hero-ryms {
      position: sticky;
      top: 0;
      z-index: 0;
    }

    /* Innehållet efter inledningen täcker den: egen stapling och opak yta. Tonerna har redan egen yta. */
    .s-hero.hero-ryms ~ .s {
      position: relative;
      z-index: 1;
    }

    /* Med bakgrundsbild (has-backdrop) är sektionerna genomskinliga så att lagret syns, och inledningen
       är aldrig klistrad (sections.js hero). Då får de inte heller en opak yta. */
    html:not(.has-backdrop) .s-hero.hero-ryms ~ .s:not(.tone-soft):not(.tone-band) {
      background-color: rgb(var(--bg));
    }

    /* ---------- Klistrad programrubrik på mobil ----------
       Som referensens .schema .h{position:sticky;top:0;background;padding:24px 0 16px;z-index:2}. Negativa
       marginaler tar tillbaka luften, så att sektionen har samma höjd som utan rörelse. Från 960 px är huvudet
       redan klistrat i vänsterspalten (guest.css). */
    @media (max-width: 959px) {
      /* Med bakgrundsbild (has-backdrop) är sektionerna genomskinliga så att lagret syns. En klistrad
         rubrik måste ha en egen opak yta för att texten under ska kunna passera bakom den, och den
         ytan i --bg läses som en grå platta mitt i bilden, smalare än sidan. Rubriken är därför inte
         klistrad med bakgrundsbild, precis som inledningen inte är det. */
      html:not(.has-backdrop) .s-schedule .s-head {
        position: sticky;
        top: 0;
        z-index: 2;
        margin-top: -24px;
        margin-bottom: calc(clamp(28px, 5vw, 44px) - 16px);
        padding-top: 24px;
        padding-bottom: 16px;
        background-color: rgb(var(--bg));
      }
    }

    /* ---------- SCROLL: referensens tabell ---------- */

    [data-rorelse],
    .ew-ord,
    .schedule::before {
      animation-timeline: view();
      animation-fill-mode: both;
      animation-timing-function: linear;
    }

    [data-rorelse='settle'],
    [data-rorelse='settle-s'],
    [data-rorelse='grow'] {
      transform-origin: left top;
    }

    [data-rorelse='rise'] { animation-name: ew-rise; animation-range: entry 10% cover 30%; }
    [data-rorelse='rise2'] { animation-name: ew-rise; animation-range: entry 20% cover 36%; }
    [data-rorelse='rise3'] { animation-name: ew-rise; animation-range: entry 30% cover 42%; }
    [data-rorelse='rise4'] { animation-name: ew-rise; animation-range: entry 40% cover 48%; }
    [data-rorelse='rise5'] { animation-name: ew-rise; animation-range: entry 50% cover 54%; }
    [data-rorelse='settle'] { animation-name: ew-settle; animation-range: entry 20% cover 40%; }
    [data-rorelse='settle-s'] { animation-name: ew-settle; animation-range: entry 30% cover 40%; }
    [data-rorelse='grow'] { animation-name: ew-grow; animation-range: entry 20% cover 35%; }
    [data-rorelse='wipe'] { animation-name: ew-wipe; animation-range: entry 0% cover 25%; }
    [data-rorelse='read'] { animation-name: ew-read; animation-range: cover 10% cover 35%; }
    [data-rorelse='read2'] { animation-name: ew-read; animation-range: cover 25% cover 50%; }
    [data-rorelse='read3'] { animation-name: ew-read; animation-range: cover 40% cover 65%; }
    [data-rorelse='readlight'] { animation-name: ew-readlight; animation-range: cover 0% cover 45%; }
    .schedule::before { animation-name: ew-line; animation-range: entry 20% cover 60%; }
    [data-rorelse='dot'] { animation-name: ew-dotin; animation-range: entry 30% cover 30%; }
    [data-rorelse='left'] { animation-name: ew-left; animation-range: entry 20% cover 35%; }
    [data-rorelse='right'] { animation-name: ew-right; animation-range: entry 20% cover 38%; }
    [data-rorelse='left-w'] { animation-name: ew-left; animation-range: entry 10% cover 35%; }
    [data-rorelse='right-w'] { animation-name: ew-right; animation-range: entry 10% cover 35%; }

    /* Orden i berättelsens rubrik: ord n har entry (10 + 10n)% cover (26 + 4n)%, som referensens åtta ord.
       Efter åtta ord fortsätter mönstret med entry högst 100%, och ord 18 och senare har ord 18:s intervall. */
    .ew-ord { animation-name: ew-word; }
    .ew-ord:nth-child(1) { animation-range: entry 20% cover 30%; }
    .ew-ord:nth-child(2) { animation-range: entry 30% cover 34%; }
    .ew-ord:nth-child(3) { animation-range: entry 40% cover 38%; }
    .ew-ord:nth-child(4) { animation-range: entry 50% cover 42%; }
    .ew-ord:nth-child(5) { animation-range: entry 60% cover 46%; }
    .ew-ord:nth-child(6) { animation-range: entry 70% cover 50%; }
    .ew-ord:nth-child(7) { animation-range: entry 80% cover 54%; }
    .ew-ord:nth-child(8) { animation-range: entry 90% cover 58%; }
    .ew-ord:nth-child(9) { animation-range: entry 100% cover 62%; }
    .ew-ord:nth-child(10) { animation-range: entry 100% cover 66%; }
    .ew-ord:nth-child(11) { animation-range: entry 100% cover 70%; }
    .ew-ord:nth-child(12) { animation-range: entry 100% cover 74%; }
    .ew-ord:nth-child(13) { animation-range: entry 100% cover 78%; }
    .ew-ord:nth-child(14) { animation-range: entry 100% cover 82%; }
    .ew-ord:nth-child(15) { animation-range: entry 100% cover 86%; }
    .ew-ord:nth-child(16) { animation-range: entry 100% cover 90%; }
    .ew-ord:nth-child(17) { animation-range: entry 100% cover 94%; }
    .ew-ord:nth-child(n + 18) { animation-range: entry 100% cover 98%; }

    /* Slutet av sidan (js/framme.js): elementet når aldrig slutet av sitt intervall och står i slutläget.
       Samma klass sätts av framme.js på det som har tangentbordsfokus, så länge fokus är kvar där. */
    .ew-framme,
    .ew-framme::before {
      animation: none !important;
    }

    /* Ett element som står i slutläget ligger före de stigande grannarna i staplingen. Grannen ovanför stiger
       fortfarande underifrån, och i OSA är avståndet 28 px medan ew-rise flyttar 96 px: hälsningsfältet ritades
       då över Skicka svar när OSA var sista sektionen, knappen såg tudelad ut och ett tryck på dess övre del
       hamnade i textrutan. Nu glider fältet in bakom knappen i stället, och knappen tar hela sitt eget tryck.
       Rörelsen är oförändrad: bara ritordningen ändras, och bara i formuläret, där ett täckt element gör att
       gästen inte kan skicka. */
    .rsvp-form .ew-framme {
      position: relative;
      z-index: 1;
    }

    /* Rubriken skalas 1,5 från vänstra överkanten (settle, grow) och sticker ut åt höger. I en kolumn klipps den
       av sektionens overflow-x, men från 960 px står huvudet i vänsterspalten och rubriken lade sig då över
       bilderna och svarskorten i högerspalten. clip klipper vid spaltens kant utan att skapa en scrollbehållare,
       så det klistrade huvudet fungerar som förut. overflow-clip-margin ger 24 px att växa i utanför spalten,
       mindre än halva det minsta spaltavståndet (48 px), så att kanten blir mjukare utan att nå högerspalten.
       Måttet är en fast längd: clamp() ger 0 i Chrome. */
    @media (min-width: 960px) {
      .s-head {
        overflow-x: clip;
        overflow-clip-margin: 24px;
      }
    }
  }
}

/* ================================================================================================
   HANDLING: på tid, svar på det gästen gör. Bara utan reducerad rörelse. Ligger efter SCROLL och har högre
   specificitet, så att en handling vinner över scrollrörelsen på samma element (ny gäst i listan).
   ================================================================================================ */

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }

  /* OSA skickat: ring, bock, och sedan rubrik, text och knappar i trappa. Tacket får ingen scrollrörelse:
     rubriken, texten och knapparna har redan den här animationen, och två animationer av opacity och translate
     på samma element skulle krocka. */
  .rsvp-thanks .checkmark-ring {
    transform-origin: center;
    transform-box: fill-box;
    animation: ew-punkt 420ms var(--ew-kurva-ut) backwards;
  }

  .rsvp-thanks .checkmark-tick {
    stroke-dasharray: 34;
    animation: ew-bock 360ms var(--ew-kurva-ut) 160ms backwards;
  }

  .rsvp-thanks .thanks-title,
  .rsvp-thanks .thanks-text,
  .rsvp-thanks .thanks-actions {
    --ew-steg: var(--ew-steg-1);
    animation: ew-stig 380ms var(--ew-kurva-ut) 120ms backwards;
  }

  .rsvp-thanks .thanks-text { animation-delay: 170ms; }
  .rsvp-thanks .thanks-actions { animation-delay: 220ms; }

  /* Formulärbyte: "Svara för en person till" och "Ändra mitt svar". */
  .rsvp-card.is-swapped > .rsvp-form {
    --ew-steg: var(--ew-steg-1);
    animation: ew-stig 320ms var(--ew-kurva-ut) backwards;
  }

  /* "Jag kommer" valt: fälten för antal och mat glider ned på plats. */
  .reveal-group.is-revealed {
    --ew-steg: -4px;
    animation: ew-stig 260ms var(--ew-kurva-ut) backwards;
  }

  /* Önskelistan: formuläret för att reservera, och bocken med raden när det är reserverat. */
  .wish-form {
    --ew-steg: -4px;
    animation: ew-stig 200ms var(--ew-kurva-ut) backwards;
  }

  .wish.just-reserved .wish-check {
    animation: ew-punkt 360ms var(--ew-kurva-ut) backwards;
  }

  .wish.just-reserved .wish-reserved {
    --ew-steg: var(--ew-steg-1);
    animation: ew-stig var(--ew-tid-kort) var(--ew-kurva-ut) backwards;
  }

  /* Frågor: svaret glider ned, plustecknet vrids till ett kryss (vridningen sätts i guest.css). */
  .faq-icon {
    transition: transform 220ms var(--ew-kurva-glid);
  }

  .faq-item[open] .faq-a {
    --ew-steg: -6px;
    animation: ew-stig var(--ew-tid-kort) var(--ew-kurva-ut) backwards;
  }

  /* Kalendermenyn och integritetstexten. */
  .cal-menu[open] .cal-options,
  .privacy[open] .privacy-body {
    --ew-steg: var(--ew-steg-1);
    animation: ew-stig 180ms var(--ew-kurva-ut) backwards;
  }

  /* Gästlistan: den som nyss svarade glider in, initialen växer fram. Raden har annars rise2 till rise5, och
     handlingen vinner (högre specificitet än [data-rorelse]). */
  .guest.is-new {
    --ew-steg: var(--ew-steg-1);
    animation: ew-stig 420ms var(--ew-kurva-ut) backwards;
  }

  .guest.is-new .guest-avatar {
    animation: ew-punkt 420ms var(--ew-kurva-ut) 80ms backwards;
  }

  /* Swish kopierat. */
  .is-done .copy-icon {
    animation: ew-punkt 280ms var(--ew-kurva-ut) backwards;
  }
}

/* ================================================================================================
   Reducerad rörelse: ingenting rör sig. Blocken ovan gäller inte, så det finns inget scrollstyrt, ingen
   klistrad inledning, inget intro, ingen mjuk scroll och inga toningar. Kvar: knapptryck utan scale.
   ================================================================================================ */

@media (prefers-reduced-motion: reduce) {
  .btn:active:not(:disabled),
  .stepper-btn:active:not(:disabled),
  .choice:active .choice-body {
    transform: none;
  }

  /* Toningarna vid handling ligger utanför media-frågorna, eftersom de också ska finnas utan stöd för
     scrollstyrd rörelse. Här stängs de av: färgen på en knapp eller en fråga byts direkt, och etiketten på
     Skicka svar är dämpad från första bildrutan medan svaret skickas i stället för att tonas dit. */
  .btn,
  .stepper-btn,
  .choice-body,
  .faq-q,
  .faq-icon,
  .btn-submit .btn-label,
  .topnav a {
    transition: none;
  }
}

/* ---------- Utskrift: inga animationer, inget klistrat ---------- */

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

  body:not(.is-ready) :is(.hero-eyebrow, .hero-title) {
    opacity: 1 !important;
  }

  .s-hero.hero-ryms,
  .s-schedule .s-head {
    position: static !important;
  }
}

/* ================================================================================================
   Keyframes. Referensens värden med prefixet ew-, och de tre för INTRO och HANDLING.
   ================================================================================================ */

@keyframes ew-rise {
  from { opacity: 0; translate: 0 96px; }
  to { opacity: 1; translate: none; }
}

@keyframes ew-grow {
  from { opacity: 0; scale: 0.7; }
  to { opacity: 1; scale: none; }
}

@keyframes ew-heroout {
  from { scale: 1; }
  to { scale: 1.25; }
}

/* heroout del två: lagret över bilden i stället för brightness(1) till brightness(.45). */
@keyframes ew-heroout-lager {
  from { opacity: 0; }
  to { opacity: 0.55; }
}

@keyframes ew-fadeup {
  from { opacity: 1; translate: none; scale: none; }
  to { opacity: 0; translate: 0 -120px; scale: 0.92; }
}

@keyframes ew-wipe {
  from { clip-path: inset(0 0 100% 0); }
  to { clip-path: inset(0); }
}

@keyframes ew-word {
  from { opacity: 0; translate: 0 0.7em; }
  to { opacity: 1; translate: none; }
}

@keyframes ew-settle {
  from { scale: 1.5; color: rgb(var(--ink) / 0.19); }
  to { scale: none; color: rgb(var(--ink)); }
}

@keyframes ew-read {
  from { color: rgb(var(--ink) / 0.22); }
  to { color: rgb(var(--ink)); }
}

@keyframes ew-readlight {
  from { color: rgb(var(--on-band) / 0.37); }
  to { color: rgb(var(--on-band)); }
}

@keyframes ew-line {
  from { scale: 1 0; }
  to { scale: 1 1; }
}

@keyframes ew-dotin {
  from { scale: 0; opacity: 0; }
  to { scale: 1; opacity: 1; }
}

@keyframes ew-left {
  from { opacity: 0; translate: -80px 0; }
  to { opacity: 1; translate: none; }
}

@keyframes ew-right {
  from { opacity: 0; translate: 80px 0; }
  to { opacity: 1; translate: none; }
}

/* INTRO och HANDLING. */
@keyframes ew-stig {
  from { opacity: 0; translate: 0 var(--ew-steg, 16px); }
  to { opacity: 1; translate: 0 0; }
}

@keyframes ew-punkt {
  from { opacity: 0; scale: 0.4; }
  to { opacity: 1; scale: 1; }
}

@keyframes ew-bock {
  from { stroke-dashoffset: 34; }
  to { stroke-dashoffset: 0; }
}
