/* ==========================================================================
   Motion tokens — one scale for the whole site. Never hand-pick a duration.
   ========================================================================== */
:root {
  --dur-instant: 100ms;
  --dur-fast:    160ms;
  --dur-base:    240ms;
  --dur-slow:    400ms;
  --dur-slower:  600ms;
  --dur-exit-base: 160ms;

  --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:     cubic-bezier(0.4, 0, 1, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-linear: linear;

  --stagger: 40ms;
  --travel-sm: 8px;
  --travel-md: 16px;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-instant: 1ms; --dur-fast: 1ms; --dur-base: 1ms;
    --dur-slow: 1ms; --dur-slower: 1ms; --dur-exit-base: 1ms;
    --stagger: 0ms; --travel-sm: 0px; --travel-md: 0px;
  }
  *, *::before, *::after {
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* Whoscall Premium claim page — Whoscall brand (Malachite green) */
:root {
  --green: #0CD25F;        /* Whoscall Malachite — logo green */
  --green-dark: #09B953;
  --green-deep: #078E40;   /* readable green for headings/links on white */
  --green-ink: #045426;    /* dark green text on green surfaces */
  --green-tint: #E6FAEF;   /* malachite lightest */
  --green-line: #CEF6DF;   /* malachite lighter */
  --ink: #262626;          /* neutral darkest */
  --muted: #6E6C6C;        /* neutral */
  --line: #F0EFEF;         /* neutral lighter */
  --bg-soft: #F6F5F5;      /* neutral lightest */
  --blue: #1AA6ED;         /* picton blue accent */
  --blue-tint: #E8F6FD;
  --gold: #F8D008;         /* supernova — premium accent */
  --gold-ink: #4A3E02;
  --danger: #ED3537;
  --font-head: 'Nunito', 'Noto Sans Thai', system-ui, sans-serif;
  --font-body: 'Nunito', 'Noto Sans Thai', system-ui, sans-serif;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-body);
  color: var(--ink);
  background: linear-gradient(165deg, #09B953 0%, #0CD25F 55%, #05F067 130%);
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Concentric ripple motif */
.bg-decor { position: fixed; inset: 0; overflow: hidden; pointer-events: none; }
.ring {
  position: absolute;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 0 0 40px rgba(255, 255, 255, 0.06), 0 0 0 90px rgba(255, 255, 255, 0.04);
}
.ring-1 { width: 320px; height: 320px; top: -120px; right: -100px; }
.ring-2 { width: 260px; height: 260px; bottom: -90px; left: -80px; }

.shell {
  position: relative;
  width: 100%;
  max-width: 480px;
  padding: 24px 16px 32px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.brand {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: 8px;
}
.brand-logo { height: 34px; max-width: 100%; display: block; }
.claim-brand { height: 40px; max-width: 100%; display: block; margin: 0 auto 12px; }

.card {
  background: #fff;
  border-radius: 20px;
  padding: 28px 24px;
  box-shadow: 0 24px 60px rgba(4, 84, 38, 0.30);
}

h1 {
  font-family: var(--font-head);
  font-size: 24px;
  font-weight: 800;
  color: var(--green-deep);
  margin: 0 0 8px;
  line-height: 1.35;
  text-align: center;
}

.sub, .details {
  font-size: 15px;
  color: var(--muted);
  line-height: 1.7;
  margin: 0 0 20px;
  white-space: pre-line;
  text-align: center;
}

.field-label {
  display: block;
  font-size: 14px;
  font-weight: 700;
  margin-bottom: 8px;
}

input[type="text"], input[type="password"] {
  width: 100%;
  padding: 14px 16px;
  font-size: 18px;
  font-family: var(--font-body);
  border: 2px solid var(--line);
  border-radius: 12px;
  outline: none;
  transition: border-color var(--dur-fast) var(--ease-out);
  letter-spacing: 1px;
}
input[type="text"]:focus, input[type="password"]:focus { border-color: var(--green); }

.ts-widget { margin: 16px 0 4px; min-height: 0; text-align: center; }
.ts-widget:empty { margin: 0; }
.ts-widget > div { margin-left: auto !important; margin-right: auto !important; }
.ts-widget iframe { margin: 0 auto; }

.btn {
  display: block;
  width: 100%;
  padding: 15px 20px;
  margin-top: 14px;
  font-family: var(--font-head);
  font-size: 17px;
  font-weight: 800;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-instant) var(--ease-out);
}
.btn:active { transform: scale(0.97); }
.btn-primary { background: var(--green); color: var(--green-ink); }
.btn-primary:hover { background: var(--green-dark); }
.btn-primary:disabled { background: var(--green-line); color: var(--muted); cursor: wait; }
.btn-ghost {
  background: #fff;
  color: var(--green-deep);
  border: 2px solid var(--green-deep);
}
.btn-ghost:hover { background: var(--green-tint); }

.error {
  margin: 14px 0 0;
  padding: 12px 14px;
  background: #FDEDED;
  color: var(--danger);
  border-radius: 10px;
  font-size: 14px;
  line-height: 1.6;
}

.camp-note {
  margin: -4px 0 18px;
  padding: 12px 14px;
  background: var(--blue-tint);
  border: 1.5px solid #B5E4FB;
  border-radius: 12px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--ink);
  text-align: center;
  line-height: 1.65;
}

.redeem-guide {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 14px;
  margin-top: 18px;
  border: 1px solid var(--line);
}

.camp-terms {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  text-align: left;
}
.camp-terms-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 6px;
}
.camp-terms p {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.7;
  margin: 0;
  white-space: pre-line;
}

.claim-package {
  font-family: var(--font-head);
  font-size: 17px;
  font-weight: 800;
  color: var(--ink);
  text-align: center;
  margin: -2px 0 12px;
}

.notice {
  margin: 0 0 16px;
  padding: 13px 15px;
  border-radius: 12px;
  font-size: 14.5px;
  line-height: 1.65;
  background: var(--bg-soft);
  color: var(--ink);
  border: 1px solid var(--line);
}
.notice.ok { background: var(--green-tint); border-color: var(--green-line); }
.notice.warn { background: #FEF5E6; border-color: #FEECCE; }

/* Claimed code card */
.claim-card {
  margin-top: 18px;
  border: 2px solid var(--green-line);
  border-radius: 16px;
  padding: 20px 18px;
  text-align: center;
  background: #fff;
}
.claim-card .qr {
  width: 172px;
  height: 172px;
  margin: 4px auto 14px;
  padding: 10px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
}
.claim-card .qr svg { width: 100%; height: 100%; display: block; }
.code-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 6px;
}
.code-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.code-value {
  font-family: var(--font-head);
  font-size: 26px;
  font-weight: 800;
  letter-spacing: 3px;
  color: var(--ink);
  background: var(--green-tint);
  border: 2px dashed var(--green-deep);
  border-radius: 10px;
  padding: 8px 14px;
  word-break: break-all;
}
.copy-btn {
  flex-shrink: 0;
  padding: 9px 14px;
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--green-deep);
  background: #fff;
  border: 1.5px solid var(--green-deep);
  border-radius: 9px;
  cursor: pointer;
}
.copy-btn:hover { background: var(--green-tint); }
.copy-btn.copied { color: var(--green-ink); border-color: var(--green); background: var(--green-tint); }

