/* Motion release (October 2026): the techniques chosen from the motion lab (Ania Kubów's CSS series, rebuilt in
   the Flowella design system). Every rule here is scoped to :root[data-motion], so nothing changes until the
   attribute is on <html>:
   - preview: the head script in layouts/base.hubl.html sets data-motion="preview" for anyone who opens a page with
     ?motion=preview (kept in localStorage until ?motion=off), plus the decision variants below from the preview panel
     (js/motion-preview.js) or the URL: data-snap (shorts | gentle | off), data-roll (steady | slow | slower),
     data-press (subtle | bold), data-squircle (on | off).
   - go-live: the layout prints data-motion="on" on <html> for everyone, with the chosen variants as defaults here.
   site.js reads the same attribute for the scripted parts (gliding highlight, rolling digits, tick bursts, home snap).
   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;
  --roll-step: 55ms;
}
:root[data-press="bold"] { --btn-light: rgb(255 255 255 / .45); --btn-shade: rgb(11 30 51 / .32); --btn-contact: rgb(11 30 51 / .26); }
@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:not([data-theme="light"])[data-press="bold"] { --btn-light: rgb(255 255 255 / .58); --btn-shade: rgb(11 30 51 / .40); --btn-contact: rgb(0 0 0 / .6); }
}
: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); }
:root[data-theme="dark"][data-press="bold"] { --btn-light: rgb(255 255 255 / .58); --btn-shade: rgb(11 30 51 / .40); --btn-contact: rgb(0 0 0 / .6); }
/* Rolling digits: the price meter's speed (slowed down from the lab's 480ms, at your request) */
:root[data-roll="steady"] { --roll-dur: 800ms; --roll-step: 40ms; }
:root[data-roll="slower"] { --roll-dur: 1500ms; --roll-step: 75ms; }

/* ---------- 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"]),
  :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 a { flex: none; scroll-snap-align: start; }
@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. Each top-level chapter is a snap point that a flick can't
   skip (scroll-snap-stop: always); chapters taller than the screen still scroll freely inside, as browsers allow
   for snap areas bigger than the viewport. "Gentle" settles only when you stop near a chapter's start. The footer
   snaps to its end, so the page can still reach the bottom. */
:root[data-motion].snap-home:not([data-snap="off"]) { scroll-snap-type: y mandatory; }
:root[data-motion].snap-home[data-snap="gentle"] { scroll-snap-type: y proximity; }
:root[data-motion].snap-home:not([data-snap="off"]) .page-home main .row-depth-1.dnd-section { scroll-snap-align: start; }
:root[data-motion].snap-home:not([data-snap="off"]):not([data-snap="gentle"]) .page-home main .row-depth-1.dnd-section { scroll-snap-stop: always; }
:root[data-motion].snap-home:not([data-snap="off"]) .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, 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-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 ----------
   Chapter fields, cards, mock screens, logo tiles, stickers and journal images take the continuous squircle curve.
   A squircle cuts away far less of a corner than a circle of the same radius, so to read as soft as today's corners
   (the same area cut away, with the smoother join) every radius grows about 1.7 times while it applies. Only
   where corner-shape is supported; elsewhere everything keeps today's round corners. */
@supports (corner-shape: squircle) {
  :root[data-motion]:not([data-squircle="off"]) { --r-xl: 61px; --r-lg: 51px; }
  :root[data-motion]:not([data-squircle="off"]) :is(
    .hero-field, .close-field, .mode, .step, .stage, .dash-field, .price-panel, .kb, .pr-step, .igd-how-step, .hs-how-step,
    .ja-hero-field, .jr-hero-field, .ja-play-field, .jcard.is-lead, .ft-meta-card, .ft-call-host, .su-panel, .ig-hub,
    [class$="-field"], [class*="-field "], [class$="-panel"], [class*="-panel "],
    .chapter, .wf, .pr-plan, .lp-tier, .lp-step, .lg-card, .try, .ja-flow, .bd-keep-card, .bd-start-card, .su-rt-card,
    .sbs-card, .co-pp-card, .co-rs-card, .ja-play-card, .ft-terms-card, .ft-setup-demo, .uc-map-sheet, .cd-rating,
    .pr-calc-controls, .pr-calc-results, .pr-count-side, .hs-plan-controls, .hs-plan-result, .igd-soon, .igd-today-panel,
    .lgg-route, .lgg-tip, .co-ct-wa, .co-ct-route, .pgd-group, .uc-five-table, .apw-events-groups, .ig-soon-page, .ig-soon-today,
    .sys-none, .cd-cost-controls, .cd-cost-results
  ) { corner-shape: squircle; }
  :root[data-motion]:not([data-squircle="off"]) :is(
    .screen, .wa-flow, .crm-rec, .chat, .sheet, .mini, .record, .jcard.is-lead .jcard-img,
    .hs-hero-art, .jr-hero-art, .uc-hero-art, .in-hero-art, .cd-hero-art, .rs-hero-art, .ch-hero-art, .fp-hero-art,
    .lp-hero-art, .co-hero-art, .ig-hero-art, .igd-hero-art, .sbd-app, .ixd-app, .chd-app, .ft-setup-shot,
    .uc-copy-card, .why-list-card, .uc-deep-card, .in-uses-card, .in-try-wf, .fp-data-wf, .lg-qr-box,
    .fp-cmp-chat, .fp-cmp-sheet, .lp-book-cal
  ) {
    border-radius: 44px; corner-shape: squircle;
  }
  :root[data-motion]:not([data-squircle="off"]) :is(.mock) { border-radius: 37px; corner-shape: squircle; }
  :root[data-motion]:not([data-squircle="off"]) :is(.jcard-img, .ja-cover img, .post-img) { border-radius: 48px; corner-shape: squircle; }
  :root[data-motion]:not([data-squircle="off"]) .logo-tile { border-radius: 31px; corner-shape: squircle; }
  :root[data-motion]:not([data-squircle="off"]) .sticker { border-radius: 27px; corner-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); }
}

/* ---------- Preview panel (js/motion-preview.js, preview mode only) ---------- */
.fl-mp {
  position: fixed; z-index: 9100; inset-inline-start: max(12px, env(safe-area-inset-left, 0px)); bottom: max(12px, env(safe-area-inset-bottom, 0px));
  width: min(320px, calc(100vw - 24px)); padding: 14px 16px 16px; border-radius: 22px;
  background: var(--surface); color: var(--ink); border: 1px solid var(--line); box-shadow: var(--shadow-lift);
  font: 500 14px/1.4 var(--text); display: grid; gap: 10px;
}
.fl-mp[data-min] > :not(.fl-mp-head) { display: none; }
.fl-mp-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.fl-mp-head b { font-weight: 800; font-size: 15px; }
.fl-mp-head button, .fl-mp-exit { min-height: 36px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); font: 700 13px/1 var(--text); cursor: pointer; }
.fl-mp label { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px; font-weight: 600; }
.fl-mp select { min-height: 36px; padding: 0 8px; border-radius: 10px; border: 1.5px solid var(--field-line); background: var(--ground); color: var(--ink); font: 600 14px/1 var(--text); }
.fl-mp p { font-size: 13px; color: var(--ink-soft); }

/* ---------- 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; }
}
