<?php
/**
 * Plattr — sign-in page.
 *
 * Sibling to the wizard at /. POSTs to /api/login.php which verifies
 * password_hash against customer_users, opens the master-DB session,
 * and returns { ok, redirect }. Apple/Google buttons are server-rendered
 * by includes/oauth-buttons.php (oalRenderButtons('login', ...)) — real
 * <a href> links to dashboard/api/oauth-start.php, present only when a
 * provider is configured + enabled; nothing prints when none are.
 *
 * Design mirrors the rest of the marketing site:
 *   - Dark base (#07060B) with a soft purple aurora behind the card
 *   - Plattr wordmark + bell mark in the top-left corner
 *   - Centred glass card with the form
 *   - Apple-graphite / Google-white / Email-purple-gradient SSO row
 *     identical to the wizard's auth-providers grid (so the brand
 *     reads as one continuous system between signup and signin)
 *
 * Looks great at every viewport: card stretches edge-to-edge on
 * narrow phones, caps at 460px on tablet+, and sits in a generous
 * vertical centre on desktop.
 */
declare(strict_types=1);

$envPath = __DIR__ . '/../config.env.php';
if (!file_exists($envPath)) {
    http_response_code(500);
    die('Login portal: master config not available.');
}
require_once $envPath;
require_once __DIR__ . '/../config.php';              // defines getDB() — the DB-backed session store reads through it
require_once __DIR__ . '/../includes/plattr-session.php';
require_once __DIR__ . '/../includes/oauth-buttons.php'; // oalRenderButtons() — no-ops when no provider active

// If a real session is already open, jump straight to the dashboard
// rather than make the user re-type credentials.
plattrStartSession();
if (!empty($_SESSION['customer_user_id']) && !empty($_SESSION['login_at'])) {
    header('Location: /dashboard');
    exit;
}

$loggedOut = !empty($_GET['logged_out']);
$returnTo  = '';
if (!empty($_GET['return']) && is_string($_GET['return']) && str_starts_with($_GET['return'], '/')) {
    $returnTo = $_GET['return'];
}

// ?oauth=cancelled|error|new|ready|verify_sent — friendly, generic copy
// (never a provider error code). new/ready mostly land on /#start (see the
// OAuth engine's outcome matrix), but cancelled/error can bounce back here
// when the OAuth button's return target was this page. ('link_required'
// retired — see oauth-verify-spec.md: every email-match outcome is now
// either an auto-link or the claim path, neither of which rejects the
// login.)
$oauthNotice = '';
$oauthError  = '';
switch ((string)($_GET['oauth'] ?? '')) {
    case 'ready':       $oauthNotice = "You're signed in — pick a plan to finish setting up your account."; break;
    case 'new':         $oauthNotice = "We couldn't find a Plattr account for that sign-in — get started below to create one."; break;
    case 'cancelled':   $oauthNotice = 'Sign-in was cancelled — try again, or use your email and password.'; break;
    case 'error':       $oauthError  = "That sign-in didn't go through — try again, or use your email and password."; break;
    case 'verify_sent': $oauthNotice = "We've sent a new verification link — check your inbox."; break;
}

// ?verified=1 / ?verify=invalid — result of clicking the emailed
// verification link (landing/api/verify-email.php). Kept separate from the
// oauth= switch above since it can also fire for a classic (non-OAuth)
// signup's best-effort verify email.
$verifyNotice = '';
$verifyError  = '';
if (!empty($_GET['verified'])) {
    $verifyNotice = 'Your email is verified — sign in to continue.';
} elseif ((string)($_GET['verify'] ?? '') === 'invalid') {
    $verifyError = "That verification link has expired or was already used — sign in, then request a new one from your dashboard.";
}

// OAuth buttons render only for ACTIVE providers (oalRenderButtons no-ops
// otherwise) — buffer so the "or use your email" divider only prints when
// there's actually something above it to divide.
ob_start();
oalRenderButtons('login', $returnTo !== '' ? $returnTo : '/dashboard');
$__oauthButtonsHtml = ob_get_clean();
?><!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Sign in · Plattr</title>
<meta name="theme-color" content="#07060B">
<meta name="robots" content="noindex">

