/* public/css/verify.css
   Self-contained - deliberately doesn't depend on the admin panel's
   style.css or appeal.css. Same design tokens as public/status/index.html
   so /verify reads as part of the same public site, not a bolted-on
   utility page. */

:root{
  --background:#000000; --foreground:#fafafa; --card:#0a0a0a;
  --primary:#fafafa; --primary-fg:#161616; --secondary:#111111;
  --muted-fg:#a1a1a1; --border:rgba(255,255,255,.10);
  --radius:.75rem;
  --shadow-glow:0 0 40px -8px rgba(255,255,255,.25),0 0 80px -20px rgba(255,255,255,.25);
  --shadow-card:0 1px 0 0 rgba(255,255,255,.04) inset,0 20px 40px -20px rgba(0,0,0,.8);
  --green:#34c759; --red:#ff3b30;
  --discord:#5865F2; --steam:#66c0f4;
}
*{box-sizing:border-box;margin:0;padding:0;border-color:var(--border)}
html,body{height:100%}
/* html itself needs the same background as body - without this, a page
   shorter than the viewport (or a mobile browser where 100vh doesn't
   quite match the real visible height once its address bar
   shows/hides) leaves a sliver of the browser's default background
   showing below body. */
html{background:var(--background)}
body{
  background:var(--background);color:var(--foreground);
  font-family:"Inter",ui-sans-serif,system-ui,sans-serif;
  -webkit-font-smoothing:antialiased;
  min-height:100vh;min-height:100dvh;display:flex;flex-direction:column;
  background-image:radial-gradient(ellipse 80% 50% at 50% -10%, rgba(255,255,255,.06), transparent);
}
h1,h2{font-family:"Space Grotesk",ui-sans-serif,system-ui,sans-serif;letter-spacing:-.02em}
a{text-decoration:none;color:inherit}
img{display:block}

/* --- Header, shared look with the status page --- */
header.site{border-bottom:1px solid var(--border);position:sticky;top:0;z-index:10;background:rgba(22,22,22,.7);backdrop-filter:blur(16px)}
header.site .bar{max-width:80rem;margin:0 auto;padding:0 1.5rem;height:4rem;display:flex;align-items:center;justify-content:space-between;gap:16px}
.brand{display:flex;align-items:center;gap:.625rem}
.brand img{height:2rem;width:2rem;border-radius:.375rem}
.brand span{font-family:"Space Grotesk";font-weight:700;font-size:.875rem;text-transform:uppercase;letter-spacing:-.02em}
@media(max-width:640px){.brand span{display:none}}

.btn{display:inline-flex;align-items:center;gap:.5rem;border-radius:.5rem;font-size:.875rem;font-weight:600;transition:all .15s cubic-bezier(.16,1,.3,1);padding:.6rem 1rem;cursor:pointer;border:1px solid transparent}
.btn-sm{padding:.5rem .875rem;font-size:.8125rem}
.btn-outline{background:transparent;color:var(--foreground);border-color:var(--border)}
.btn-outline:hover{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.22);transform:translateY(-1px)}
.btn-outline:active{transform:translateY(0) scale(.97)}

/* --- Entrance animation - same fade+rise the status page uses (see
   public/status/index.html's .reveal/.reveal.in and app.js's
   IntersectionObserver stagger). Elements start hidden via inline
   opacity:0/translateY offset here, then verify.js flips .in on once
   each one scrolls into view (in practice, on load, since this whole
   page sits above the fold) - staggered per-element via data-delay so
   the hero copy, card, and footer settle in one after another instead
   of popping in all at once. */
.reveal{opacity:0;transform:translate3d(0,16px,0);transition:opacity .6s cubic-bezier(.16,1,.3,1),transform .6s cubic-bezier(.16,1,.3,1)}
.reveal.in{opacity:1;transform:none}

