/* ============================================================
   Tailored Kenyan — motion layer
   Additive. Loaded after styles.css, which it never overrides.

   Reduced motion is handled globally by styles.css:657-660, which
   kills every animation and transition. Nothing here needs its own
   media query — with one exception, marked below, because
   view-transition pseudo-elements are not matched by that rule.

   Spec: docs/spec/02-motion.md
   ============================================================ */

:root{
  /* Easing. The house curve is lifted from lai.africa so both sites
     move the same way. */
  --tk-ease:        cubic-bezier(.25,.46,.45,.94);
  --tk-ease-exit:   cubic-bezier(.55,.06,.68,.19);
  --tk-ease-drama:  cubic-bezier(.16,1,.3,1);
  --tk-ease-soft:   ease-in-out;

  /* Duration */
  --tk-t-tap:    120ms;
  --tk-t-fast:   200ms;
  --tk-t-hover:  300ms;
  --tk-t-reveal: 460ms;
  --tk-t-head:   540ms;
  --tk-t-hero:   620ms;
  --tk-t-slow:   720ms;
  --tk-t-card:   560ms;
  /* Under 5s on purpose: WCAG 2.2.2 only demands a pause control for
     motion that runs longer. */
  --tk-t-kb:     4500ms;

  /* Stagger */
  --tk-st-tight: 40ms;
  --tk-st-base:  65ms;
  --tk-st-loose: 95ms;

  /* Travel */
  --tk-d-xs:  8px;
  --tk-d-sm: 12px;
  --tk-d-md: 18px;
  --tk-d-lg: 28px;
}

/* Preserve the site's native smooth anchor navigation. Scroll reveals are
   deliberately timed independently, so the page glides without making the
   content feel as though it is lagging behind the visitor. */
html{scroll-behavior:smooth}

/* ---------- reveal initial states ----------
   Applied by JS only to elements that are below the fold at init, so an
   element that is already on screen never flashes and can never become an
   invisible LCP candidate. If JS never runs, nothing is ever hidden. */
[data-tk-reveal]{
  opacity:0;
  transform:translateY(var(--tk-d-md));
  will-change:opacity,transform;
}
[data-tk-reveal="small"]{transform:translateY(var(--tk-d-sm))}
[data-tk-reveal="left"]{transform:translateX(calc(var(--tk-d-lg) * -1))}
[data-tk-reveal="right"]{transform:translateX(var(--tk-d-lg))}
/* Rise-only: opacity is never touched, for text that may be the LCP element. */
[data-tk-reveal="rise"]{opacity:1;transform:translateY(var(--tk-d-sm))}
[data-tk-reveal="done"]{opacity:1;transform:none;will-change:auto}

/* ---------- buttons ---------- */
.btn-primary{transition:background-color var(--tk-t-fast) var(--tk-ease)}
.btn-primary .pip{transition:transform var(--tk-t-hover) var(--tk-ease)}
.btn-primary:hover .pip,
.btn-primary:focus-visible .pip{transform:translateX(3px)}
.btn-primary:active{transform:scale(.985)}

.btn-outline,.chip{transition:border-color var(--tk-t-fast) var(--tk-ease),
  color var(--tk-t-fast) var(--tk-ease),background-color var(--tk-t-fast) var(--tk-ease)}
.btn-outline:active,.chip:active{transform:scale(.97)}

.btn-ghost .ring{transition:transform var(--tk-t-hover) var(--tk-ease)}
.btn-ghost:hover .ring,.btn-ghost:focus-visible .ring{transform:scale(1.08)}

/* ---------- product and category cards ----------
   .veil is deliberately untouched: it carries the caption's contrast. */
.prod img,.cat img{transition:transform var(--tk-t-card) var(--tk-ease)}
.prod:hover img,.prod:focus-within img,
.cat:hover img,.cat:focus-visible img{transform:scale(1.04)}
.prod .cap,.cat .cap{transition:transform var(--tk-t-hover) var(--tk-ease)}
.prod:hover .cap,.prod:focus-within .cap,
.cat:hover .cap,.cat:focus-visible .cap{transform:translateY(-3px)}

/* ---------- nav ----------
   Underline draws in the colour the active state already uses, so the
   palette gains nothing new. Suppressed on .on, which has a real border. */
.navlinks a{position:relative}
.navlinks a::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:var(--wine-mid);transform:scaleX(0);transform-origin:left;
  transition:transform var(--tk-t-fast) var(--tk-ease);
}
.navlinks a:hover::after,.navlinks a:focus-visible::after{transform:scaleX(1)}
.navlinks a.on::after{display:none}

