/* ==========================================================================
   winAI site — hand-written additions on top of the prebuilt Tailwind bundle.
   New markup uses wn-* classes so it never depends on a Tailwind rebuild.
   ========================================================================== */

:root{
  --wn-primary:#0d7ff2;
  --wn-accent:#5eb0ff;   /* light blue — headline emphasis */
  --wn-primary-soft:rgba(13,127,242,.15);
  --wn-bg:#101922;
  --wn-card:#18232f;
  --wn-dim:#26333f;

  /* Motion tokens. The built-in CSS easings are too weak to read as
     intentional; these are the stronger variants used site-wide. */
  --wn-ease-out:cubic-bezier(.23,1,.32,1);
  --wn-ease-in-out:cubic-bezier(.77,0,.175,1);

  --wn-press:160ms;   /* button press feedback */
  --wn-fast:180ms;    /* hover / colour changes */
  --wn-mid:240ms;     /* accordions, small reveals */
  --wn-deck:480ms;    /* carousel slide — explanatory, allowed to run longer */
}

/* ---------- Thai typography ---------------------------------------------- */
html[lang="th"] body,
html[lang="th"] .font-display{
  font-family:"Noto Sans Thai","Space Grotesk",sans-serif;
}
html[lang="th"] h1,
html[lang="th"] h2,
html[lang="th"] h3{ line-height:1.45; }
/* ---------- Single-line headlines ---------------------------------------
   .wn-fit headlines must never wrap, in either language. fitHeadings() in
   index.html shrinks the font just enough to fit; these rules let the text
   run wider than its max-w-* parent before that kicks in. */
.wn-fit{
  white-space:nowrap;
  max-width:none;
  width:100%;
  margin-left:auto;
  margin-right:auto;
}
html[lang="th"] .wn-fit{ white-space:nowrap; }

/* ---------- Headline accent ----------------------------------------------
   Lives in the markup (and in the Thai string) so the i18n layer, which
   swaps innerHTML, carries it through both languages. */
.wn-accent{
  color:var(--wn-accent);
}

/* ==========================================================================
   Press feedback
   Every pressable element confirms the interface heard the tap. Scale is
   subtle and the transition names exact properties, never `all`.
   ========================================================================== */
button,
.btn-primary,
.wn-tester-btn,
a[href^="https://play.google.com"]{
  transition:transform var(--wn-press) var(--wn-ease-out);
}
button:active,
.btn-primary:active,
.wn-tester-btn:active,
a[href^="https://play.google.com"]:active{
  transform:scale(.97);
}
/* The deck phones own their transform; press feedback there is handled by
   the slot rules below so the two never fight. */
.wn-phone:active{ transform:none; }

/* Consistent, visible keyboard focus. */
a:focus-visible,
button:focus-visible{
  outline:2px solid var(--wn-primary);
  outline-offset:3px;
  border-radius:6px;
}

/* ==========================================================================
   Scroll reveal + stagger
   Transitions (not keyframes) so a fast scroll can retarget mid-animation.
   --wn-i is the child index; delays stay short so nothing feels withheld.
   ========================================================================== */
/* Gated on .wn-js: without JavaScript nothing is ever hidden, since the
   observer that would reveal it never runs. */
.wn-js .wn-reveal{
  opacity:0;
  transform:translateY(14px);
  transition:opacity 420ms var(--wn-ease-out),
             transform 420ms var(--wn-ease-out);
  transition-delay:calc(var(--wn-i,0) * 60ms);
}
.wn-js .wn-reveal.is-in{
  opacity:1;
  transform:translateY(0);
}

/* Hero enters on load rather than on scroll — it is already in view. */
.wn-js .wn-hero-in{
  opacity:0;
  transform:translateY(16px);
  animation:wn-rise 560ms var(--wn-ease-out) forwards;
  animation-delay:calc(var(--wn-i,0) * 70ms);
}
@keyframes wn-rise{
  to{ opacity:1; transform:translateY(0); }
}