<link rel="icon" type="image/png" href="assets/favicon/favicon-96x96.png?v=4" sizes="96x96">
<link rel="icon" type="image/svg+xml" href="assets/favicon/favicon.svg?v=4">
<link rel="shortcut icon" href="assets/favicon/favicon.ico?v=4">
<link rel="apple-touch-icon" sizes="180x180" href="assets/favicon/apple-touch-icon.png?v=4">

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet">

<style>
:root{
  --purple:#3B6FF6;
  --purple-light:#7AA0FB;
  --purple-bright:#B3CCFE;
  --purple-dim:#2352D8;
  --purple-deep:#16307A;
  --brand:var(--purple);
  --brand-light:var(--purple-light);
  --brand-rgb:59,111,246;

  --bg:#07060B;
  --surface:#0E0C16;
  --card:#14101F;
  --card-2:#1B1730;
  --border:rgba(255,255,255,0.06);
  --border-strong:rgba(255,255,255,0.12);

  --text:#F5F2FF;
  --text-2:#A8A2C0;
  --text-3:#69647F;
  --text-4:#3F3B52;

  --green:#34D399;
  --red:#F87171;

  --ease:cubic-bezier(0.16,1,0.3,1);
  --spring:cubic-bezier(0.34,1.38,0.64,1);

  --r-sm:10px;
  --r-md:14px;
  --r-lg:22px;
  --r-xl:28px;
}

*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
/* Use min-height instead of fixed height + use 100dvh so iOS Safari's
   collapsing chrome (address bar appears/disappears on scroll) doesn't
   cause the body to be shorter than the visible viewport, which was
   clipping the brand-mark at the top AND the legal-trust strip at the
   bottom on iPhone-sized screens. */
html,body{min-height:100%;min-height:100dvh}
html{scroll-behavior:smooth}
body{
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'SF Pro Display',sans-serif;
  background:var(--bg);color:var(--text);
  -webkit-font-smoothing:antialiased;
  letter-spacing:-0.01em;
  /* Auto-margins on .card handle the vertical centering. Doing it via
     justify-content:center would clip the TOP of the card when its
     height exceeds the viewport (every mobile device with our default
     copy). Auto-margins collapse to 0 in that case and the card sits
     at the natural top so the user can scroll the whole thing. */
  display:flex;flex-direction:column;align-items:center;
  padding:
    max(60px, env(safe-area-inset-top, 60px))
    clamp(20px, 4vw, 40px)
    max(28px, env(safe-area-inset-bottom, 28px));
  min-height:100vh;
  min-height:100dvh;
  position:relative;overflow-x:hidden;
}

/* ── Brand-coloured aurora behind everything ───────────────────── */
.bg-glow{
  position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
}
.bg-glow::before,
.bg-glow::after{
  content:'';position:absolute;border-radius:50%;
  filter:blur(120px);opacity:0.55;
  animation:drift 18s ease-in-out infinite alternate;
}
.bg-glow::before{
  top:-200px;left:50%;transform:translateX(-50%);
  width:780px;height:780px;
  background:radial-gradient(circle, rgba(59,111,246,0.45), transparent 70%);
}
.bg-glow::after{
  bottom:-300px;left:50%;transform:translateX(-30%);
  width:680px;height:680px;
  background:radial-gradient(circle, rgba(236,72,153,0.22), transparent 70%);
  animation-delay:-9s;
}
@keyframes drift{
  to{transform:translate(-55%, 12%) scale(1.05)}
}

/* Grainy noise overlay — adds the soft cinematic texture that the
   marketing site uses. CSS-only (no image fetch) so it loads instant. */
.bg-glow svg{position:absolute;inset:0;width:100%;height:100%;opacity:0.04;mix-blend-mode:overlay}

/* ── Top-left brand mark — same masked-logo treatment the rest of
   the marketing site uses, so the chrome reads as continuous. ──── */