.textlink span,.story-panel a i{
  display:inline-block;transition:transform var(--tk-t-hover) var(--tk-ease);
}
.textlink:hover span,.textlink:focus-visible span,
.story-panel a:hover i,.story-panel a:focus-visible i{transform:translateX(3px)}

/* ---------- controls ---------- */
.circ,.arrows button,.slides button,.news button{
  transition:transform var(--tk-t-hover) var(--tk-ease),
    background-color var(--tk-t-fast) var(--tk-ease),
    color var(--tk-t-fast) var(--tk-ease),border-color var(--tk-t-fast) var(--tk-ease);
}
.circ:hover,.circ:focus-visible,
.arrows button:hover,.arrows button:focus-visible{transform:scale(1.06)}
.circ:active,.arrows button:active,.slides button:active{transform:scale(.92)}
.news button:hover,.news button:focus-visible{transform:scale(1.06)}
.news button:active{transform:scale(.94)}

/* ---------- form fields ---------- */
.field,.news form{position:relative}
.field input,.field textarea,.field select{
  transition:border-color var(--tk-t-fast) var(--tk-ease);
}

/* ---------- mobile menu ---------- */
/* The panel itself must not fade: while it did, the hero photo showed straight
   through the links and "Contact" was unreadable for the whole 220ms. The
   sheet is opaque from the first frame; only the links animate in. */