/* --- Hero --- */
main{flex:1;width:100%;max-width:560px;margin:0 auto;padding:48px 20px 64px}
.hero{position:relative;text-align:center;margin-bottom:32px}
.hero .glow{
  position:absolute;left:50%;top:-40px;width:520px;height:320px;
  transform:translateX(-50%);pointer-events:none;z-index:-1;
  background:radial-gradient(ellipse 70% 60% at 50% 0%, rgba(255,255,255,.08), transparent 72%);
  animation:glowDrift 7s ease-in-out infinite;
}
@keyframes glowDrift{
  0%,100%{opacity:.85;transform:translateX(-50%) scale(1)}
  50%{opacity:1;transform:translateX(-50%) scale(1.06)}
}
.kicker{
  font-family:"JetBrains Mono",ui-monospace,monospace;font-size:11.5px;letter-spacing:.14em;
  color:var(--muted-fg);text-transform:uppercase;margin-bottom:12px;font-weight:600;
}
.hero h1{
  font-size:clamp(30px,7vw,44px);font-weight:700;letter-spacing:-.03em;line-height:1.1;
  padding-bottom:.25rem;margin-bottom:10px;text-transform:uppercase;
  background:linear-gradient(180deg,#fff,#b4b7c0);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.hero .lead{color:var(--muted-fg);font-size:14.5px;line-height:1.6;max-width:440px;margin:0 auto}
.hero .lead-note{
  color:var(--muted-fg);font-size:12.5px;line-height:1.5;max-width:420px;margin:10px auto 0;
  padding:.6rem .9rem;border-radius:.5rem;background:rgba(255,255,255,.04);border:1px solid var(--border);
}
.hero .lead-note code{
  font-family:"JetBrains Mono",ui-monospace,monospace;font-size:11.5px;
  background:rgba(255,255,255,.08);border-radius:.3rem;padding:.1rem .35rem;
}

/* --- Verify card + provider rows --- */
.card-stack{position:relative}
.verify-card{
  background:var(--card);border:1px solid var(--border);border-radius:1rem;
  box-shadow:var(--shadow-card);padding:8px;
  transition:opacity .35s cubic-bezier(.16,1,.3,1),transform .35s cubic-bezier(.16,1,.3,1),filter .35s cubic-bezier(.16,1,.3,1);
}
.provider-row{
  display:flex;align-items:center;gap:14px;padding:16px 12px;
  border-bottom:1px solid var(--border);
  transition:background .2s cubic-bezier(.16,1,.3,1);
}
.provider-row:last-of-type{border-bottom:none}
.provider-row.linked{background:rgba(52,199,89,.05);animation:rowLinkedIn .5s cubic-bezier(.16,1,.3,1)}
@keyframes rowLinkedIn{
  0%{background:rgba(52,199,89,.22)}
  100%{background:rgba(52,199,89,.05)}
}

.provider-icon{
  width:42px;height:42px;border-radius:10px;flex:0 0 auto;
  display:flex;align-items:center;justify-content:center;
  transition:transform .2s cubic-bezier(.16,1,.3,1);
}
.provider-row:hover .provider-icon{transform:scale(1.06)}
.provider-icon svg{width:22px;height:22px}
.icon-discord{background:rgba(88,101,242,.16);color:var(--discord)}
.icon-steam{background:rgba(102,192,244,.14);color:var(--steam)}

.provider-info{flex:1;min-width:0}
.provider-name{font-weight:600;font-size:14.5px;margin-bottom:2px}
.provider-desc{font-size:12.5px;color:var(--muted-fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .2s}
.provider-row.linked .provider-desc{color:var(--green)}

.provider-check{
  display:none;flex:0 0 auto;width:22px;height:22px;border-radius:50%;
  background:var(--green);color:#04170a;align-items:center;justify-content:center;
  transform:scale(0);
}
.provider-check svg{width:13px;height:13px}
.provider-row.linked .provider-check{display:flex;animation:checkPop .45s cubic-bezier(.34,1.56,.64,1) forwards}
@keyframes checkPop{
  0%{transform:scale(0);opacity:0}
  60%{transform:scale(1.15);opacity:1}
  100%{transform:scale(1);opacity:1}
}
.provider-row.linked .provider-btn{display:none}

.provider-btn{
  flex:0 0 auto;padding:.55rem 1rem;border-radius:.5rem;font-size:.8125rem;font-weight:600;
  background:var(--secondary);color:var(--foreground);border:1px solid var(--border);cursor:pointer;
  transition:all .15s cubic-bezier(.16,1,.3,1);
}
.provider-row[data-provider="discord"] .provider-btn:hover{background:var(--discord);border-color:var(--discord);color:#fff;transform:translateY(-1px)}
.provider-row[data-provider="steam"] .provider-btn:hover{background:var(--steam);border-color:var(--steam);color:#04202e;transform:translateY(-1px)}
.provider-btn:active{transform:translateY(0) scale(.96)}
.provider-btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}

.partial-warning{
  margin-top:14px;padding:.75rem .9rem;border-radius:.65rem;
  background:rgba(245,166,35,.08);border:1px solid rgba(245,166,35,.28);
  animation:warningIn .4s cubic-bezier(.16,1,.3,1) both;
  transform-origin:top center;
}
@keyframes warningIn{
  from{opacity:0;transform:translateY(-8px) scaleY(.92)}
  to{opacity:1;transform:none}
}
.partial-warning-text{color:#f5a623;font-size:12.5px;line-height:1.5;margin:0 0 8px}
.partial-accept{display:flex;align-items:flex-start;gap:.55rem;cursor:pointer;font-size:12.5px;color:var(--muted-fg);line-height:1.45}
.partial-accept input[type="checkbox"]{margin-top:2px;accent-color:#f5a623;width:16px;height:16px;flex-shrink:0;cursor:pointer}

.finish-btn{
  position:relative;width:100%;margin-top:12px;padding:.9rem 1rem;border-radius:.65rem;
  background:var(--primary);color:var(--primary-fg);font-weight:700;font-size:14.5px;
  border:none;cursor:pointer;transition:all .2s cubic-bezier(.16,1,.3,1);
  display:flex;align-items:center;justify-content:center;
}
.finish-btn:hover:not(:disabled){box-shadow:var(--shadow-glow);transform:translateY(-1px)}
.finish-btn:active:not(:disabled){transform:translateY(0) scale(.99)}
.finish-btn:disabled{opacity:.35;cursor:not-allowed;background:var(--secondary);color:var(--muted-fg)}
.finish-btn-spinner{display:none;width:16px;height:16px;border-radius:50%;
  border:2px solid rgba(22,22,22,.25);border-top-color:var(--primary-fg);
  animation:spin .7s linear infinite;
}
.finish-btn.is-loading .finish-btn-label{opacity:0}
.finish-btn.is-loading .finish-btn-spinner{display:block;position:absolute}
@keyframes spin{to{transform:rotate(360deg)}}

.finish-hint{color:var(--muted-fg);font-size:12.5px;margin:8px 4px 0;text-align:center;transition:opacity .2s}
.finish-hint:empty{display:none}
.verify-error{color:var(--red);font-size:13px;margin:10px 4px 2px;min-height:0;text-align:center}
.verify-error:empty{display:none}
.verify-error.shake{animation:errorShake .4s cubic-bezier(.36,.07,.19,.97)}
@keyframes errorShake{
  10%,90%{transform:translate3d(-1px,0,0)}
  20%,80%{transform:translate3d(2px,0,0)}
  30%,50%,70%{transform:translate3d(-4px,0,0)}
  40%,60%{transform:translate3d(4px,0,0)}
}

/* --- Cloudflare Turnstile widget - see lib/turnstile.js. Only ever
   renders anything when the operator has turned it on for this page
   (Community Settings -> Turnstile -> "Show on the public /verify sign-
   in page"); the :empty rule below collapses the space entirely when
   it's off, same pattern as appeal.css's .appeal-turnstile. */
.verify-turnstile{margin:14px 0 0;display:flex;justify-content:center}
.verify-turnstile:empty{display:none;margin:0}

/* --- Sign-in/out rate limit notice - see lib/publicVerifyRoutes.js's
   signInOutLimiter. Hidden by default; verify.js adds .rate-limited to
   #verifyCard once the visitor has used up their budget (either
   discovered on page load via GET /status, or from a fresh attempt
   bouncing off the limiter), which is what actually swaps this in for
   the provider rows/turnstile/finish button below - see the sibling
   rule right after it. */
.rate-limit-notice{display:none;text-align:center;padding:28px 12px 8px}
.verify-card.rate-limited>*:not(.rate-limit-notice){display:none}
.verify-card.rate-limited .rate-limit-notice{display:block;animation:fadeUp .4s cubic-bezier(.16,1,.3,1) both}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.rate-limit-icon{
  width:46px;height:46px;margin:0 auto 14px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:rgba(245,166,35,.1);border:1px solid rgba(245,166,35,.3);color:#f5a623;
}
.rate-limit-icon svg{width:22px;height:22px}
.rate-limit-title{font-weight:700;font-size:15px;margin-bottom:6px}
.rate-limit-sub{color:var(--muted-fg);font-size:13px}
#rateLimitCountdown{color:#f5a623;font-weight:600}

/* --- Success state --- */
/* Sits in the same .card-stack as #verifyCard (see verify.html) so the
   swap between them is a short crossfade instead of an instant cut -
   verify.js adds .leaving to the card and .in to this one on the same
   frame; both are absolutely positioned only while that's happening
   (verify.css's .card-stack .verify-card.leaving rule below), so normal
   layout is untouched the rest of the time. */
.success-card{
  background:var(--card);border:1px solid var(--border);border-radius:1rem;
  box-shadow:var(--shadow-card);padding:36px 24px;text-align:center;
  opacity:0;transform:translate3d(0,10px,0) scale(.98);
  transition:opacity .4s cubic-bezier(.16,1,.3,1),transform .4s cubic-bezier(.16,1,.3,1);
}
.success-card.in{opacity:1;transform:none}
.card-stack .verify-card.leaving{
  position:absolute;inset:0;opacity:0;transform:scale(.98);filter:blur(2px);pointer-events:none;
}
.success-check{
  width:56px;height:56px;border-radius:50%;background:var(--green);color:#04170a;
  display:flex;align-items:center;justify-content:center;margin:0 auto 16px;
  box-shadow:0 0 0 8px rgba(52,199,89,.12);
  animation:checkRingPulse 1.8s ease-in-out .5s infinite;
}
.success-check svg{width:32px;height:32px;overflow:visible}
.success-check-tick{
  stroke-dasharray:100;stroke-dashoffset:100;
  animation:tickDraw .5s cubic-bezier(.65,0,.35,1) .15s forwards;
}
@keyframes tickDraw{to{stroke-dashoffset:0}}
@keyframes checkRingPulse{
  0%,100%{box-shadow:0 0 0 8px rgba(52,199,89,.12)}
  50%{box-shadow:0 0 0 12px rgba(52,199,89,.05)}
}
.success-card h2{font-size:20px;margin-bottom:6px;animation:fadeUp .4s cubic-bezier(.16,1,.3,1) .3s both}
.success-card p{color:var(--muted-fg);font-size:13.5px;animation:fadeUp .4s cubic-bezier(.16,1,.3,1) .38s both}
.signout-btn{
  margin-top:20px;padding:.55rem 1.1rem;border-radius:.5rem;font-size:.8125rem;font-weight:600;
  background:transparent;color:var(--muted-fg);border:1px solid var(--border);cursor:pointer;
  transition:all .15s cubic-bezier(.16,1,.3,1);
}
.signout-btn:hover{background:rgba(255,255,255,.06);color:var(--foreground);border-color:rgba(255,255,255,.22)}

.verify-footer{text-align:center;margin-top:28px;font-size:12.5px;color:var(--muted-fg)}
.verify-footer a{text-decoration:underline;text-underline-offset:2px;transition:color .15s}
.verify-footer a:hover{color:var(--foreground)}
.verify-copyright{text-align:center;margin-top:10px;font-size:11px;color:var(--muted-fg);opacity:.7}

.hidden{display:none !important}

@media (max-width:480px){
  .provider-row{gap:10px;padding:14px 8px}
  .provider-icon{width:36px;height:36px}
  .provider-btn{padding:.5rem .8rem;font-size:.75rem}
}

/* Respect visitors who've asked for reduced motion - same opt-out the
   status page provides (see public/status/index.html's own
   prefers-reduced-motion block). Content still appears, just without
   the movement. */
@media (prefers-reduced-motion:reduce){
  .reveal{opacity:1;transform:none;transition:none}
  .hero .glow{animation:none}
  .provider-row.linked{animation:none}
  .provider-check{transform:none}
  .provider-row.linked .provider-check{animation:none}
  .partial-warning{animation:none}
  .verify-error.shake{animation:none}
  .rate-limit-notice{animation:none}
  .success-card{transition:none;opacity:1;transform:none}
  .success-card h2,.success-card p{animation:none}
  .success-check{animation:none}
  .success-check-tick{animation:none;stroke-dashoffset:0}
  .card-stack .verify-card.leaving{display:none}
}