.brand-mark{
  /* absolute (not fixed): on a phone the card is taller than the viewport and
     scrolls — a FIXED logo would float on top of the form as you scroll ("logo
     on top of it"). Absolute keeps it pinned to the top of the page so it scrolls
     away cleanly with the content. */
  position:absolute;top:24px;left:24px;z-index:5;
  display:inline-block;
  width:112px;height:32px;
  background:linear-gradient(135deg,var(--purple-light) 0%,var(--purple-bright) 100%);
  -webkit-mask:url('plattr-logo.webp?v=4') left center / contain no-repeat;
          mask:url('plattr-logo.webp?v=4') left center / contain no-repeat;
  text-indent:-9999px;overflow:hidden;
  transition:opacity .25s var(--ease),transform .25s var(--ease);
}
.brand-mark:hover{opacity:0.92;transform:translateY(-1px)}
@media (max-width:520px){
  .brand-mark{top:18px;left:18px;width:96px;height:28px}
}

/* ── Sign-in card ──────────────────────────────────────────────── */
.card{
  position:relative;z-index:1;
  width:100%;max-width:460px;
  /* margin: auto 0 vertically centers the card via flex-auto when
     there's spare room, AND naturally collapses (acts as 0) when the
     card is taller than the viewport — letting the user scroll the
     whole thing instead of clipping at the top. */
  margin:auto 0;
  background:linear-gradient(180deg, rgba(20,16,31,0.88) 0%, rgba(14,12,22,0.95) 100%);
  backdrop-filter:blur(28px) saturate(1.25);
  -webkit-backdrop-filter:blur(28px) saturate(1.25);
  border:1px solid var(--border-strong);
  border-radius:var(--r-xl);
  padding:clamp(28px,5vw,40px);
  box-shadow:
    0 30px 80px rgba(0,0,0,0.55),
    0 10px 30px rgba(var(--brand-rgb),0.10),
    inset 0 1px 0 rgba(255,255,255,0.06);
  animation:cardIn .55s var(--spring) both;
}
/* Tighter card on tiny screens — drop the inner padding so the form
   fits in landscape too, and shrink the giant top headline. */
@media (max-width:520px){
  .card{
    padding:24px 20px;
    border-radius:20px;
  }
  /* Tighter vertical rhythm so the whole form fits a phone with far less
     scrolling — the card was overflowing well past the viewport before. */
  h1{font-size:26px}
  .sub{margin-bottom:18px;font-size:14px}
  .providers{margin-bottom:12px}
  .sep{margin:12px 0}
  .field{margin-bottom:10px}
  .foot{margin-top:16px}
  .trust{margin-top:12px}
}
@keyframes cardIn{
  from{opacity:0;transform:translateY(14px) scale(0.985)}
  to  {opacity:1;transform:translateY(0)    scale(1)}
}

.eyebrow{
  display:inline-flex;align-items:center;gap:7px;
  font-size:10.5px;font-weight:700;color:var(--brand-light);
  text-transform:uppercase;letter-spacing:0.16em;
  margin-bottom:14px;
}
.eyebrow::before{
  content:'';width:6px;height:6px;border-radius:50%;background:var(--brand);
  box-shadow:0 0 12px rgba(var(--brand-rgb),0.7);
}
h1{
  font-size:clamp(28px,5vw,34px);font-weight:800;line-height:1.05;
  letter-spacing:-0.035em;margin-bottom:8px;
}
h1 em{font-style:normal;color:var(--brand-light)}
.sub{
  font-size:14.5px;color:var(--text-2);line-height:1.55;
  margin-bottom:26px;letter-spacing:-0.005em;
}

/* ── Sign-out / error / info notices ───────────────────────────── */
.notice,.error{
  display:flex;align-items:flex-start;gap:10px;
  padding:12px 14px;border-radius:var(--r-md);
  font-size:13px;line-height:1.5;
  margin-bottom:18px;
}
.notice{
  background:rgba(52,211,153,0.10);
  border:1px solid rgba(52,211,153,0.28);
  color:#A7F3D0;
  animation:noticeIn .4s var(--ease) both;
}
.notice i{color:var(--green);font-size:18px;flex:0 0 auto}
.error{
  background:rgba(248,113,113,0.10);
  border:1px solid rgba(248,113,113,0.30);
  color:#FCA5A5;
  display:none;
}
.error.is-visible{display:flex;animation:noticeIn .35s var(--ease) both}
.error i{color:var(--red);font-size:18px;flex:0 0 auto}
@keyframes noticeIn{
  from{opacity:0;transform:translateY(-4px)}
  to  {opacity:1;transform:translateY(0)}
}