.navlinks.open > a{animation:tk-menu-item 220ms var(--tk-ease) both}
.navlinks.open > a:nth-child(1){animation-delay:30ms}
.navlinks.open > a:nth-child(2){animation-delay:60ms}
.navlinks.open > a:nth-child(3){animation-delay:90ms}
.navlinks.open > a:nth-child(4){animation-delay:120ms}
.navlinks.open > a:nth-child(5){animation-delay:150ms}
.navlinks.open > a:nth-child(6){animation-delay:180ms}
.navlinks.open > a:nth-child(7){animation-delay:210ms}
@keyframes tk-menu-item{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

/* ---------- hero Ken Burns ----------
   The garment drifts a little while it is on screen. Only the active slide
   moves, and it finishes well inside 5s. */
.slidewrap img.on{animation:tk-kenburns var(--tk-t-kb) var(--tk-ease-soft) both}
@keyframes tk-kenburns{from{transform:scale(1)}to{transform:scale(1.05)}}

/* ---------- M-PESA waiting state ----------
   The most anxious 90 seconds on the site. This says "we are alive and we are
   calling your phone" — a resting heart rate, not an urgent pulse. The
   existing 1s spinner in styles.css is left exactly as it is. */
.disc.wait{animation:tk-breath 2400ms var(--tk-ease-soft) infinite}
.disc.wait::after{
  content:"";position:absolute;inset:-1px;border-radius:50%;
  border:1px solid var(--rule-2);pointer-events:none;
  animation:tk-signal 2400ms var(--tk-ease-soft) infinite;
}
@keyframes tk-breath{0%,100%{transform:scale(1)}50%{transform:scale(1.03)}}
@keyframes tk-signal{from{transform:scale(1);opacity:.5}to{transform:scale(1.5);opacity:0}}

/* Digits must not shift width as the countdown ticks. */
.ticker{font-variant-numeric:tabular-nums}

/* One soft headshake on the disc alone when a payment genuinely failed.
   A person saying "hm, no" — never an alarm, and never on a form field. */
.disc.bad.tk-shake{animation:tk-shake 320ms var(--tk-ease) 1}
@keyframes tk-shake{
  0%,100%{transform:translateX(0)}
  25%{transform:translateX(-4px)}
  60%{transform:translateX(4px)}
  85%{transform:translateX(-2px)}
}

/* ---------- add to cart ----------
   The button becomes its own receipt. No image flies across the page: the
   cart target is a ~60px text label on mobile, and cloning an image into a
   fixed layer is the jankiest thing you can do on a mid-range Android in the
   one moment where jank costs a sale. */
.buybox .btn-primary .pip,
.prod-add .pip{transition:transform 120ms var(--tk-ease-exit)}
.btn-primary.tk-added .pip{animation:tk-pip-pop 300ms var(--tk-ease) both}
@keyframes tk-pip-pop{0%{transform:scale(0)}60%{transform:scale(1.15)}100%{transform:scale(1)}}

/* ---------- cart line removal ----------
   The one sanctioned layout animation. It is user-initiated, so it falls
   inside the CLS input-exclusion window, and collapsing beats content
   teleporting up the page. */
.line.tk-removing{
  animation:tk-line-out 200ms var(--tk-ease-exit) both;
  overflow:hidden;
}
@keyframes tk-line-out{to{opacity:0;transform:translateX(-12px)}}

/* ---------- page transitions ----------
   Cross-document View Transitions are deliberately NOT enabled.

   They worked on discover -> product, but were skipped on every route through
   the cart and checkout, because those pages' controllers rewrite the DOM on
   load and the browser abandons the transition mid-snapshot. A skipped
   transition rejects, surfacing as an uncaught "Transition was skipped" on
   exactly the navigations that matter most — the buying funnel — where the
   console needs to look clean and error tracking must not fill with noise.

   Making it work means deferring each page controller's first render until
   after the pagereveal event. That is real coupling between the motion layer
   and every page's data loading, bought for a 240ms crossfade. Not worth it.

   Everything else in this file — the reveals, the micro-interactions, the
   payment-state choreography — is unaffected. */

/* ---------- pre-launch layout fixes ---------- */

/* D1: bespoke.html's measurements form is far taller than its aside,
   leaving the whole right column blank for the scroll depth of the form.
   Scoped to bespoke only so cart.html, which shares .split and was not
   reported as broken, is unaffected. Mobile already collapses .split to a
   single column (styles.css ~619), so this rule has nothing to do there. */
body[data-page="bespoke"] .split .aside{align-self:start;position:sticky;top:20px}

/* D2: checkout.html has the same class of bug between the delivery form and
   the order summary. checkout.html shares data-page="cart" with cart.html
   (not reported as broken), so this is scoped to the #state-form id that
   only checkout.html's .split carries. */
#state-form .aside{align-self:start;position:sticky;top:20px}

/* F2: the hero carousel's pause control (#heroPause) was styled identically
   to the prev/next circles, making it too easy to miss mid-caption.
   Reduced motion already hides this button entirely (index.html JS), so no
   media query is needed here. */
#heroPause{background:var(--wine);border-color:var(--gold)}

/* D3: the sitewide WhatsApp control (injected by chrome.js into every page's
   <body>) was only styled in commerce.css, which 7 of the 13 pages that load
   chrome.js never link. motion.css is the one additive stylesheet all those
   pages already load, so the rule lives here. The green circle and white help
   label are the client's approved WhatsApp treatment. */
.wa-float-wrap{
  position:fixed;right:20px;bottom:20px;z-index:40;
  display:flex;align-items:center;gap:12px;
  opacity:0;transform:translateY(var(--tk-d-sm));
  animation:tk-wa-in var(--tk-t-reveal) var(--tk-ease) 400ms both;
}
.wa-float-label{
  padding:15px 24px;border-radius:7px;background:#fff;color:#4a4a4a;
  box-shadow:0 8px 24px rgba(30,18,16,.18);
  font:16px/1.2 'Jost',sans-serif;white-space:nowrap
}
.wa-float{
  width:64px;height:64px;border-radius:50%;
  display:grid;place-items:center;
  background:#25D366;color:#fff;
  box-shadow:0 12px 28px rgba(30,18,16,.28);
  flex:0 0 auto;
}
.wa-float:hover,.wa-float:focus-visible{background:#1ebe5d}
.wa-float svg{width:32px;height:32px;fill:currentColor}
@keyframes tk-wa-in{to{opacity:1;transform:none}}
@media (max-width:768px){
  .wa-float-wrap{right:14px;bottom:14px;gap:8px}
  .wa-float-label{padding:12px 14px;font-size:13px}
  .wa-float{width:54px;height:54px}
  .wa-float svg{width:27px;height:27px}
}
@media (prefers-reduced-motion:reduce){
  .wa-float-wrap{animation:none;opacity:1;transform:none}
}

/* ---------- bespoke availability ----------
   The service remains built in the API for later activation, but the client
   is not taking bespoke work yet. Keep the preview visible while making its
   status unmistakable at every customer-facing entry point. */
.nav-status,.coming-soon-tag{display:inline-block;margin-left:8px;color:var(--muted);font-size:9px;letter-spacing:.12em}
.coming-soon-tag{margin-left:10px}
.bespoke.is-coming-soon .bespoke-img{filter:grayscale(1);opacity:.6}
.bespoke.is-coming-soon .bespoke-copy{background:#e6e2dd}
.bespoke.is-coming-soon .steps{opacity:.68}
.coming-soon-stamp{position:absolute;left:50%;top:50%;z-index:2;transform:translate(-50%,-50%) rotate(-6deg);padding:12px 18px;border:1px solid rgba(180,137,79,.75);background:rgba(27,22,20,.82);color:var(--gold-lt);font-size:11px;letter-spacing:.28em;white-space:nowrap}
.bespoke.is-coming-soon .btn-primary.is-disabled{background:#8f8b85;color:#f5f1eb;cursor:not-allowed;opacity:.78}
.coming-soon-notice{display:flex;align-items:baseline;gap:14px;margin-top:24px;padding:16px 18px;border-left:2px solid var(--gold);background:var(--band-2)}
.coming-soon-notice .kicker{flex:none;margin:0;color:var(--wine-2);font-size:11px}
.coming-soon-notice p{margin:0;font-size:13px;line-height:1.7;color:var(--body)}
.coming-soon-panel{background:var(--band-2)}
@media (max-width:1000px){
  .nav-status{display:block;margin:3px 0 0;font-size:8px}
  .coming-soon-notice{display:block}
  .coming-soon-notice .kicker{margin-bottom:6px}
}

/* ---------- newsletter sign-up modal ----------
   Additive sitewide capture. The image uses existing approved brand
   photography; the offer mirrors the live announcement bar: FIRST300 for
   KSh 300 off a first order. */
.tk-newsletter-modal{
  width:min(780px,calc(100vw - 32px));
  max-width:none;max-height:min(640px,calc(100vh - 32px));
  padding:0;border:0;border-radius:4px;overflow:hidden;
  background:var(--card);color:var(--ink);box-shadow:0 30px 80px rgba(30,18,16,.34)
}
.tk-newsletter-modal::backdrop{background:rgba(27,22,20,.62)}
.tk-newsletter-modal[open]{animation:tk-newsletter-in var(--tk-t-reveal) var(--tk-ease) both}
.tk-newsletter-popup{display:grid;grid-template-columns:1fr 1fr;min-height:430px}
.tk-newsletter-media{min-height:430px;background:var(--band-3);overflow:hidden}
.tk-newsletter-media img{width:100%;height:100%;object-fit:cover;object-position:center 24%}
.tk-newsletter-content{position:relative;display:flex;flex-direction:column;justify-content:center;padding:44px 42px;background:var(--band-2)}
.tk-newsletter-content .kicker{margin-bottom:18px;font-size:11px;color:var(--muted-2)}
.tk-newsletter-content h2{margin:0 0 18px;font-size:clamp(30px,4vw,42px);line-height:1.02;font-weight:500;color:var(--ink-dk-2)}
.tk-newsletter-content h2 em{font-style:italic;color:var(--wine-2)}
.tk-newsletter-content > p{max-width:270px;margin:0 0 25px;font-size:14px;line-height:1.75;color:var(--body)}
.tk-newsletter-form{display:flex;flex-direction:column;gap:12px}
.tk-newsletter-form input[type=email]{width:100%;min-height:48px;padding:12px 14px;border:1px solid var(--rule-2);background:var(--cream-lt);font:14px 'Jost',sans-serif;color:var(--ink);outline:none}
.tk-newsletter-form input[type=email]:focus{border-color:var(--wine-br);box-shadow:0 0 0 2px rgba(154,37,48,.14)}
.tk-newsletter-form .btn-primary{width:100%;justify-content:space-between;padding:13px 16px}
.tk-newsletter-form .consent{margin:0;font-size:11px;line-height:1.55}
.tk-newsletter-message{margin:14px 0 0!important;color:var(--wine-2)!important;font-size:13px!important;line-height:1.7!important}
.tk-newsletter-message.is-error{color:var(--wine-2)!important}
.tk-newsletter-dismiss{align-self:flex-start;margin:16px 0 0;padding:0;border:0;background:transparent;color:var(--muted);font:12px 'Jost',sans-serif;cursor:pointer}
.tk-newsletter-dismiss:hover,.tk-newsletter-dismiss:focus-visible{color:var(--wine-2)}
.tk-newsletter-close{position:absolute;top:13px;right:14px;width:34px;height:34px;border:0;background:transparent;color:var(--ink);font-size:26px;font-weight:300;line-height:1;cursor:pointer}
@keyframes tk-newsletter-in{from{opacity:0;transform:translateY(14px) scale(.985)}to{opacity:1;transform:none}}
@media (max-width:600px){
  .tk-newsletter-modal{width:calc(100vw - 24px);max-height:calc(100vh - 24px)}
  .tk-newsletter-popup{display:block;min-height:0;max-height:calc(100vh - 24px);overflow:auto}
  .tk-newsletter-media{height:190px;min-height:0}
  .tk-newsletter-media img{object-position:center 22%}
  .tk-newsletter-content{padding:30px 24px 26px}
  .tk-newsletter-content h2{font-size:34px}
}
@media (prefers-reduced-motion:reduce){
  .tk-newsletter-modal[open]{animation:none}
}
