/* Motion release (October 2026): the techniques chosen from the motion lab (Ania Kubów's CSS series, rebuilt in
   the Flowella design system), live for everyone since 21 October 2026. Every rule here is scoped to
   :root[data-motion], which layouts/base.hubl.html prints on <html>; take the attribute off and the site goes back
   to how it was before. site.js reads the same attribute for the scripted parts (gliding highlight, rolling digits,
   tick bursts, home snap). Decisions from the preview round: Shorts-style snapping to every chapter and journey
   step, prices rolling over 1.1s both ways, subtle button depth, squircle corners everywhere.
   Reduced motion: travel, scale and loops go; colour changes stay. */

/* ---------- Tokens ---------- */
:root {
  --btn-light: rgb(255 255 255 / .30);
  --btn-shade: rgb(11 30 51 / .22);
  --btn-contact: rgb(11 30 51 / .18);
  --roll-dur: 1100ms;   /* the price meter, slowed from the lab's 480ms so the change reads */
  --roll-step: 55ms;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --btn-light: rgb(255 255 255 / .42); --btn-shade: rgb(11 30 51 / .30); --btn-contact: rgb(0 0 0 / .45); }
}
:root[data-theme="dark"] { --btn-light: rgb(255 255 255 / .42); --btn-shade: rgb(11 30 51 / .30); --btn-contact: rgb(0 0 0 / .45); }

/* ---------- 4. Pressable buttons ----------
   A 1px light along the top inside edge and a 2px shade along the bottom read as a raised pill; pressing sinks it a
   pixel and moves the shade inside. Buttons only (DESIGN.md, Soft Drop Rule exception for buttons). */
:root[data-motion] .btn {
  box-shadow: inset 0 1px 0 var(--btn-light), inset 0 -2px 0 var(--btn-shade), 0 1px 1px var(--btn-contact), var(--shadow-sm);
  transition: transform 160ms var(--ease-out), background-color 200ms ease, box-shadow 160ms var(--ease-out);
}
:root[data-motion] .btn:active {
  transform: translateY(1px) scale(0.98);
  box-shadow: inset 0 1px 0 transparent, inset 0 1px 3px var(--btn-shade), 0 0 0 transparent, 0 6px 12px -10px var(--btn-contact);
}

/* ---------- 5. Gliding highlight ----------
   One highlight per control, drawn by the container's ::before and clipped to the item under the pointer or
   keyboard focus (site.js sets --g-t/r/b/l). clip-path keeps the round ends as it stretches and stays off layout.
   The items' own hover fills go; an open menu keeps its Sky Field fill, so state and hover never mix. */
/* The menu panels are already positioned (absolute), so only the pill needs a containing block */
:root[data-motion] .nav-pill { position: relative; }
:root[data-motion] :is(.nav-pill, .menu-mega, .menu-lang)::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit; background: var(--surface-2);
  clip-path: inset(var(--g-t, 0) var(--g-r, 100%) var(--g-b, 0) var(--g-l, 0) round var(--g-round, 999px));
  opacity: 0;
  transition: clip-path 260ms var(--ease-in-out), opacity 140ms ease;
}
:root[data-motion] .menu-mega::before { --g-round: 16px; }
:root[data-motion] .menu-lang::before { --g-round: 12px; }
:root[data-motion] :is(.nav-pill, .menu-mega, .menu-lang).g-on::before { opacity: 1; }
:root[data-motion] :is(.nav-pill, .menu-mega, .menu-lang).g-jump::before { transition: opacity 140ms ease; }
:root[data-motion] :is(.nav-pill .nav-item, .menu-mega .mega-col:not(.mega-side) .mega-link, .menu-lang a) { position: relative; }
@media (hover: hover) and (pointer: fine) {
  :root[data-motion] .nav-pill .nav-item:hover:not([aria-expanded="true"]):not([aria-current]),
  :root[data-motion] .menu-mega .mega-col:not(.mega-side) .mega-link:hover,
  :root[data-motion] .menu-lang a:hover:not([aria-current="true"]) { background: transparent; }
}

/* ---------- 2. Chip rows that fade at the edge you can scroll to ----------
   The phone menu's language pills become one scrolling row; the tour's tab row on phones already scrolls. The fade
   is a mask driven by the row's own scroll position (no script); without scroll-driven animations the row simply
   shows no fade. */