/* ── Auth providers (Apple / Google) ───────────────────────────── */
.providers{
  display:grid;grid-template-columns:1fr 1fr;gap:10px;
  margin-bottom:18px;
}
@media (max-width:420px){
  .providers{grid-template-columns:1fr}
}
.prov-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:13px 14px;border-radius:var(--r-md);cursor:pointer;
  font-family:inherit;font-size:13.5px;font-weight:600;letter-spacing:-0.005em;
  border:1px solid transparent;
  transition:transform .22s var(--ease),box-shadow .22s var(--ease),background .22s var(--ease);
}
.prov-btn:hover{transform:translateY(-1px)}
.prov-btn:active{transform:translateY(0) scale(0.98)}
.prov-btn:focus-visible{outline:2px solid var(--brand);outline-offset:3px}
.prov-glyph{flex:0 0 auto;display:inline-grid;place-items:center;width:22px;height:22px}
.prov-glyph i{font-size:18px;line-height:1}
.prov-glyph svg{display:block}

.prov-btn--apple{
  background:linear-gradient(180deg,#222226 0%,#0A0A0F 100%);
  color:#fff;border-color:rgba(255,255,255,0.08);
  box-shadow:inset 0 1px 0 rgba(255,255,255,0.10), 0 4px 14px rgba(0,0,0,0.32);
}
.prov-btn--apple:hover{
  background:linear-gradient(180deg,#2C2C32 0%,#15151A 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,0.14), 0 8px 22px rgba(0,0,0,0.42);
}
.prov-btn--apple .prov-glyph i{font-size:20px}

.prov-btn--google{
  background:#FFFFFF;color:#1F2937;border-color:rgba(255,255,255,0.55);
  box-shadow:0 4px 14px rgba(255,255,255,0.08), 0 1px 0 rgba(255,255,255,0.4) inset;
}
.prov-btn--google:hover{
  background:#FFFFFF;
  box-shadow:
    0 4px 14px rgba(66,133,244,0.18),
    0 4px 14px rgba(52,168,83,0.10),
    0 4px 14px rgba(234,67,53,0.10),
    0 4px 14px rgba(251,188,5,0.10),
    0 1px 0 rgba(255,255,255,0.4) inset;
}

/* ── "or" divider between SSO and email ────────────────────────── */
.sep{
  display:flex;align-items:center;gap:12px;
  font-size:10.5px;font-weight:700;color:var(--text-3);
  letter-spacing:0.18em;text-transform:uppercase;
  margin:18px 0 16px;
}
.sep::before,.sep::after{
  content:'';flex:1;height:1px;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,0.10),transparent);
}

/* ── Form fields ───────────────────────────────────────────────── */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}
.field-row{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:12px;letter-spacing:0.02em;
}
.field-label{
  font-size:12px;font-weight:600;color:var(--text-2);letter-spacing:0.02em;
}
.field-link{
  font-size:12px;color:var(--brand-light);text-decoration:none;font-weight:500;
  transition:color .2s var(--ease);
}
.field-link:hover{color:var(--purple-bright);text-decoration:underline}
.field-input-wrap{position:relative}
.field-input{
  width:100%;padding:14px 14px;
  background:rgba(255,255,255,0.025);
  border:1px solid var(--border);border-radius:var(--r-md);
  color:var(--text);font-size:15.5px;font-family:inherit;letter-spacing:-0.01em;
  outline:none;
  transition:border-color .22s var(--ease),background .22s var(--ease),box-shadow .22s var(--ease);
  -webkit-appearance:none;appearance:none;
}
.field-input::placeholder{color:var(--text-3)}
.field-input:hover:not(:focus){border-color:rgba(255,255,255,0.12)}
.field-input:focus{
  border-color:rgba(var(--brand-rgb),0.55);
  background:rgba(var(--brand-rgb),0.06);
  box-shadow:0 0 0 4px rgba(var(--brand-rgb),0.14);
}
.field-input.is-error{
  border-color:rgba(248,113,113,0.45);
  box-shadow:0 0 0 4px rgba(248,113,113,0.10);
}