.deep-link {
  display: block;
  padding: 13px 16px;
  margin: 4px 0 10px;
  background: var(--green);
  color: var(--green-ink);
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 15.5px;
  border-radius: 11px;
  text-decoration: none;
}
.deep-link:hover { background: var(--green-dark); }
.link-row {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: center;
}
.link-text {
  font-size: 12px;
  color: var(--muted);
  word-break: break-all;
  max-width: 260px;
}
.claimed-at { font-size: 12px; color: var(--muted); margin-top: 10px; }

.fallback { margin-top: 20px; border-top: 1px solid var(--line); padding-top: 10px; text-align: center; }
.hint { font-size: 12.5px; color: var(--muted); line-height: 1.6; margin: 0 0 8px; }
.fallback-btn {
  display: inline-block;
  padding: 8px 18px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  background: #fff;
  border: 1.5px solid #D9D8D8;
  border-radius: 999px;
  text-decoration: none;
}
.fallback-btn:hover { color: var(--green-deep); border-color: var(--green-deep); background: var(--green-tint); }

.linklike {
  display: block;
  margin: 18px auto 0;
  background: none;
  border: none;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 13.5px;
  text-decoration: underline;
  cursor: pointer;
}
.linklike:hover { color: var(--green-deep); }

@media (max-width: 380px) {
  .card { padding: 22px 16px; }
  .code-value { font-size: 21px; letter-spacing: 2px; }
  .brand-logo { height: 26px; }
}