@property --fade-start { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --fade-end { syntax: "<length>"; inherits: false; initial-value: 0px; }
:root[data-motion] .mm-langs {
  flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter); scroll-padding-inline: var(--gutter);
  scroll-snap-type: x proximity;
}
:root[data-motion] .mm-langs::-webkit-scrollbar { display: none; }
:root[data-motion] .mm-langs > li { flex: none; scroll-snap-align: start; }  /* the list items are the flex items */
@supports (animation-timeline: scroll()) {
  :root[data-motion] :is(.mm-langs, .dash-side) {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent 100%);
    animation: fl-edge-fade linear both;
    animation-timeline: scroll(self inline);
  }
  [dir="rtl"]:root[data-motion] :is(.mm-langs, .dash-side) {
    -webkit-mask-image: linear-gradient(to left, transparent 0, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent 100%);
    mask-image: linear-gradient(to left, transparent 0, #000 var(--fade-start), #000 calc(100% - var(--fade-end)), transparent 100%);
  }
}
@keyframes fl-edge-fade {
  0% { --fade-start: 0px; --fade-end: 44px; }
  10% { --fade-start: 44px; }
  90% { --fade-end: 44px; }
  100% { --fade-start: 44px; --fade-end: 0px; }
}

/* ---------- 3. Shorts-style snapping on the home page ----------
   site.js adds .snap-home to <html> on the home page. Every chapter of the page is a snap point, and so is each of
   the journey's six steps ("Here's what happens when your form moves to WhatsApp"), and a flick can't skip one
   (scroll-snap-stop: always): one flick, one step. Each lands with its top edge on the header's bottom edge, so the
   home page's scroll padding is the header's height rather than base.css's 104px anchor offset. On wide screens
   each journey step is one screen tall beside the sticky stage, so the next flick always brings the next step; a
   snap area taller than the screen (long chapters, the journey's cards on phones) scrolls freely inside, as browsers
   allow, and the next one catches. The footer snaps to its end, so the page can still reach the bottom. */
:root[data-motion].snap-home { scroll-snap-type: y mandatory; scroll-padding-top: calc(var(--header-h) + 1px); }
:root[data-motion].snap-home .page-home :is(main .row-depth-1.dnd-section, .chapter[data-chapter]) {
  scroll-snap-align: start; scroll-snap-stop: always;
}
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  :root[data-motion].snap-home .page-home .chapter[data-chapter] {
    box-sizing: border-box; min-height: calc(100svh - var(--header-h) - 1px); padding-bottom: 32px;
  }
}
/* Phones and tablets: the same stops and nothing in between. Chapters and journey steps are taller than the screen
   there, so each scrolls freely inside, and a flick still can't run past the next one's start. Journey steps land
   16px under the header, the same as the side inset */
@media (max-width: 1023px) {
  :root[data-motion].snap-home .page-home .chapter[data-chapter] { scroll-margin-top: 16px; }
}
:root[data-motion].snap-home .page-home .site-footer { scroll-snap-align: end; }

/* ---------- 6. Prices that roll ----------
   Flowella.roll() (site.js) renders each digit as a column of 0 to 9 moved by whole line heights. Separators stay
   put; the plain amount stays in the page for screen readers. Digits always run left to right, Arabic pages
   included (the calculator uses Latin digits there). */