/* ---------- Nav ----------------------------------------------------------- */
.wn-nav{
  transition:box-shadow var(--wn-fast) ease,
             background-color var(--wn-fast) ease;
}
.wn-nav.is-scrolled{
  box-shadow:0 1px 0 rgba(13,127,242,.18), 0 8px 30px -18px rgba(0,0,0,.9);
}
/* Underline that grows from the link itself, not a colour-only hover. */
.wn-navlink{
  position:relative;
  transition:color var(--wn-fast) ease;
}
.wn-navlink::after{
  content:"";
  position:absolute;
  left:0; right:0; bottom:-6px;
  height:1.5px;
  background:var(--wn-primary);
  border-radius:2px;
  transform:scaleX(0);
  transform-origin:left;
  transition:transform var(--wn-fast) var(--wn-ease-out);
}
@media (hover:hover) and (pointer:fine){
  .wn-navlink:hover{ color:#fff; }
  .wn-navlink:hover::after{ transform:scaleX(1); }
}

/* ---------- Orange emphasis text ------------------------------------------ */
.wn-orange{
  color:#f97316;
}

/* ---------- Tester page: header back-link ---------------------------------
   On a real phone width there isn't room for "Back to winAI.com" next to
   the language toggle without wrapping — the wrap then breaks the header's
   fixed h-16 and the whole bar overflows. The logo already links home, so
   below 480px the text link just isn't needed. */
@media (max-width:479px){
  .wn-back-link{ display:none; }
}

/* ---------- Tester CTA (orange outline) ----------------------------------- */
.wn-tester-btn{
  border-color:#f97316;
  color:#f97316;
  background:transparent;
  transition:transform var(--wn-press) var(--wn-ease-out),
             border-color var(--wn-fast) ease,
             background-color var(--wn-fast) ease,
             color var(--wn-fast) ease;
}
@media (hover:hover) and (pointer:fine){
  .wn-tester-btn:hover{
    border-color:#fb923c;
    background:rgba(249,115,22,.1);
    color:#fb923c;
  }
}

/* ---------- Social (TikTok) ----------------------------------------------- */
.wn-social{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:36px;height:36px;
  flex:0 0 auto;
  border:1px solid var(--wn-dim);
  border-radius:10px;
  background:rgba(24,35,47,.7);
  color:hsla(0,0%,100%,.62);
  transition:color var(--wn-fast) ease,
             border-color var(--wn-fast) ease,
             transform var(--wn-press) var(--wn-ease-out);
}
.wn-social svg{ width:19px;height:19px;display:block; }
@media (hover:hover) and (pointer:fine){
  .wn-social:hover{ color:#fff; border-color:rgba(13,127,242,.55); }
}
.wn-social:active{ transform:scale(.94); }

/* ---------- Footer row ----------------------------------------------------- */
/* Both outer blocks take an equal share of the row, so the TikTok icon between
   them sits on the true centre of the footer rather than being nudged by the
   differing widths of the copyright line and the privacy link. Only from the
   sm breakpoint up — below that the footer stacks into a column. */
@media (min-width:640px){
  .wn-foot-side{ flex:1 1 0; }
  .wn-foot-end{ justify-content:flex-end; }
}

/* ---------- Language switcher -------------------------------------------- */
.wn-lang{
  display:inline-flex;
  align-items:center;
  gap:2px;
  padding:3px;
  border:1px solid var(--wn-dim);
  border-radius:9999px;
  background:rgba(24,35,47,.7);
}
.wn-lang button{
  display:inline-flex;
  align-items:center;
  gap:7px;
  padding:5px 11px 5px 6px;
  border-radius:9999px;
  font-size:.8125rem;
  font-weight:600;
  line-height:1;
  color:hsla(0,0%,100%,.55);
  background:transparent;
  transition:background-color var(--wn-fast) ease,
             color var(--wn-fast) ease,
             transform var(--wn-press) var(--wn-ease-out);
}
@media (hover:hover) and (pointer:fine){
  .wn-lang button:hover{ color:#fff; }
}
.wn-lang button[aria-pressed="true"]{
  color:#fff;
  background:var(--wn-primary-soft);
  box-shadow:inset 0 0 0 1px rgba(13,127,242,.45);
}
.wn-flag{
  width:22px;height:16px;
  border-radius:3px;
  display:block;
  box-shadow:0 0 0 1px rgba(255,255,255,.15);
  overflow:hidden;
  transition:filter var(--wn-fast) ease;
}
.wn-lang button[aria-pressed="false"] .wn-flag{ filter:saturate(.35) opacity(.75); }

/* ---------- Cards --------------------------------------------------------- */
/* Overrides the prebuilt .card-hover so lift is ease-out, exact-property and
   gated behind a real pointer (touch taps otherwise stick the hover state). */
.card-hover{
  transition:transform var(--wn-mid) var(--wn-ease-out),
             border-color var(--wn-mid) ease,
             box-shadow var(--wn-mid) ease;
}
.card-hover:hover{
  transform:none;
  border-color:var(--wn-dim);
  box-shadow:none;
}
@media (hover:hover) and (pointer:fine){
  .card-hover:hover{
    transform:translateY(-4px);
    border-color:rgba(13,127,242,.5);
    box-shadow:0 20px 40px -20px rgba(13,127,242,.35);
  }
}

/* ==========================================================================
   Card effects — shared by "How it works" (3) and "Features" (6)
   Cursor spotlight, top-edge sheen, reactive icon. Everything here is
   transform/opacity only and gated behind a real pointer, so phones get
   none of the pointer work. The JS that drives the spotlight binds to
   .wn-cardfx, so adding the class to a new card is all that is required.
   ========================================================================== */
.wn-cardfx{
  position:relative;
  overflow:hidden;              /* keeps the glow inside the rounded corners */
  isolation:isolate;
}
.wn-cardfx > *:not(.wn-cardfx-glow):not(.wn-cardfx-sheen){
  position:relative;
  z-index:1;
}

/* Cursor-tracked spotlight. Positioned by JS via translate3d rather than a
   CSS variable — changing a var on the card would restyle every child. */
.wn-cardfx-glow{
  position:absolute;
  top:0; left:0;
  width:340px; height:340px;
  margin:-170px 0 0 -170px;
  border-radius:9999px;
  background:radial-gradient(circle,rgba(13,127,242,.30),rgba(13,127,242,.10) 45%,transparent 70%);
  opacity:0;
  transition:opacity 260ms ease;
  pointer-events:none;
  z-index:0;
  will-change:transform;
}
.wn-cardfx.is-lit .wn-cardfx-glow{ opacity:1; }

/* Light sweeps across the top edge on enter. */
.wn-cardfx-sheen{
  position:absolute;
  top:0; left:0; right:0;
  height:1px;
  background:linear-gradient(90deg,transparent,rgba(13,127,242,.9),transparent);
  transform:scaleX(0);
  transform-origin:left;
  opacity:0;
  transition:transform 520ms var(--wn-ease-out), opacity 200ms ease;
  pointer-events:none;
  z-index:2;
}
.wn-cardfx.is-lit .wn-cardfx-sheen{ transform:scaleX(1); opacity:1; }

.wn-cardfx-icon{
  transition:transform 320ms var(--wn-ease-out),
             background-color 220ms ease,
             box-shadow 220ms ease;
}

@media (hover:hover) and (pointer:fine){
  .wn-cardfx:hover{
    transform:translateY(-6px);
    border-color:rgba(13,127,242,.55);
    box-shadow:0 26px 50px -24px rgba(13,127,242,.5);
  }
  .wn-cardfx:hover .wn-cardfx-icon{
    transform:translateY(-2px) scale(1.12) rotate(-6deg);
    background-color:rgba(13,127,242,.28);
    box-shadow:0 8px 22px -10px rgba(13,127,242,.8);
  }
}

/* Entrance: rise and settle from 98%. Never from scale(0) — an element that
   grows out of nothing reads as a glitch rather than an arrival. */
.wn-js .wn-cardfx{
  transform:translateY(18px) scale(.98);
  transition:opacity 520ms var(--wn-ease-out),
             transform 520ms var(--wn-ease-out),
             border-color var(--wn-mid) ease,
             box-shadow var(--wn-mid) ease;
}
.wn-js .wn-cardfx.is-in{
  transform:translateY(0) scale(1);
}

/* ---------- FAQ accordion -------------------------------------------------
   grid-template-rows 0fr→1fr animates to the content's REAL height. The old
   max-height guess made short answers snap and long ones clip. */
.accordion-content{
  max-height:none;
  display:grid;
  grid-template-rows:0fr;
  padding-bottom:0;
  transition:grid-template-rows var(--wn-mid) var(--wn-ease-out),
             padding-bottom var(--wn-mid) var(--wn-ease-out);
}
/* The answer's own bottom padding counts toward the track's minimum size and
   would hold every closed row open by 20px. It lives on the container instead. */
.accordion-content > *{
  min-height:0;
  overflow:hidden;
  padding-bottom:0;
  opacity:0;
  transition:opacity 140ms ease;
}
.accordion-item{
  transition:border-color var(--wn-fast) ease;
}
.accordion-item.open{
  border-color:rgba(13,127,242,.4);
}
.accordion-item.open .accordion-content{
  grid-template-rows:1fr;
  padding-bottom:1.25rem;
}
.accordion-item.open .accordion-content > *{
  opacity:1;
  transition-delay:80ms;
}
.chevron{
  transition:transform var(--wn-mid) var(--wn-ease-out),
             color var(--wn-fast) ease;
}
.accordion-item.open .chevron{
  transform:rotate(180deg);
  color:var(--wn-primary);
}
.faq-trigger{ transition:color var(--wn-fast) ease; }
.faq-trigger:active{ transform:none; }   /* row press would look like a jump */

/* ---------- Screens showcase --------------------------------------------- */
.wn-showcase{ position:relative; overflow:hidden; }
.wn-showcase .wn-orb{
  position:absolute;left:50%;top:38%;
  width:620px;height:620px;margin-left:-310px;
  border-radius:9999px;
  background:radial-gradient(circle,rgba(13,127,242,.22),transparent 65%);
  filter:blur(40px);
  pointer-events:none;
}

/* The deck: five phones on an arc, the middle one in focus. */
.wn-deck{
  position:relative;
  height:min(74vh,660px);
  min-height:430px;
  margin:0 auto;
  max-width:1000px;
  perspective:1600px;
  touch-action:pan-y;
}
.wn-phone{
  position:absolute;
  top:50%;left:50%;
  width:min(50vw,248px);
  margin:0;
  transform-origin:50% 60%;
  transition:transform var(--wn-deck) var(--wn-ease-in-out),
             opacity var(--wn-deck) ease,
             filter var(--wn-deck) ease;
  will-change:transform,opacity;
  /* Firefox's compositor can get stuck mid-animation on elements that mix
     perspective + will-change + repeated transform updates, leaving the
     DOM state correct but the paint frozen. These hints keep it on a
     stable layer instead of silently dropping repaints. */
  backface-visibility:hidden;
  transform-style:preserve-3d;
  cursor:pointer;
  border:0;
  background:none;
  padding:0;
}
.wn-phone img{
  width:100%;
  height:auto;
  display:block;
  border-radius:22px;
  filter:drop-shadow(0 26px 44px rgba(0,0,0,.6));
}

/* Position slots. data-slot is set by JS: 0 = focused. */
.wn-phone[data-slot="0"]{
  transform:translate(-50%,-50%) translateZ(0) scale(1.12);
  opacity:1;z-index:5;
}
.wn-phone[data-slot="-1"]{
  transform:translate(-50%,-50%) translateX(-104%) rotateY(26deg) scale(.86);
  opacity:.72;z-index:4;filter:brightness(.72);
}
.wn-phone[data-slot="1"]{
  transform:translate(-50%,-50%) translateX(104%) rotateY(-26deg) scale(.86);
  opacity:.72;z-index:4;filter:brightness(.72);
}
.wn-phone[data-slot="-2"]{
  transform:translate(-50%,-50%) translateX(-186%) rotateY(34deg) scale(.7);
  opacity:.34;z-index:3;filter:brightness(.5);
}
.wn-phone[data-slot="2"]{
  transform:translate(-50%,-50%) translateX(186%) rotateY(-34deg) scale(.7);
  opacity:.34;z-index:3;filter:brightness(.5);
}
/* Never scale(0) — an element that shrinks to nothing reads as a glitch.
   .6 keeps a shape behind the fade, like a deflated balloon. */
.wn-phone[data-slot="hidden"]{
  transform:translate(-50%,-50%) scale(.6);
  opacity:0;z-index:1;pointer-events:none;
}
.wn-phone[data-slot="0"]{ cursor:default; }
.wn-phone:focus-visible{ outline:2px solid var(--wn-primary);outline-offset:8px;border-radius:26px; }

/* Side phones lean toward the pointer before you commit to the tap. */
@media (hover:hover) and (pointer:fine){
  .wn-phone[data-slot="-1"]:hover,
  .wn-phone[data-slot="1"]:hover{
    opacity:.9;
    filter:brightness(.88);
  }
  .wn-phone[data-slot="-1"]:hover{
    transform:translate(-50%,-50%) translateX(-100%) rotateY(22deg) scale(.89);
  }
  .wn-phone[data-slot="1"]:hover{
    transform:translate(-50%,-50%) translateX(100%) rotateY(-22deg) scale(.89);
  }
}

/* Glow ring behind the focused phone. */
.wn-phone[data-slot="0"]::before{
  content:"";
  position:absolute;inset:-6% -14%;
  border-radius:40px;
  background:radial-gradient(ellipse at center,rgba(13,127,242,.35),transparent 70%);
  filter:blur(26px);
  z-index:-1;
}

/* Caption under the deck.
   The blur bridges the two texts so the swap reads as one transformation
   instead of two strings overlapping. */
.wn-caption{
  max-width:32rem;
  margin:0 auto;
  text-align:center;
  min-height:6.5rem;
  transition:opacity 200ms ease,
             transform 200ms var(--wn-ease-out),
             filter 200ms ease;
}
.wn-caption h3{
  font-family:"Space Grotesk",sans-serif;
  font-weight:700;
  font-size:1.5rem;
  margin-bottom:.5rem;
}
html[lang="th"] .wn-caption h3{ font-family:"Noto Sans Thai",sans-serif; }
.wn-caption p{
  color:hsla(0,0%,100%,.55);
  font-size:.95rem;
  line-height:1.65;
}
.wn-caption.is-swapping{
  opacity:0;
  transform:translateY(6px);
  filter:blur(3px);
}

/* Tab pills. */
.wn-tabs{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:.5rem;
  margin-top:2rem;
}
.wn-tabs button{
  position:relative;
  padding:.5rem 1.05rem;
  border-radius:9999px;
  border:1px solid var(--wn-dim);
  font-size:.85rem;
  font-weight:500;
  color:hsla(0,0%,100%,.55);
  background:rgba(24,35,47,.6);
  transition:color var(--wn-fast) ease,
             border-color var(--wn-fast) ease,
             background-color var(--wn-fast) ease,
             transform var(--wn-press) var(--wn-ease-out);
  overflow:hidden;
}
@media (hover:hover) and (pointer:fine){
  .wn-tabs button:hover{ color:#fff;border-color:rgba(13,127,242,.5); }
}
.wn-tabs button[aria-selected="true"]{
  color:#fff;
  border-color:rgba(13,127,242,.65);
  background:var(--wn-primary-soft);
}
/* Autoplay progress sweep on the active pill. linear is correct here —
   a progress indicator that eases would misreport how much time is left. */
.wn-tabs button .wn-sweep{
  position:absolute;left:0;bottom:0;height:2px;width:100%;
  background:var(--wn-primary);
  transform:scaleX(0);transform-origin:left;
}
.wn-tabs button[aria-selected="true"].is-timing .wn-sweep{
  animation:wn-sweep var(--wn-dwell,6s) linear forwards;
}
@keyframes wn-sweep{ from{transform:scaleX(0)} to{transform:scaleX(1)} }

/* Arrows. */
.wn-arrow{
  position:absolute;top:50%;z-index:8;
  width:44px;height:44px;
  margin-top:-22px;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--wn-dim);
  border-radius:9999px;
  background:rgba(16,25,34,.75);
  backdrop-filter:blur(6px);
  color:hsla(0,0%,100%,.75);
  font-size:1.1rem;line-height:1;
  transition:border-color var(--wn-fast) ease,
             color var(--wn-fast) ease,
             background-color var(--wn-fast) ease,
             transform var(--wn-press) var(--wn-ease-out);
}
@media (hover:hover) and (pointer:fine){
  .wn-arrow:hover{ color:#fff;border-color:rgba(13,127,242,.6);background:rgba(13,127,242,.18); }
}
.wn-arrow:active{ transform:scale(.92); }
.wn-arrow-prev{ left:0; }
.wn-arrow-next{ right:0; }

/* ---------- Video cards --------------------------------------------------- */
.wn-video-card{
  transition:border-color var(--wn-mid) ease,
             box-shadow var(--wn-mid) ease;
}
/* The source clips are tall 9:20 portrait recordings — at full column width
   (aspect-[9/20] on a ~400px column) they render ~900px tall and don't fit
   a laptop or phone viewport. Capping the card's width keeps the same
   aspect ratio but at a size that fits the screen.
   Do NOT cap height (e.g. max-height:Nvh) — combined with the video's
   aspect-[9/20], a height cap fights the width cap: on any viewport
   shorter than expected the browser satisfies the height limit by
   shrinking the WIDTH far below max-width instead. Width is the one
   dimension controlled here, and it's viewport-relative (vw), not a flat
   px number, because a fixed px cap (e.g. 280px) is still far too wide on
   an actual ~360-420px-wide phone screen — min() picks whichever is
   smaller so desktop gets the full 280px and phones scale down with it. */
.wn-video-sm{
  width:100%;
  max-width:min(280px, 55vw);
}
@media (max-width:639px){
  .wn-video-sm{ max-width:min(220px, 62vw); }
}
@media (hover:hover) and (pointer:fine){
  .wn-video-card:hover{
    border-color:rgba(13,127,242,.45);
    box-shadow:0 24px 48px -28px rgba(13,127,242,.5);
  }
}

/* ---------- Tester page: step sections ------------------------------------ */
/* Each step is a full-width row: copy + button on one side, a media
   placeholder on the other. Stacks to one column on mobile. */
.wn-step-row{
  display:grid;
  gap:2rem;
  align-items:center;
}
@media (min-width:640px){
  .wn-step-row{ grid-template-columns:1.1fr 1fr; }
  .wn-step-row.wn-step-row-rev .wn-step-copy{ order:2; }
  .wn-step-row.wn-step-row-rev .wn-media-placeholder{ order:1; }
}
.wn-step-copy{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
}

/* ---------- Media placeholder ----------------------------------------------
   Generic "not shot yet" slot — stands in for a real video or screenshot
   until the asset exists. Dashed border reads as "empty slot", not a
   broken asset. Reuses .wn-video-card's hover treatment. */
.wn-media-placeholder{
  width:100%;
  max-width:320px;
  aspect-ratio:9/16;
  margin:0 auto;
  background:var(--wn-dim);
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:.75rem;
  text-align:center;
  padding:2rem;
  border-style:dashed;
}
/* Inside a .wn-step-row grid cell the placeholder should fill the whole
   cell — full column width, not capped to a phone-screen size — and fill
   the row's height rather than lock to a phone-screen aspect ratio. */
.wn-step-row .wn-media-placeholder{
  max-width:none;
  aspect-ratio:auto;
  min-height:220px;
  height:100%;
}
/* Standalone use (main page "watch the walkthrough" teaser) needs its own
   bottom margin; inside a .wn-step-row grid cell it must not. */
.wn-media-placeholder.wn-media-placeholder-standalone{
  margin-bottom:4rem;
}

@media (max-width:640px){
  .wn-deck{ height:min(66vh,540px);min-height:380px; }
  .wn-phone{ width:min(58vw,220px); }
  .wn-phone[data-slot="-1"]{ transform:translate(-50%,-50%) translateX(-92%) rotateY(32deg) scale(.7);opacity:.3;filter:brightness(.45); }
  .wn-phone[data-slot="1"]{ transform:translate(-50%,-50%) translateX(92%) rotateY(-32deg) scale(.7);opacity:.3;filter:brightness(.45); }
  .wn-phone[data-slot="-2"],
  .wn-phone[data-slot="2"]{ opacity:0;pointer-events:none; }
  .wn-arrow-prev{ left:-6px; }
  .wn-arrow-next{ right:-6px; }
}

/* ==========================================================================
   Reduced motion
   Fewer and gentler, not zero. Opacity and colour changes still carry
   meaning; everything that MOVES is removed.
   ========================================================================== */
/* Every rule is scoped to :not(.wn-force-motion) so ?motion=full can preview
   the full motion design on a machine that asks for reduced motion. The
   override is opt-in per visit and never applies to normal traffic. */
@media (prefers-reduced-motion:reduce){
  html:not(.wn-force-motion){ scroll-behavior:auto; }

  /* The deck advances on its own, so stripping its transform used to leave
     the phones CUTTING between five positions — more jarring than the slide
     it replaced. Shortened (480ms -> 320ms), not removed. */
  html:not(.wn-force-motion) .wn-phone{
    transition:transform 320ms var(--wn-ease-in-out),
               opacity 320ms ease,
               filter 320ms ease;
  }
  html:not(.wn-force-motion) .wn-caption{ transition:opacity 180ms ease, filter 180ms ease; }
  html:not(.wn-force-motion) .wn-caption.is-swapping{ transform:none; }

  /* Entrances keep a short rise (18px -> 6px) instead of losing it. */
  html:not(.wn-force-motion) .wn-reveal{ transform:translateY(6px); transition-duration:260ms; transition-delay:0ms; }
  html:not(.wn-force-motion) .wn-reveal.is-in{ transform:translateY(0); }
  html:not(.wn-force-motion) .wn-hero-in{ transform:translateY(6px); animation-duration:320ms; animation-delay:0ms; }
  html:not(.wn-force-motion) .wn-cardfx{ transform:translateY(6px) scale(1); transition-duration:260ms; }
  html:not(.wn-force-motion) .wn-cardfx.is-in{ transform:translateY(0) scale(1); }

  /* Hover and press are user-driven and stop the instant the user does, so
     they stay — just smaller, and scaling in place rather than travelling. */
  html:not(.wn-force-motion) .card-hover:hover{ transform:translateY(-2px); }
  html:not(.wn-force-motion) .wn-cardfx:hover{ transform:translateY(-2px); }
  html:not(.wn-force-motion) .wn-cardfx:hover .wn-cardfx-icon{ transform:scale(1.06); }

  /* Two loops deliberately left running: the tab sweep is a progress
     indicator (removing it hides when the deck will advance), and the badge
     dot animates opacity only, which is not movement. */
}

/* Mobile-only / desktop-only line breaks. The prebuilt Tailwind bundle has no
   sm: variants for `hidden`, so these are hand-written. Breakpoint = Tailwind sm. */
br.wn-br-mobile{ display:inline; }
br.wn-br-desktop{ display:none; }
@media (min-width:640px){
  br.wn-br-mobile{ display:none; }
  br.wn-br-desktop{ display:inline; }
}