/* Show/hide password toggle */
.pw-toggle{
  position:absolute;right:8px;top:50%;transform:translateY(-50%);
  width:34px;height:34px;border-radius:8px;
  background:transparent;border:0;cursor:pointer;
  color:var(--text-3);
  display:inline-grid;place-items:center;
  transition:color .2s var(--ease),background .2s var(--ease);
}
.pw-toggle:hover{color:var(--text);background:rgba(255,255,255,0.04)}
.pw-toggle:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.pw-toggle i{font-size:18px}

/* ── Submit button ─────────────────────────────────────────────── */
.submit-btn{
  width:100%;padding:15px;margin-top:4px;
  background:linear-gradient(135deg,var(--purple) 0%,var(--purple-light) 100%);
  color:#fff;border:1px solid rgba(255,255,255,0.12);border-radius:var(--r-md);
  font-size:15px;font-weight:700;font-family:inherit;letter-spacing:-0.01em;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  box-shadow:
    0 10px 28px rgba(var(--brand-rgb),0.42),
    inset 0 1px 0 rgba(255,255,255,0.22);
  transition:transform .22s var(--ease),box-shadow .3s var(--ease),filter .25s var(--ease);
}
.submit-btn:hover:not(:disabled){
  filter:brightness(1.06);transform:translateY(-1px);
  box-shadow:0 16px 38px rgba(var(--brand-rgb),0.55),inset 0 1px 0 rgba(255,255,255,0.28);
}
.submit-btn:active:not(:disabled){transform:translateY(0) scale(0.99)}
.submit-btn:disabled{opacity:0.7;cursor:progress;transform:none}
.submit-btn i{font-size:18px;transition:transform .25s var(--ease)}
.submit-btn:hover:not(:disabled) i{transform:translateX(3px)}
.submit-spinner{
  width:16px;height:16px;display:none;
  border:2px solid rgba(255,255,255,0.3);border-top-color:#fff;border-radius:50%;
  animation:spin .7s linear infinite;
}
.submit-btn.is-loading .submit-spinner{display:block}
.submit-btn.is-loading .submit-text{opacity:0.7}
.submit-btn.is-loading i.bx-right-arrow-alt{display:none}
@keyframes spin{to{transform:rotate(360deg)}}

/* ── Footer line ───────────────────────────────────────────────── */
.foot{
  text-align:center;margin-top:24px;
  font-size:13px;color:var(--text-3);
}
.foot a{
  color:var(--brand-light);text-decoration:none;font-weight:600;
  transition:color .2s var(--ease);
}
.foot a:hover{color:var(--purple-bright);text-decoration:underline}

/* Trust strip — small, fine, just for confidence */
.trust{
  display:flex;align-items:center;justify-content:center;gap:12px;
  margin-top:18px;font-size:11px;color:var(--text-4);
  letter-spacing:0.04em;
}
.trust span{display:inline-flex;align-items:center;gap:4px}
.trust i{font-size:12px;color:var(--text-3)}

