/*
  taste-skill motion.css - the micro-interactions a company site has (figma.com, x.ai): one small
  opt-in layer over system.css and ui/cards.css. Link it after them (data-motion on <html> is optional);
  load motion.js once (it adds the tab indicator, the number tick and the card loops).
  The layer is ON as soon as this file is linked: no attribute is needed (a hover shot of a linked card or photo
  shows the 2px lift and the 1.03 zoom). <html data-motion="off"> switches the whole layer off; data-motion="live"
  is the number tick in a headless shot (motion.js).
  references/motion.md says what moves and how fast. Everything here answers the pointer, the
  keyboard or a number coming into view once; nothing moves because the page scrolled.

  Hover rules sit under (hover: hover) and (pointer: fine): a phone never keeps a stuck hover.
  Under prefers-reduced-motion colours still change; nothing moves or scales.
*/

:root {
  --m-in: 140ms;                              /* hover in: buttons, links, nav, tabs */
  --m-out: 200ms;                             /* hover out, panel close */
  --m-press: 90ms;                            /* a press */
  --m-panel: 200ms;                           /* a menu or sheet opening */
  --m-slide: 240ms;                           /* an indicator travelling between two tabs or links */
  --m-image: 600ms;                           /* a photograph scaling inside its mask */
  --m-ease: cubic-bezier(.2, .8, .2, 1);      /* ease-out: fast start, soft landing */
  --m-ease-io: cubic-bezier(.65, 0, .35, 1);  /* the indicator: leaves and lands softly */
  --m-cycle: 3.2s;                            /* one cycle of a card's loop (2.5-4 s) */
  --m-lift: -2px;
}

/* ---------------------------------------------------------------- buttons */
html:not([data-motion="off"]) .btn {
  position: relative;
  transition: background-color var(--m-out) var(--m-ease), color var(--m-out) var(--m-ease),
              border-color var(--m-out) var(--m-ease), box-shadow var(--m-out) var(--m-ease),
              transform var(--m-press) var(--m-ease);
}
/* the tone moves toward the ground: a black button greys a little on white, a white one on black */
html:not([data-motion="off"]) .btn--primary:hover { background: color-mix(in oklab, var(--action) 84%, var(--bg)); }
html:not([data-motion="off"]) .btn--secondary:hover {
  background: color-mix(in oklab, var(--bg) 92%, var(--ink));
  border-color: color-mix(in oklab, var(--ink) 32%, transparent);
}
html:not([data-motion="off"]) .btn:hover { transition-duration: var(--m-in), var(--m-in), var(--m-in), var(--m-in), var(--m-press); }
html:not([data-motion="off"]) .btn:active { transform: translateY(1px) scale(.985); }
/* the step: a button rises 1px on hover (2px on the large size) and presses 1px down with a 1.5% shrink on
   click; pointer only. The text-link secondary (arrow and block looks) keeps its underline and does not step.
   Reduced motion keeps the colour change only (see the end of this file). */
@media (hover: hover) and (pointer: fine) {
  html:not([data-motion="off"]) .btn:hover { transform: translateY(-1px); }
  html:not([data-motion="off"]) .btn--xl:hover { transform: translateY(-2px); }
  html:not([data-motion="off"]):is([data-buttons="arrow"], [data-buttons="block"]) .btn--secondary:hover { transform: none; }
  html:not([data-motion="off"]) .btn:active { transform: translateY(1px) scale(.985); }
  html:not([data-motion="off"]):is([data-buttons="arrow"], [data-buttons="block"]) .btn--secondary:active { transform: scale(.985); }
}

/* the arrow: the text arrow (.btn--arrow, .link) nudges 3px; the drawn arrow (.m-arrow) grows its
   stem and moves its head 3px, the way a good product site's "Learn more" does */
html:not([data-motion="off"]) :is(.btn--arrow, .link)::after { transition: transform var(--m-out) var(--m-ease); }
html:not([data-motion="off"]) :is(.btn--arrow, .link):hover::after { transform: translateX(3px); transition-duration: var(--m-in); }
.m-arrow { width: 12px; height: 12px; flex: none; overflow: visible; fill: none; stroke: currentColor; stroke-width: 1.6;
           stroke-linecap: round; stroke-linejoin: round; }