.fl-roll { unicode-bidi: isolate; }
.fl-roll .num { display: inline-flex; direction: ltr; vertical-align: top; font-variant-numeric: tabular-nums; }
.fl-roll .col { display: inline-block; height: 1lh; overflow: hidden; }
.fl-roll .strip {
  display: flex; flex-direction: column;
  translate: 0 calc(var(--d, 0) * -1lh);
  transition: translate var(--roll-dur) var(--ease-out);
  transition-delay: calc(var(--i, 0) * var(--roll-step));
}
.fl-roll .strip > span, .fl-roll .sep { display: block; height: 1lh; }
.fl-roll .strip > span { text-align: center; }
.fl-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ---------- 1. Tick bursts ----------
   Flowella.burst() throws white WhatsApp discs, Flowella logomarks, read ticks and chat bubbles from where an
   in-page action completes (copying a link, sending a demo form, a form's thank-you).
   Fixed layer above everything, no hits. */
.fl-burst { position: fixed; inset: 0; z-index: 9000; pointer-events: none; overflow: hidden; }
.fl-burst > span { position: absolute; left: 0; top: 0; will-change: transform, opacity; }
.fl-burst .pt-wa { width: 30px; height: 30px; border-radius: 50%; background: #FFFFFF; box-shadow: 0 2px 4px rgb(11 30 51 / .25); display: grid; place-items: center; color: #13994A; }
.fl-burst .pt-wa svg { width: 18px; height: 18px; }
.fl-burst .pt-tick { display: grid; place-items: center; width: 34px; height: 22px; border-radius: 8px; background: #FFFFFF; box-shadow: 0 2px 4px rgb(11 30 51 / .25); color: #53BDEB; }
.fl-burst .pt-tick svg { width: 22px; height: 14px; }
.fl-burst .pt-mark { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 9px; background: #FFFFFF; box-shadow: 0 2px 4px rgb(11 30 51 / .25); }
.fl-burst .pt-mark svg { width: 19px; height: 19px; }
.fl-burst .pt-bubble { width: 26px; height: 20px; border-radius: 10px 10px 10px 3px; background: #D9FDD3; box-shadow: 0 2px 4px rgb(11 30 51 / .22); }
.fl-burst .pt-bubble.alt { background: #FFFFFF; border-radius: 10px 10px 3px 10px; }

/* ---------- 7. Squircle corners ----------
   Every rounded rectangle on the site takes the continuous squircle curve: cards, fields, mock screens, inputs, tiles,
   images and the pieces inside them. Each radius in the theme CSS is written as calc(Npx * var(--sq-scale, 1)) beside
   corner-shape: var(--sq-shape, round), and inherited radii inherit the shape too, so nested corners always match
   (scripts/squircle-corners.py keeps it that way; run it with --check). A squircle cuts away far less of a corner
   than a circle of the same radius, so to read as soft as round corners every radius grows 1.7 times while it
   applies. Pills and circles stay round. Only where corner-shape is supported; elsewhere corners stay round. */
@supports (corner-shape: squircle) {
  :root[data-motion] { --r-xl: 61px; --r-lg: 51px; --sq-scale: 1.7; --sq-shape: squircle; }
}

/* ---------- 8. Reading bar on journal posts ----------
   A 3px Accent Ink line along the bottom of the sticky header fills as the article body scrolls past (a view
   timeline on .ja-body, shared up to the header with timeline-scope); where that isn't supported, the page's own
   scroll drives it. No scroll listener either way. */
:root[data-motion] .page-journal-post .site-header::after {
  content: ""; position: absolute; inset-inline: 0; bottom: -1px; height: 3px; pointer-events: none;
  background: var(--accent-ink); transform: scaleX(0); transform-origin: 0 50%;
}
[dir="rtl"]:root[data-motion] .page-journal-post .site-header::after { transform-origin: 100% 50%; }
@supports (animation-timeline: scroll()) {
  :root[data-motion] .page-journal-post .site-header::after { animation: fl-read linear both; animation-timeline: scroll(root block); }
}
@supports (timeline-scope: --a) and (animation-timeline: view()) {
  :root[data-motion] .page-journal-post { timeline-scope: --fl-article; }
  :root[data-motion] .page-journal-post .ja-body { view-timeline: --fl-article block; }
  :root[data-motion] .page-journal-post .site-header::after { animation-timeline: --fl-article; animation-range: contain 0% contain 100%; }
}
@keyframes fl-read { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ---------- 9. A live dot that breathes ----------
   While a demo workflow runs, its Running pill carries a dot whose ring grows and fades every 2.4 seconds: a solid
   ring in the pill's own ink, not a glow. */
:root[data-motion] :is([data-state="running"] .state-pill, [data-wf-pill][data-state="running"], [data-pill][data-state="running"])::before {
  content: ""; flex: none; width: 7px; height: 7px; margin-inline-end: 6px; border-radius: 50%;
  display: inline-block; vertical-align: 1px; background: currentColor;
  animation: fl-live-ring 2400ms var(--ease-out) infinite;
}
@keyframes fl-live-ring {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 55%, transparent); }
  60%, 100% { box-shadow: 0 0 0 7px color-mix(in srgb, currentColor 0%, transparent); }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  :root[data-motion] .btn:active { transform: none; }
  :root[data-motion] :is(.nav-pill, .menu-mega, .menu-lang)::before { transition: opacity 140ms ease; }
  .fl-roll .strip { transition: none; }
  :root[data-motion] :is([data-state="running"] .state-pill, [data-wf-pill][data-state="running"], [data-pill][data-state="running"])::before { animation: none; }
  :root[data-motion].snap-home { scroll-snap-type: none; }
}