/* Reduced-motion users: skip the aurora drift + card pop. */
@media (prefers-reduced-motion: reduce){
  .bg-glow::before,.bg-glow::after,.card,.notice,.error.is-visible{
    animation:none !important;
  }
}
</style>
</head>
<body>
  <div class="bg-glow" aria-hidden="true">
    <!-- Inline SVG noise (no asset fetch) for cinematic texture. -->
    <svg xmlns="http://www.w3.org/2000/svg">
      <filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/></filter>
      <rect width="100%" height="100%" filter="url(#n)"/>
    </svg>
  </div>

  <a class="brand-mark" href="/" aria-label="Plattr — go home">plattr</a>

  <main class="card" role="main">
    <h1>Sign in to <em>Plattr</em>.</h1>
    <p class="sub">Pick up where you left off — your storefront, orders, and brand are right where you parked them.</p>

    <?php if ($loggedOut): ?>
      <div class="notice" role="status">
        <i class="bx bx-check-circle"></i>
        <span>You're signed out. Sign back in to access your dashboard.</span>
      </div>
    <?php endif; ?>

    <?php if ($oauthNotice !== ''): ?>
      <div class="notice" role="status">
        <i class="bx bx-info-circle"></i>
        <span><?= htmlspecialchars($oauthNotice) ?></span>
      </div>
    <?php endif; ?>

    <?php if ($verifyNotice !== ''): ?>
      <div class="notice" role="status">
        <i class="bx bx-check-circle"></i>
        <span><?= htmlspecialchars($verifyNotice) ?></span>
      </div>
    <?php endif; ?>

    <?php if ($verifyError !== ''): ?>
      <div class="error is-visible" role="alert">
        <i class="bx bx-error-circle"></i>
        <span><?= htmlspecialchars($verifyError) ?></span>
      </div>
    <?php endif; ?>

    <?php if ($oauthError !== ''): ?>
      <div class="error is-visible" role="alert">
        <i class="bx bx-error-circle"></i>
        <span><?= htmlspecialchars($oauthError) ?></span>
      </div>
    <?php endif; ?>

    <div class="error" id="loginErr" role="alert">
      <i class="bx bx-error-circle"></i>
      <span id="loginErrMsg"></span>
    </div>

    <?php if (trim($__oauthButtonsHtml) !== ''): ?>
    <!-- Server-rendered — only ACTIVE providers ever print here. Real <a>
         links straight to oauth-start.php, no JS wiring needed. -->
    <div role="group" aria-label="Sign in with a provider"><?= $__oauthButtonsHtml ?></div>
    <div class="sep" aria-hidden="true"><span>or use your email</span></div>
    <?php endif; ?>

    <form id="loginForm" autocomplete="on" novalidate>
      <div class="field">
        <label class="field-label" for="email">Email</label>
        <input class="field-input" id="email" name="email" type="email"
               required autocomplete="username" autofocus inputmode="email"
               placeholder="you@yourbusiness.com">
      </div>
      <div class="field">
        <div class="field-row">
          <label class="field-label" for="password">Password</label>
          <a class="field-link" href="#" id="forgotLink">Forgot?</a>
        </div>
        <div class="field-input-wrap">
          <input class="field-input" id="password" name="password" type="password"
                 required autocomplete="current-password" minlength="8"
                 placeholder="Your password" style="padding-right:50px">
          <button type="button" class="pw-toggle" id="pwToggle" aria-label="Show password" aria-pressed="false">
            <i class="bx bx-show" id="pwToggleIcon"></i>
          </button>
        </div>
      </div>

      <label class="remember-row" for="rememberMe" style="display:flex;align-items:center;gap:9px;margin:2px 0 14px;cursor:pointer;user-select:none;font-size:13px;color:rgba(255,255,255,0.65)">
        <input type="checkbox" id="rememberMe" name="remember" value="1" checked style="width:16px;height:16px;accent-color:var(--brand-primary,#3B6FF6);cursor:pointer">
        <span>Remember me on this device</span>
      </label>

      <button class="submit-btn" type="submit" id="submitBtn">
        <span class="submit-spinner" aria-hidden="true"></span>
        <span class="submit-text">Sign in</span>
        <i class="bx bx-right-arrow-alt" aria-hidden="true"></i>
      </button>
    </form>

    <p class="foot">
      Don't have a Plattr account? <a href="/#start">Get started</a>
    </p>

    <div class="trust" aria-hidden="true">
      <span><i class="bx bxs-lock-alt"></i> Encrypted</span>
      <span>·</span>
      <span><i class="bx bx-shield-quarter"></i> SOC 2-aligned</span>
      <span>·</span>
      <span>NZ-hosted</span>
    </div>
  </main>

<script>
const RETURN = <?= json_encode($returnTo) ?>;

const form       = document.getElementById('loginForm');
const emailEl    = document.getElementById('email');
const pwEl       = document.getElementById('password');
const errBox     = document.getElementById('loginErr');
const errMsg     = document.getElementById('loginErrMsg');
const btn        = document.getElementById('submitBtn');