.m-arrow__stem { opacity: 0; transform: scaleX(.2); transform-origin: 1px 50%; transform-box: fill-box;
                 transition: opacity var(--m-out) var(--m-ease), transform var(--m-out) var(--m-ease); }
.m-arrow__head { transition: transform var(--m-out) var(--m-ease); }
:is(a, button):hover > .m-arrow .m-arrow__stem,
:is(a, button):focus-visible > .m-arrow .m-arrow__stem { opacity: 1; transform: none; transition-duration: var(--m-in); }
:is(a, button):hover > .m-arrow .m-arrow__head,
:is(a, button):focus-visible > .m-arrow .m-arrow__head { transform: translateX(3px); transition-duration: var(--m-in); }

/* a pill that is a link (the line above a hero headline) */
html:not([data-motion="off"]) a.pill { transition: background-color var(--m-out) var(--m-ease), border-color var(--m-out) var(--m-ease); }
html:not([data-motion="off"]) a.pill:hover { background: color-mix(in oklab, var(--card) 90%, var(--ink)); border-color: var(--line-strong); transition-duration: var(--m-in); }

/* ---------------------------------------------------------------- links: an underline that draws */
/* .link and links in running text: a rest line, and the ink line draws left to right on hover and
   leaves to the right. A link in a sentence rests on its own colour at 45% (about 3.3:1 on its
   ground), so it reads as a link even when it is the same colour as the text (WCAG 1.4.1); .link
   and .m-link carry an arrow or sit alone and rest on the faint hairline */
html:not([data-motion="off"]) :is(.link, .m-link, p a:not([class]), .footer ul a, .footer .link) {
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor), linear-gradient(var(--m-rest, var(--line-strong)), var(--m-rest, var(--line-strong)));
  background-repeat: no-repeat; background-size: 0% 1px, 100% 1px;
  background-position: right 0 bottom 0, 0 100%;
  transition: background-size var(--m-out) var(--m-ease), color var(--m-out) var(--m-ease);
  padding-bottom: 2px;
}
/* set on its own rule: the shared rule above is (0,3,1) through :is(.footer .link) and would win */
html:not([data-motion="off"]) p a:not([class]) { --m-rest: color-mix(in oklab, currentColor 45%, transparent); }
html:not([data-motion="off"]) :is(.footer ul a, .m-link--bare):not(:hover, :focus-visible) { background-size: 0% 1px, 0% 1px; }
html:not([data-motion="off"]) :is(.link, .m-link, p a:not([class]), .footer ul a, .footer .link):is(:hover, :focus-visible) {
  background-size: 100% 1px, 100% 1px; background-position: left 0 bottom 0, 0 100%; transition-duration: var(--m-in);
}
/* the link's arrow is not underlined: the line runs under the words only */
html:not([data-motion="off"]) .link { background-size: 0% 1px, calc(100% - 1em - var(--s-2)) 1px; }
html:not([data-motion="off"]) .link:is(:hover, :focus-visible) { background-size: calc(100% - 1em - var(--s-2)) 1px, calc(100% - 1em - var(--s-2)) 1px; }
html:not([data-motion="off"]) .link, html:not([data-motion="off"]) .link:is(:hover, :focus-visible) { background-position: 0 100%, 0 100%; }
html:not([data-motion="off"]) .footer ul a:hover { color: var(--ink); }

/* ---------------------------------------------------------------- navigation */
/* the nav words take no pill (ST-08, ST-11): system.css draws the underline under a word from the left on hover and focus, and
   takes it away to the right; the page you are on keeps its still bar (system.css draws it, at the same inset) */