/* ==========================================================================
   Motion
   Each block below answers "what does this tell the user that a static frame
   cannot?" Anything that could not answer it was left static on purpose:
   the background rings never loop (peripheral looping motion), and campaign
   details / T&C never animate (never animate body text).
   ========================================================================== */

/* Continuity: the card has arrived. Runs once per view switch, not per render. */
.view-in { animation: view-in var(--dur-slow) var(--ease-out) both; }
@keyframes view-in {
  from { opacity: 0; transform: translateY(var(--travel-md)); }
  to   { opacity: 1; transform: none; }
}

/* Attention: this message was not here a moment ago. */
.msg-in { animation: msg-in var(--dur-base) var(--ease-out) both; }
@keyframes msg-in {
  from { opacity: 0; transform: translateY(calc(var(--travel-sm) * -1)); }
  to   { opacity: 1; transform: none; }
}

/* The hero moment: the code the user just won.
   Only ever applied to a code the user has not seen before. */
.claim-card.is-new {
  animation: prize-in var(--dur-slow) var(--ease-spring) both;
  transform-origin: center top;
}
@keyframes prize-in {
  from { opacity: 0; transform: scale(0.96) translateY(var(--travel-md)); }
  to   { opacity: 1; transform: none; }
}

/* Contents arrive just behind the card, in reading order. Delay is capped so
   the last row never straggles. */
.claim-card.is-new > * { animation: item-in var(--dur-base) var(--ease-out) both; }
.claim-card.is-new > *:nth-child(1) { animation-delay: calc(var(--stagger) * 3); }
.claim-card.is-new > *:nth-child(2) { animation-delay: calc(var(--stagger) * 4); }
.claim-card.is-new > *:nth-child(3) { animation-delay: calc(var(--stagger) * 5); }
.claim-card.is-new > *:nth-child(4) { animation-delay: calc(var(--stagger) * 6); }
.claim-card.is-new > *:nth-child(n+5) { animation-delay: calc(var(--stagger) * 7); }
@keyframes item-in {
  from { opacity: 0; transform: translateY(var(--travel-sm)); }
  to   { opacity: 1; transform: none; }
}

/* Status: the request is still in flight. The button label already says so in
   Thai, so under reduced motion the spinner simply goes away. */
.btn-spinner {
  display: inline-block;
  width: 1em;
  height: 1em;
  margin-right: 8px;
  vertical-align: -0.14em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s var(--ease-linear) infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .btn-spinner { display: none; }
}

/* Causality: the copy landed. */
.copy-btn { transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), transform var(--dur-instant) var(--ease-out); }
.copy-btn:active { transform: scale(0.94); }
.copy-btn.copied { animation: pop var(--dur-base) var(--ease-spring); }
@keyframes pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}

/* The primary redeem CTA is the one thing on the page worth reaching for. */
.deep-link { transition: background var(--dur-fast) var(--ease-out), transform var(--dur-instant) var(--ease-out); }
.deep-link:active { transform: scale(0.98); }
.fallback-btn { transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out); }

/* App download CTA. The brand name is the wordmark asset, never typed text —
   dark ground so it reads as a distinct action and never competes with the
   green redeem button, which stays the one primary CTA on the page. */
.btn-download {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin-top: 18px;
  background: var(--ink);
  color: #fff;
}
.btn-download:hover { background: #3D3D3D; }
.btn-wordmark { height: 19px; width: auto; display: block; }

/* Download block on the entry page: separated from the form so it never reads
   as a step of it, and never competes with the green submit button. */
.download-block {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  text-align: center;
}
.download-block .hint { margin: 0 0 2px; }
.download-block .btn-download { margin-top: 10px; }