function showErr(msg){
  errMsg.textContent = msg;
  errBox.classList.add('is-visible');
  emailEl.classList.toggle('is-error', /email/i.test(msg));
  pwEl.classList.toggle('is-error',    /password|credential/i.test(msg));
}
function clearErr(){
  errBox.classList.remove('is-visible');
  emailEl.classList.remove('is-error');
  pwEl.classList.remove('is-error');
}
emailEl.addEventListener('input', clearErr);
pwEl.addEventListener('input',    clearErr);

/* Show/hide password — small a11y nicety, expected on modern forms. */
const pwToggle = document.getElementById('pwToggle');
const pwIcon   = document.getElementById('pwToggleIcon');
pwToggle.addEventListener('click', function(){
  const showing = pwEl.type === 'text';
  pwEl.type = showing ? 'password' : 'text';
  pwToggle.setAttribute('aria-pressed', showing ? 'false' : 'true');
  pwToggle.setAttribute('aria-label', showing ? 'Show password' : 'Hide password');
  pwIcon.className = showing ? 'bx bx-show' : 'bx bx-hide';
});

/* Forgot password — POST to /api/forgot-password.php which mints a
   reset token (or no-ops silently if the email isn't registered, to
   prevent account enumeration), logs the URL server-side, and on
   preview hosts returns the URL in `_preview_reset_url` so devs can
   complete the flow without email infra. */
document.getElementById('forgotLink').addEventListener('click', async function(e){
  e.preventDefault();
  clearErr();
  const email = emailEl.value.trim();
  if (!email || !email.includes('@')) {
    showErr('Enter your email first — we\'ll send a reset link to it.');
    emailEl.focus();
    return;
  }
  let res;
  try {
    const r = await fetch('/api/forgot-password.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'same-origin',
      body: JSON.stringify({ email })
    });
    res = await r.json();
  } catch (err) { res = { ok:false, error:'network' }; }

  if (res && res.error === 'rate_limited') {
    showErr('Too many reset requests — wait 15 minutes and try again.');
    return;
  }
  // Same response either way so we don't reveal whether the email is
  // registered. On preview hosts, surface the URL so the dev can click
  // straight through.
  if (res && res._preview_reset_url) {
    if (confirm("Reset link generated (preview-host only):\n\n" + res._preview_reset_url + "\n\nOpen it now?")) {
      window.location.href = res._preview_reset_url;
      return;
    }
  } else {
    alert("If that email is registered, we've sent a reset link.\nCheck your inbox (and spam) — links expire in 60 minutes.");
  }
});

form.addEventListener('submit', async function(e){
  e.preventDefault();
  clearErr();

  const email    = emailEl.value.trim();
  const password = pwEl.value;
  if (!email || !email.includes('@'))     { showErr('Enter a valid email.');         emailEl.focus(); return; }
  if (!password || password.length < 8)   { showErr('Password must be at least 8 characters.'); pwEl.focus(); return; }

  btn.disabled = true;
  btn.classList.add('is-loading');

  let res;
  try {
    const r = await fetch('/api/login.php', {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'same-origin',
      body: JSON.stringify({ email: email, password: password, remember: document.getElementById('rememberMe')?.checked ? 1 : 0 })
    });
    res = await r.json();
  } catch (err) {
    res = { ok:false, error:'network' };
  }

  if (!res || !res.ok) {
    btn.disabled = false;
    btn.classList.remove('is-loading');
    const code = (res && res.error) || 'unknown';
    const map = {
      invalid_credentials: "That email and password don't match. Try again, or sign up below.",
      account_disabled:    'This account has been disabled. Contact hello@plattr.nz to reactivate.',
      no_workspace:        'We couldn\'t find an active workspace for this account — finish signup at /.',
      missing_fields:      'Fill in both fields.',
      rate_limited:        'Too many attempts — wait a minute and try again.',
      network:             "Couldn't reach the server. Check your connection and try again.",
    };
    showErr(map[code] || 'Sign-in failed. Please try again.');
    return;
  }

  window.location.href = res.redirect || RETURN || '/dashboard';
});
</script>
</body>
</html>