@media (min-width: 1024px) {
  /* the full-width panel: fades and settles 6px in 200ms, its columns follow 30ms apart; it closes
     in 120ms. While one menu is open, the next word swaps the panel at once (motion.js .m-menu-open) */
  html:not([data-motion="off"]) .nav__menu {
    transform: translateY(-6px);
    transition: opacity 120ms var(--m-ease), transform 120ms var(--m-ease), visibility 0s linear 120ms;
  }
  html:not([data-motion="off"]) .nav__item:is(:hover, :focus-within) > .nav__menu {
    transition: opacity var(--m-panel) var(--m-ease) 150ms, transform var(--m-panel) var(--m-ease) 150ms, visibility 0s linear 150ms;
  }
  /* .m-menu-open comes on when the first panel starts to show (150ms): from then the next word swaps
     the panel at once. Duration and delay go to 0, the properties stay listed, so the panel that is
     still fading in finishes its own 200ms instead of snapping */
  html:not([data-motion="off"]) .nav.m-menu-open .nav__menu,
  html:not([data-motion="off"]) .nav.m-menu-open .nav__item:is(:hover, :focus-within) > .nav__menu { transition-duration: 0s; transition-delay: 0s; }
  html:not([data-motion="off"]) .nav__menu .grid > * { opacity: 0; transform: translateY(4px); transition: opacity 100ms var(--m-ease), transform 100ms var(--m-ease); }
  html:not([data-motion="off"]) .nav__item:is(:hover, :focus-within) > .nav__menu .grid > * {
    opacity: 1; transform: none; transition: opacity 260ms var(--m-ease), transform 260ms var(--m-ease);
    transition-delay: calc(170ms + var(--m-i, 0) * 30ms);
  }
  html:not([data-motion="off"]) .nav.m-menu-open .nav__item:is(:hover, :focus-within) > .nav__menu .grid > * { transition-delay: calc(var(--m-i, 0) * 30ms); }
  html:not([data-motion="off"]) .nav__menu .grid > :nth-child(2) { --m-i: 1; }
  html:not([data-motion="off"]) .nav__menu .grid > :nth-child(3) { --m-i: 2; }
  html:not([data-motion="off"]) .nav__menu .grid > :nth-child(4) { --m-i: 3; }
  html:not([data-motion="off"]) .nav__menu .grid > :nth-child(n + 5) { --m-i: 4; }
  html:not([data-motion="off"]) .menu-col a { transition: color var(--m-out) var(--m-ease); }
  html:not([data-motion="off"]) .menu-col a:hover { transition-duration: var(--m-in); }
  html:not([data-motion="off"]) .menu-card .panel { transition: border-color var(--m-out) var(--m-ease); }
}
/* the phone sheet slides in 8px and fades (system.js opens it) */
@media (max-width: 1023px) {
  html:not([data-motion="off"]) .nav.is-open .nav__links { transition: opacity var(--m-panel) var(--m-ease), transform var(--m-panel) var(--m-ease); }
  @starting-style { html:not([data-motion="off"]) .nav.is-open .nav__links { opacity: 0; transform: translateY(-8px); } }
}

/* ---------------------------------------------------------------- tabs: the indicator slides */
html:not([data-motion="off"]) .tabs.m-has-ind { position: relative; isolation: isolate; }
html:not([data-motion="off"]) .tabs.m-has-ind .tab { position: relative; z-index: 1; }
html:not([data-motion="off"]) .tabs.m-has-ind .tab[aria-selected="true"] { background: transparent; border-color: transparent; }
.m-tab-ind {
  position: absolute; left: 0; top: 0; z-index: 0; width: var(--w, 0px); height: var(--h, 0px);
  border-radius: var(--r-btn); background: var(--ink); pointer-events: none;
  transform: translate(var(--x, 0px), var(--y, 0px));
  transition: transform var(--m-slide) var(--m-ease-io), width var(--m-slide) var(--m-ease-io), height var(--m-slide) var(--m-ease-io);
}
.m-tab-ind.is-jump { transition: none; }
html:not([data-motion="off"]) .tab { transition: color var(--m-slide) var(--m-ease), border-color var(--m-out) var(--m-ease), background-color var(--m-out) var(--m-ease); }

/* ---------------------------------------------------------------- focus: the ring settles in */
/* only on the plain controls, never on a part of a loop, and not on an element that runs its own
   animation (mark it .m-anim): an animation set here would replace that element's own */
html:not([data-motion="off"]) :is(a, button, input, select, textarea, summary, [tabindex]):focus-visible:not([data-m-loop] *, .m-anim, .loop-pause) {
  animation: m-focus var(--m-out) var(--m-ease);
}
@keyframes m-focus { from { outline-offset: 6px; outline-color: transparent; } }

/* ---------------------------------------------------------------- cards: a 2px lift */
/* the card rises 2px, its border and shadow deepen; a card without its own box (.card--open) lifts
   its panel. Never tilts, flips or follows the pointer */
html:not([data-motion="off"]) .card:not(.card--open, .card--plan) {
  transition: transform var(--m-out) var(--m-ease), border-color var(--m-out) var(--m-ease), box-shadow var(--m-out) var(--m-ease);
}
html:not([data-motion="off"]) .card--open > .panel { transition: transform var(--m-out) var(--m-ease), box-shadow var(--m-out) var(--m-ease); }
@media (hover: hover) and (pointer: fine) {
  html:not([data-motion="off"]) .card:not(.card--open, .card--plan, .m-nolift):hover {
    transform: translateY(var(--m-lift));
    border-color: color-mix(in oklab, var(--ink) 16%, transparent);
    /* the shadow is deeper on a dark card (its lightness l): about .2 on white, .64 on near-black */
    box-shadow: inset 0 1px 0 var(--c-hi, transparent), 0 1px 2px rgb(0 0 0 / .06),
                0 14px 32px -18px oklch(from var(--card, #fff) 0 0 0 / calc(0.75 - l * 0.55));
    transition-duration: 180ms;
  }
  html:not([data-motion="off"]) .card--open:not(.m-nolift):hover > .panel {
    transform: translateY(var(--m-lift)); box-shadow: 0 14px 32px -18px oklch(from var(--bg, #fff) 0 0 0 / calc(0.75 - l * 0.55)); transition-duration: 180ms;
  }
}

/* ---------------------------------------------------------------- photographs: 1.03 inside the mask */
/* a photograph in a link or a linked card grows 3% inside its own mask (the frame, the tile, a .m-zoom wrapper);
   the mask clips it, so the box never moves. .m-zoom is the hook for any other photograph: wrap it. */
.m-zoom { overflow: clip; }
html:not([data-motion="off"]) :is(.frame__img, .tile__media > img, .m-zoom img, .m-zoom > video) {
  transition: transform var(--m-image) var(--m-ease);
}
@media (hover: hover) and (pointer: fine) {
  html:not([data-motion="off"]) :is(a, .card:has(a), .m-zoom):hover :is(.frame__img, .tile__media > img),
  html:not([data-motion="off"]) .m-zoom:hover :is(img, video) { transform: scale(1.03); }
}

/* ---------------------------------------------------------------- inputs, switches, checks */
html:not([data-motion="off"]) .field :is(input, select, textarea), .m-input {
  transition: border-color var(--m-out) var(--m-ease), box-shadow var(--m-out) var(--m-ease), background-color var(--m-out) var(--m-ease);
}
html:not([data-motion="off"]) .field :is(input, select, textarea):hover, .m-input:hover { border-color: color-mix(in oklab, var(--ink) 48%, transparent); }
html:not([data-motion="off"]) .field :is(input, select, textarea):focus, .m-input:focus {
  outline: none; border-color: var(--action);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--action) 20%, transparent); transition-duration: var(--m-in);
}
/* a switch: <button class="m-switch" role="switch" aria-checked="false" aria-label="..."></button>
   (motion.js flips aria-checked on click unless the page handles it). The knob slides in 180ms and
   stretches 4px while pressed. The off track is ink at 48% (3.3:1 or more against the knob and the
   ground, the 3:1 a control needs) */
.m-switch {
  position: relative; flex: none; width: 40px; height: 24px; padding: 0; border: 0; border-radius: 999px; cursor: pointer;
  background: color-mix(in oklab, var(--ink) 48%, var(--bg));
  transition: background-color 180ms var(--m-ease);
}
.m-switch::before {
  content: ""; position: absolute; left: 3px; top: 3px; width: 18px; height: 18px; border-radius: 999px;
  background: var(--bg); box-shadow: 0 1px 3px rgb(0 0 0 / .25);
  transition: transform 180ms var(--m-ease), width var(--m-press) var(--m-ease);
}
.m-switch[aria-checked="true"] { background: var(--action); }
.m-switch[aria-checked="true"]::before { transform: translateX(16px); background: var(--action-ink); }
.m-switch:active::before { width: 22px; }
.m-switch[aria-checked="true"]:active::before { transform: translateX(12px); }
.m-switch:hover:not([aria-checked="true"]) { background: color-mix(in oklab, var(--ink) 58%, var(--bg)); }
/* a check box: <input type="checkbox" class="m-check">, the tick draws in 160ms */
.m-check {
  appearance: none; -webkit-appearance: none; flex: none; margin: 0; width: 20px; height: 20px; border-radius: 6px; cursor: pointer;
  border: 1px solid color-mix(in oklab, var(--ink) 48%, var(--bg)); background: var(--bg) no-repeat center / 14px 14px;
  display: inline-grid; place-items: center;
  transition: background-color var(--m-in) var(--m-ease), border-color var(--m-in) var(--m-ease);
}
.m-check::before {
  content: ""; width: 10px; height: 6px; margin-top: -2px; border: solid var(--action-ink); border-width: 0 0 2px 2px;
  transform: rotate(-45deg) scale(0); transform-origin: 50% 50%; transition: transform 160ms var(--m-ease);
}
.m-check:hover:not(:checked) { border-color: color-mix(in oklab, var(--ink) 64%, var(--bg)); }
.m-check:checked { background-color: var(--action); border-color: var(--action); }
.m-check:checked::before { transform: rotate(-45deg) scale(1); }

/* ---------------------------------------------------------------- FAQ
   Every answer stays open (ST-47, system.css): nothing here opens, closes or animates its height. */

/* ---------------------------------------------------------------- numbers that tick once */
[data-m-count] { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- loops inside a card's panel */
/* a CSS loop the page writes inside [data-m-loop] runs one cycle in --m-cycle (2.5-4 s) and repeats
   while on screen; motion.js pauses it off screen and restarts it on hover (motion.md) */
[data-m-loop] { --m-cycle: 3.2s; --m-still: 0.6; --m-safe: 48px; }
/* the pause button (a 32px disc 12px in from the bottom-right corner) gets a clear band: content in
   the flow ends --m-safe (48px) above the bottom edge, and a fading stage dissolves above that band,
   never under the button. Size the panel so the loop's subject sits above the fade */
[data-m-loop]:not(:has(> .stage)) { padding-bottom: var(--m-safe); }
[data-m-loop] > .stage:is(.stage--fade-b, .stage--fade-br) { bottom: var(--m-safe); }
/* on a phone (as ui.js does) and under reduced motion a loop holds its finished picture: the frame at
   --m-still (0-1) of the cycle, set on the loop where the picture is complete */
@media (max-width: 639px) {
  [data-m-loop] * { animation-duration: var(--m-cycle) !important; animation-delay: calc(-1 * var(--m-still) * var(--m-cycle)) !important;
                    animation-play-state: paused !important; }
}
[data-m-loop].is-m-off *, [data-m-loop].is-m-paused * { animation-play-state: paused !important; }

/* ---------------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion: reduce) {
  html:not([data-motion="off"]) .card:hover, html:not([data-motion="off"]) .card--open:hover > .panel,
  html:not([data-motion="off"]) :is(a, .card:has(a), .m-zoom):hover :is(.frame__img, .tile__media > img, img, video),
  html:not([data-motion="off"]) .btn:is(:hover, :active), html:not([data-motion="off"]) :is(.btn--arrow, .link):hover::after,
  :is(a, button):hover > .m-arrow :is(.m-arrow__head, .m-arrow__stem) { transform: none !important; }
  .m-arrow__stem { opacity: 1; transform: none; }
  html:not([data-motion="off"]) .nav__menu, html:not([data-motion="off"]) .nav__menu .grid > * { transform: none !important; }
  html:not([data-motion="off"]) :is(a, button, input, select, textarea, summary, [tabindex]):focus-visible { animation: none; }
  [data-m-loop] * { animation-duration: var(--m-cycle) !important; animation-iteration-count: infinite !important;
                    animation-delay: calc(-1 * var(--m-still) * var(--m-cycle)) !important; animation-play-state: paused !important; }
}
