<script setup lang="ts">
// Security · password / two-factor / recovery codes / sign-in history.
// Faithfully ports project/platform-enduser.jsx `SecurityEndUserScreen`
// (lines 352–971) and its sub-modals.


import { mfaMethods, recoveryCodes, signInHistory } from '~/data/enduser'

const toast = useToast()
const tab = ref('password')

// --- Password ---
const pwd = reactive({ current: '••••••••••••', next: 'northern-coffee-bridge-april', confirm: 'northern-coffee-bridge-april' })
const showPwd = reactive({ current: false, next: false, confirm: false })
const savedFlash = ref(false)
const strength = computed(() => {
  const v = pwd.next
  if (!v) return 0
  let s = Math.min(100, v.length * 8)
  if (/[A-Z]/.test(v)) s += 8
  if (/[0-9]/.test(v)) s += 8
  if (/[^A-Za-z0-9]/.test(v)) s += 10
  return Math.min(100, s)
})
const strengthLabel = computed(() => {
  const s = strength.value
  if (s > 80) return 'excellent'
  if (s > 60) return 'strong'
  if (s > 30) return 'fair'
  return 'weak'
})
const strengthColor = computed(() => {
  const s = strength.value
  if (s > 60) return 'var(--ok)'
  if (s > 30) return 'var(--warn)'
  return 'var(--bad)'
})

// Criteria list matches source's 4 fixed entries (all marked passing).
const criteria = [
  { id: 'len',     label: 'At least 14 characters',                 ok: true },
  { id: 'mix',     label: 'Mix of letters, numbers, or words',       ok: true },
  { id: 'breach',  label: 'Not in known breach lists',               ok: true },
  { id: 'rotate',  label: 'Different from your last 5 passwords',    ok: true },
]

const canSubmitPwd = computed(() => !!pwd.next && pwd.next === pwd.confirm)

function submitPwd() {
  if (!canSubmitPwd.value) return
  savedFlash.value = true
  setTimeout(() => (savedFlash.value = false), 3000)
}
function resetPwd() {
  pwd.next = 'northern-coffee-bridge-april'
  pwd.confirm = 'northern-coffee-bridge-april'
  savedFlash.value = false
}

const backupEmail = ref('anne@gmail.com')
const verifyOpen = ref(false)
const verifyCode = ref('')

// --- MFA ---
const renameMfa = ref<typeof mfaMethods[number] | null>(null)
const renameMfaValue = ref('')
const removeMfa = ref<typeof mfaMethods[number] | null>(null)
const mfaSetup = ref<'webauthn' | 'totp' | null>(null)
const mfaSetupStep = ref(1)

function openSetup(kind: 'webauthn' | 'totp') {
  mfaSetup.value = kind
  mfaSetupStep.value = 1
}

const mfaMenuFor = ref<string | null>(null)
const mfaMenuPos = ref({ top: 0, right: 0 })
const mfaTriggerRefs = ref<Record<string, HTMLElement | null>>({})

function openMfaMenu(id: string, e: MouseEvent) {
  e.stopPropagation()
  const btn = mfaTriggerRefs.value[id]
  if (btn) {
    const r = btn.getBoundingClientRect()
    mfaMenuPos.value = { top: r.bottom + 4, right: window.innerWidth - r.right }
  }
  mfaMenuFor.value = mfaMenuFor.value === id ? null : id
}
function setMfaTriggerRef(id: string, el: any) {
  mfaTriggerRefs.value[id] = el as HTMLElement | null
}
onMounted(() => {
  const close = () => (mfaMenuFor.value = null)
  const onScroll = () => (mfaMenuFor.value = null)
  const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') mfaMenuFor.value = null }
  document.addEventListener('mousedown', close)
  document.addEventListener('keydown', onKey)
  window.addEventListener('scroll', onScroll, true)
  window.addEventListener('resize', onScroll)
  onBeforeUnmount(() => {
    document.removeEventListener('mousedown', close)
    document.removeEventListener('keydown', onKey)
    window.removeEventListener('scroll', onScroll, true)
    window.removeEventListener('resize', onScroll)
  })
})

// --- Recovery codes ---
const showCodesOpen = ref(false)
const regenOpen = ref(false)
const regenAck = ref(false)
const copyFlash = ref(false)
const downloadFlash = ref(false)

// Source marks first 2 codes as "used" via opacity/strikethrough.
const usedCount = 2

function copyCodes() {
  const text = recoveryCodes
    .map((c, i) => `${String(i + 1).padStart(2, '0')}  ${c}`)
    .join('\n')
  if (navigator.clipboard) navigator.clipboard.writeText(text).catch(() => {})
  copyFlash.value = true
  setTimeout(() => (copyFlash.value = false), 1800)
}
function downloadCodes() {
  downloadFlash.value = true
  setTimeout(() => (downloadFlash.value = false), 1800)
}

// --- Sign-in history ---
const exportOpen = ref(false)
const exportPeriod = ref<'30d' | '90d' | '12mo' | 'all'>('90d')
const exportFormat = ref<'csv' | 'json'>('csv')
const exportIncludeFailed = ref(true)
const exportDelivery = ref<'download' | 'email'>('download')

const exportRowEst = computed(() => {
  if (exportPeriod.value === '30d') return 14
  if (exportPeriod.value === '90d') return 42
  if (exportPeriod.value === '12mo') return 186
  return 312
})
</script>

<template>
  <div>
    <PageHeader
      eyebrow="Account"
      title="Security"
      subtitle="Password, multi-factor methods, recovery codes, and a complete history of sign-ins on your account."
    />

    <div class="tabs-wrap">
      <Tabs
        v-model="tab"
        :items="[
          { value: 'password',  label: 'Password' },
          { value: 'mfa',       label: 'Two-factor', count: mfaMethods.length },
          { value: 'recovery',  label: 'Recovery codes' },
          { value: 'history',   label: 'Sign-in history', count: signInHistory.length },
        ]"
      />
    </div>

    <div class="content">
      <!-- Password tab -->
      <section v-if="tab === 'password'" class="stack">
        <Card>
          <header class="card-header">
            <Eyebrow>Change password</Eyebrow>
            <h2>Choose a strong password</h2>
            <p>At least 14 characters · we recommend a passphrase like 'wide-lemon-tunnel-corn' or use your password manager.</p>
          </header>
          <div class="pwd-form">
            <EnduserFormField label="Current password">
              <div class="pwd-input">
                <input :type="showPwd.current ? 'text' : 'password'" v-model="pwd.current" />
                <button type="button" class="eye" @click="showPwd.current = !showPwd.current">
                  <UiIcon name="key" :size="14" />
                </button>
              </div>
            </EnduserFormField>
            <EnduserFormField label="New password">
              <div class="pwd-input">
                <input :type="showPwd.next ? 'text' : 'password'" v-model="pwd.next" />
                <button type="button" class="eye" @click="showPwd.next = !showPwd.next">
                  <UiIcon :name="showPwd.next ? 'x' : 'key'" :size="14" />
                </button>
              </div>
            </EnduserFormField>
            <div class="strength">
              <div class="strength-head">
                <Mono dim>STRENGTH</Mono>
                <Mono :style="{ color: strengthColor }">{{ strengthLabel }}</Mono>
              </div>
              <div class="bar"><span :style="{ width: strength + '%', background: strengthColor }" /></div>
              <ul class="criteria">
                <li v-for="c in criteria" :key="c.id" :data-ok="c.ok">
                  <UiIcon :name="c.ok ? 'check' : 'x'" :size="11" :stroke-width="2.4" />
                  <span>{{ c.label }}</span>
                </li>
              </ul>
            </div>
            <EnduserFormField label="Confirm new password">
              <div class="pwd-input">
                <input :type="showPwd.confirm ? 'text' : 'password'" v-model="pwd.confirm" />
                <button type="button" class="eye" @click="showPwd.confirm = !showPwd.confirm">
                  <UiIcon :name="showPwd.confirm ? 'x' : 'key'" :size="14" />
                </button>
              </div>
            </EnduserFormField>
          </div>
          <div class="actions">
            <span v-if="savedFlash" class="flash-ok">
              <UiIcon name="check" :size="13" :stroke-width="2.5" />
              Password updated · confirmation email sent
            </span>
            <div style="flex: 1;" />
            <UiButton variant="ghost" @click="resetPwd">Cancel</UiButton>
            <UiButton variant="primary" :disabled="!canSubmitPwd" @click="submitPwd">
              <template #leading><UiIcon name="check" :size="13" /></template>
              Update password
            </UiButton>
          </div>
          <p class="footer-note">
            <Mono>// after you change</Mono> we'll sign you out everywhere except this device. You'll get an email confirmation. If you didn't make this change, contact your admin immediately.
          </p>
        </Card>

        <Card>
          <header class="card-header with-actions">
            <div class="head-text">
              <Eyebrow>Account recovery</Eyebrow>
              <h2>Backup recovery email</h2>
              <p>If you lose access to dezky, we can send a recovery link to this address. Use a personal address, not a work one.</p>
            </div>
            <Badge tone="ok" dot>verified</Badge>
          </header>
          <div class="recovery">
            <div class="email-input">
              <UiIcon name="mail" :size="14" stroke="var(--text-mute)" />
              <input v-model="backupEmail" />
            </div>
            <UiButton size="sm" variant="secondary" @click="verifyOpen = true">Verify new address</UiButton>
          </div>
          <div class="callout-info">
            <UiIcon name="shield" :size="14" />
            <span>We'll never use this address for marketing or workspace announcements — only account recovery and security alerts. Verified <Mono>14 Jan 2026</Mono> · last reachability check <Mono>2 days ago</Mono>.</span>
          </div>
        </Card>
      </section>

      <!-- MFA tab -->
      <section v-else-if="tab === 'mfa'" class="stack">
        <Card>
          <div class="mfa-on">
            <span class="mfa-shield"><UiIcon name="shield" :size="20" /></span>
            <div>
              <h3>Two-factor is on</h3>
              <p>You have <b>{{ mfaMethods.length }} methods</b> set up · workspace requires MFA for admins.</p>
            </div>
          </div>
        </Card>

        <Card :pad="0">
          <header class="mfa-head">
            <Eyebrow>Active methods</Eyebrow>
            <Mono dim>{{ mfaMethods.length }}</Mono>
          </header>
          <ul class="mfa-list">
            <li v-for="m in mfaMethods" :key="m.id">
              <span class="mfa-icon">
                <UiIcon :name="m.kind === 'webauthn' ? 'key' : 'device'" :size="16" />
              </span>
              <div class="mfa-text">
                <div class="mfa-row">
                  <span class="mfa-name">{{ m.label }}</span>
                  <Badge v-if="m.primary" tone="invert">primary</Badge>
                  <Badge tone="neutral">{{ m.kind === 'webauthn' ? 'hardware key' : 'authenticator app' }}</Badge>
                </div>
                <Mono dim>added {{ m.enrolledOn }} · last used {{ m.lastUsed }}</Mono>
              </div>
              <span :ref="(el) => setMfaTriggerRef(m.id, el)" class="mfa-trigger">
                <UiButton size="sm" variant="ghost" @click="openMfaMenu(m.id, $event)">
                  <UiIcon name="more" :size="14" />
                </UiButton>
              </span>
            </li>
          </ul>
          <div class="mfa-add">
            <UiButton variant="secondary" @click="openSetup('webauthn')">
              <template #leading><UiIcon name="key" :size="13" /></template>
              Add security key (WebAuthn)
            </UiButton>
            <UiButton variant="secondary" @click="openSetup('totp')">
              <template #leading><UiIcon name="device" :size="13" /></template>
              Add authenticator app (TOTP)
            </UiButton>
          </div>
        </Card>

        <!-- Portaled MFA method action menus -->
        <Teleport to="body">
          <div
            v-if="mfaMenuFor"
            class="mfa-menu"
            :style="{ top: mfaMenuPos.top + 'px', right: mfaMenuPos.right + 'px' }"
            @mousedown.stop
          >
            <template v-for="m in mfaMethods" :key="m.id">
              <template v-if="m.id === mfaMenuFor">
                <button @click="renameMfa = m; renameMfaValue = m.label; mfaMenuFor = null">
                  <UiIcon name="brush" :size="14" />
                  <span>Rename method</span>
                </button>
                <button :disabled="m.primary" @click="!m.primary && toast.ok(`${m.label} is now primary`); mfaMenuFor = null">
                  <UiIcon name="shield" :size="14" />
                  <span>{{ m.primary ? 'Already primary' : 'Set as primary' }}</span>
                </button>
                <button @click="toast.info(`Testing ${m.label}`); mfaMenuFor = null">
                  <UiIcon name="refresh" :size="14" />
                  <span>Test this method</span>
                </button>
                <span class="sep" />
                <button class="danger" :disabled="m.primary" @click="!m.primary && (removeMfa = m); mfaMenuFor = null">
                  <UiIcon name="trash" :size="14" />
                  <span>{{ m.primary ? 'Remove · pick a new primary first' : 'Remove method' }}</span>
                </button>
              </template>
            </template>
          </div>
        </Teleport>
      </section>

      <!-- Recovery codes tab -->
      <section v-else-if="tab === 'recovery'" class="stack">
        <Card>
          <header class="card-header">
            <Eyebrow>Recovery codes</Eyebrow>
            <h2>One-time codes for when you lose access</h2>
            <p>If you lose your phone and security key, these codes get you back in. Treat them like passwords — store offline.</p>
          </header>
          <div class="codes-summary">
            <span class="codes-tile"><UiIcon name="shield" :size="18" /></span>
            <div class="codes-meta">
              <div class="codes-title">{{ recoveryCodes.length }} codes generated · {{ recoveryCodes.length - usedCount }} unused</div>
              <Mono dim>last regenerated · 14 Jan 2026 · {{ usedCount }} used</Mono>
            </div>
            <UiButton variant="secondary" @click="showCodesOpen = true">
              <template #leading><UiIcon name="key" :size="13" /></template>
              View codes
            </UiButton>
          </div>
          <div class="callout-warn">
            <UiIcon name="bell" :size="14" />
            <div class="cw-body">
              <b>Keep these somewhere safe</b> — printed, password manager, or offline note. Each code works once. If you suspect they've been seen by someone else, regenerate immediately.
            </div>
            <UiButton size="sm" variant="ghost" @click="regenOpen = true; regenAck = false">Regenerate</UiButton>
          </div>
        </Card>
      </section>

      <!-- Sign-in history tab -->
      <section v-else-if="tab === 'history'" class="stack">
        <div class="hist-bar">
          <div class="hist-intro">
            Every sign-in attempt on your account, successful or not. If you see something you don't recognize, change your password and revoke unknown sessions.
          </div>
          <UiButton variant="secondary" @click="exportOpen = true">
            <template #leading><UiIcon name="download" :size="13" /></template>
            Export
          </UiButton>
        </div>

        <Card :pad="0">
          <table class="history">
            <thead>
              <tr>
                <th>Time</th>
                <th>IP</th>
                <th>Location</th>
                <th>Client</th>
                <th>Method</th>
                <th>Result</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="r in signInHistory" :key="r.id">
                <td><Mono>{{ r.when }}</Mono></td>
                <td><Mono>{{ r.ip }}</Mono></td>
                <td><span :class="{ unknown: r.location === 'Unknown' }">{{ r.location }}</span></td>
                <td><Mono dim>{{ r.ua }}</Mono></td>
                <td><Mono>{{ r.method }}</Mono></td>
                <td>
                  <Badge :tone="r.result === 'ok' ? 'ok' : 'bad'" dot>
                    {{ r.result === 'ok' ? 'success' : 'failed' }}
                  </Badge>
                </td>
              </tr>
            </tbody>
          </table>
        </Card>

        <div class="callout-bad-strong">
          <UiIcon name="shield" :size="14" />
          <div class="cb-text">
            <div class="cb-title">3 failed attempts from 203.0.113.4 yesterday at 18:02</div>
            <Mono dim>Unknown location. Your password is still safe, but consider rotating it.</Mono>
          </div>
          <UiButton size="sm" variant="primary" @click="tab = 'password'">Change password</UiButton>
        </div>
      </section>
    </div>

    <!-- Verify backup email modal -->
    <Modal :open="verifyOpen" eyebrow="Account recovery" title="Verify backup email" size="sm" @close="verifyOpen = false">
      <p class="modal-body">
        We sent a 6-digit code to <Mono>{{ backupEmail }}</Mono>. Paste it below to confirm you control the inbox.
      </p>
      <EnduserFormField label="Verification code">
        <input v-model="verifyCode" maxlength="6" placeholder="6-digit code" />
      </EnduserFormField>
      <Mono dim style="display: block; text-align: center; margin-top: 12px;">
        didn't get it? <a href="#" @click.prevent="toast.info('Code resent')">resend</a> · expires in 10 min
      </Mono>
      <template #footer>
        <UiButton variant="ghost" @click="verifyOpen = false">Cancel</UiButton>
        <UiButton variant="primary" :disabled="verifyCode.length < 6" @click="verifyOpen = false; toast.ok('Backup email verified')">
          <template #leading><UiIcon name="check" :size="13" /></template>
          Verify
        </UiButton>
      </template>
    </Modal>

    <!-- Rename MFA modal -->
    <Modal :open="renameMfa !== null" eyebrow="Two-factor · rename" :title="renameMfa ? `Rename ${renameMfa.label}` : ''" size="sm" @close="renameMfa = null">
      <EnduserFormField label="Method name">
        <input v-model="renameMfaValue" placeholder="e.g. YubiKey 5C · work laptop" />
      </EnduserFormField>
      <Mono dim style="display: block; margin-top: 12px;">helps you tell methods apart when signing in or removing one</Mono>
      <template #footer>
        <UiButton variant="ghost" @click="renameMfa = null">Cancel</UiButton>
        <UiButton variant="primary" :disabled="!renameMfaValue.trim()" @click="toast.ok(`Renamed to ${renameMfaValue}`); renameMfa = null">
          <template #leading><UiIcon name="check" :size="13" /></template>
          Save name
        </UiButton>
      </template>
    </Modal>

    <!-- Remove MFA method modal (matches source DefList layout) -->
    <Modal :open="removeMfa !== null" eyebrow="Two-factor · remove" :title="removeMfa ? `Remove ${removeMfa.label}?` : ''" size="md" @close="removeMfa = null">
      <div class="modal-stack">
        <div class="callout-bad">
          <UiIcon name="shield" :size="16" />
          <div>You'll be required to re-enter your password to confirm. We'll send a confirmation email — if you didn't do this, contact your admin immediately.</div>
        </div>
        <div class="def-block">
          <dl>
            <div><dt>Method</dt><dd>{{ removeMfa?.label }}</dd></div>
            <div><dt>Kind</dt><dd>{{ removeMfa?.kind === 'webauthn' ? 'hardware security key' : 'authenticator app (TOTP)' }}</dd></div>
            <div><dt>Added</dt><dd>{{ removeMfa?.enrolledOn }}</dd></div>
            <div><dt>Last used</dt><dd>{{ removeMfa?.lastUsed }}</dd></div>
          </dl>
        </div>
        <Mono dim>at least one MFA method must remain — workspace requires MFA for admins</Mono>
      </div>
      <template #footer>
        <UiButton variant="ghost" @click="removeMfa = null">Cancel</UiButton>
        <UiButton variant="danger" @click="toast.warn(`${removeMfa?.label} removed`); removeMfa = null">
          <template #leading><UiIcon name="trash" :size="13" /></template>
          Remove method
        </UiButton>
      </template>
    </Modal>

    <!-- MFA setup wizard -->
    <Modal :open="mfaSetup !== null" :eyebrow="`Step ${mfaSetupStep} of 3`" :title="mfaSetup === 'webauthn' ? 'Add a security key' : 'Add an authenticator app'" size="md" @close="mfaSetup = null">
      <div v-if="mfaSetupStep === 1">
        <p class="modal-body">
          <template v-if="mfaSetup === 'webauthn'">Security keys are the strongest form of MFA. Use a hardware token (YubiKey) or your laptop's built-in Touch ID / Windows Hello.</template>
          <template v-else>You'll scan a QR code with an authenticator app like 1Password, Bitwarden, Authy, or Google Authenticator.</template>
        </p>
        <EnduserFormField label="Name this method">
          <input :value="mfaSetup === 'webauthn' ? 'MacBook Pro · Touch ID' : '1Password · main vault'" />
        </EnduserFormField>
      </div>
      <div v-else-if="mfaSetupStep === 2 && mfaSetup === 'webauthn'" class="wait-key">
        <div class="key-circle"><UiIcon name="key" :size="48" :stroke-width="1.4" /></div>
        <div class="key-title">Waiting for your key…</div>
        <Mono dim style="margin-top: 8px; display: block;">// touch your YubiKey or use Touch ID</Mono>
      </div>
      <div v-else-if="mfaSetupStep === 2 && mfaSetup === 'totp'">
        <p class="modal-body">
          Scan this code with your authenticator app. Or copy the setup key and add it manually.
        </p>
        <div class="qr-setup">
          <div class="qr-big">
            <span v-for="n in 441" :key="n" :style="{ background: (n * 7919 % 100) < 48 ? '#0A0A0A' : 'transparent' }" />
          </div>
          <div class="qr-meta">
            <Mono dim>SETUP KEY</Mono>
            <div class="qr-key">JBSW Y3DP EHPK 3PXP</div>
            <UiButton size="sm" variant="ghost">
              <template #leading><UiIcon name="copy" :size="13" /></template>
              Copy key
            </UiButton>
          </div>
        </div>
        <EnduserFormField label="Enter the 6-digit code from your app">
          <input value="481" />
        </EnduserFormField>
      </div>
      <div v-else-if="mfaSetupStep === 3">
        <div class="success">
          <span class="success-tile"><UiIcon name="check" :size="22" :stroke-width="2.5" /></span>
          <div>
            <h3>{{ mfaSetup === 'webauthn' ? 'Security key added' : 'App registered' }}</h3>
            <Mono dim>verified · ready to use on next sign-in</Mono>
          </div>
        </div>
        <div class="success-note">
          <Mono>// next time you sign in</Mono><br />
          We'll ask you to use this method as your second factor. Make sure you have recovery codes saved somewhere safe in case you lose access to your authenticator.
        </div>
      </div>
      <template #footer>
        <UiButton variant="ghost" @click="mfaSetup = null">Cancel</UiButton>
        <div style="flex: 1;" />
        <UiButton v-if="mfaSetupStep > 1" variant="secondary" @click="mfaSetupStep--">Back</UiButton>
        <UiButton v-if="mfaSetupStep < 3" variant="primary" @click="mfaSetupStep++">Continue</UiButton>
        <UiButton v-else variant="primary" @click="toast.ok('Method added'); mfaSetup = null">
          <template #leading><UiIcon name="check" :size="13" /></template>
          Done · use this method
        </UiButton>
      </template>
    </Modal>

    <!-- Recovery codes modal -->
    <Modal :open="showCodesOpen" eyebrow="One-time use · keep safe" title="Your recovery codes" size="md" @close="showCodesOpen = false">
      <p class="modal-body">
        Save these somewhere offline. Each code works <b>once</b>. We'll show you any codes you haven't used yet.
      </p>
      <div class="codes-grid">
        <div
          v-for="(c, i) in recoveryCodes"
          :key="c"
          class="code"
          :class="{ used: i < usedCount }"
        >
          <span class="code-idx">{{ i + 1 < 10 ? '0' + (i + 1) : i + 1 }}</span>
          <span class="code-val">{{ c }}</span>
          <Mono v-if="i < usedCount" dim>used</Mono>
        </div>
      </div>
      <template #footer>
        <UiButton variant="ghost" @click="showCodesOpen = false">Close</UiButton>
        <div style="flex: 1;" />
        <UiButton variant="secondary" @click="copyCodes">
          <template #leading><UiIcon :name="copyFlash ? 'check' : 'copy'" :size="13" /></template>
          {{ copyFlash ? 'Copied · paste somewhere safe' : 'Copy all' }}
        </UiButton>
        <UiButton variant="primary" @click="downloadCodes">
          <template #leading><UiIcon :name="downloadFlash ? 'check' : 'download'" :size="13" /></template>
          {{ downloadFlash ? 'Downloaded' : 'Download .txt' }}
        </UiButton>
      </template>
    </Modal>

    <!-- Regenerate recovery codes modal -->
    <Modal :open="regenOpen" eyebrow="Destructive · invalidates existing codes" title="Regenerate recovery codes?" size="md" @close="regenOpen = false">
      <div class="modal-stack">
        <div class="callout-bad">
          <UiIcon name="shield" :size="16" />
          <div>Your <b>10 existing codes</b> ({{ recoveryCodes.length - usedCount }} still unused) will be invalidated immediately. Anyone holding a printed copy will no longer be able to use them.</div>
        </div>
        <div class="def-block">
          <Mono dim>// when to do this</Mono>
          <div class="def-body">You're seeing this option to <b>regenerate</b> because either you used a code recently, or you suspect someone else has seen your saved codes. If neither, you can keep the existing ones.</div>
        </div>
        <label class="check-row">
          <input type="checkbox" v-model="regenAck" />
          I understand the old codes will stop working
        </label>
      </div>
      <template #footer>
        <UiButton variant="ghost" @click="regenOpen = false">Cancel</UiButton>
        <UiButton variant="danger" :disabled="!regenAck" @click="regenOpen = false; showCodesOpen = true; toast.ok('10 new codes generated')">
          <template #leading><UiIcon name="refresh" :size="13" /></template>
          Generate 10 new codes
        </UiButton>
      </template>
    </Modal>

    <!-- Export sign-in history modal -->
    <Modal :open="exportOpen" eyebrow="Sign-in history · export" title="Export sign-in history" size="md" @close="exportOpen = false">
      <div class="modal-stack">
        <div>
          <Eyebrow style="display: block; margin-bottom: 8px;">Period</Eyebrow>
          <div class="period">
            <button
              v-for="p in [
                { v: '30d', l: '30 days' },
                { v: '90d', l: '90 days' },
                { v: '12mo', l: '12 months' },
                { v: 'all', l: 'All time' },
              ]"
              :key="p.v"
              :class="{ active: exportPeriod === p.v }"
              @click="exportPeriod = p.v as typeof exportPeriod"
            >{{ p.l }}</button>
          </div>
        </div>

        <EnduserFormField label="Format">
          <div class="seg">
            <button :class="{ active: exportFormat === 'csv' }" @click="exportFormat = 'csv'">CSV</button>
            <button :class="{ active: exportFormat === 'json' }" @click="exportFormat = 'json'">JSON</button>
          </div>
        </EnduserFormField>

        <label class="check-row">
          <input type="checkbox" v-model="exportIncludeFailed" />
          Include failed attempts
        </label>

        <div>
          <Eyebrow style="display: block; margin-bottom: 8px;">Delivery</Eyebrow>
          <div class="delivery">
            <button
              :class="{ active: exportDelivery === 'download' }"
              @click="exportDelivery = 'download'"
            >
              <UiIcon name="download" :size="15" />
              <div class="d-text">
                <div class="d-l">Download now</div>
                <Mono dim>browser download</Mono>
              </div>
            </button>
            <button
              :class="{ active: exportDelivery === 'email' }"
              @click="exportDelivery = 'email'"
            >
              <UiIcon name="mail" :size="15" />
              <div class="d-text">
                <div class="d-l">Email to me</div>
                <Mono dim>sent to anne@dezky.com</Mono>
              </div>
            </button>
          </div>
        </div>

        <div class="def-block">
          <Mono dim>// estimated</Mono>
          <div class="def-body">~{{ exportRowEst }} rows · {{ exportIncludeFailed ? 'includes failed attempts' : 'successes only' }} · {{ exportFormat.toUpperCase() }}</div>
        </div>
      </div>
      <template #footer>
        <UiButton variant="ghost" @click="exportOpen = false">Cancel</UiButton>
        <UiButton variant="primary" @click="exportOpen = false; toast.ok(`Exported · ${exportFormat.toUpperCase()}`)">
          <template #leading><UiIcon :name="exportDelivery === 'email' ? 'mail' : 'download'" :size="13" /></template>
          {{ exportDelivery === 'email' ? 'Email export' : 'Download' }}
        </UiButton>
      </template>
    </Modal>
  </div>
</template>

<style scoped>
.tabs-wrap { padding: 0 40px; margin-top: 16px; }
.content { padding: 20px 40px 64px 40px; max-width: 900px; }
.stack { display: flex; flex-direction: column; gap: 16px; }

.card-header { margin-bottom: 16px; }
.card-header.with-actions { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.card-header .head-text { min-width: 0; flex: 1; }
.card-header h2 { font-family: var(--font-display); font-weight: 600; font-size: 17px; margin: 6px 0 0 0; letter-spacing: -0.015em; }
.card-header p { margin: 8px 0 0 0; font-size: 13px; color: var(--text-mute); line-height: 1.55; max-width: 600px; }

/* Password form */
.pwd-form { display: flex; flex-direction: column; gap: 14px; max-width: 480px; }
.pwd-input { position: relative; }
.pwd-input input { width: 100%; height: 36px; padding: 0 36px 0 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; font-size: 13px; font-family: inherit; color: var(--text); outline: none; box-sizing: border-box; }
.pwd-input input:focus { border-color: var(--text); background: var(--bg); }
.eye { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); background: transparent; border: none; cursor: pointer; padding: 6px; color: var(--text-mute); }
.eye:hover { color: var(--text); }

.strength { padding: 0; }
.strength-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.bar { height: 4px; background: var(--border); border-radius: 999px; overflow: hidden; }
.bar span { display: block; height: 100%; border-radius: 999px; transition: width 0.18s, background 0.18s; }
.criteria { list-style: none; padding: 0; margin: 10px 0 0 0; display: flex; flex-direction: column; gap: 6px; }
.criteria li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-mute); }
.criteria li[data-ok='true'] :deep(svg) { color: var(--ok); }
.criteria li[data-ok='false'] :deep(svg) { color: var(--bad); opacity: 0.55; }

.actions { display: flex; align-items: center; gap: 8px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border); }
.flash-ok { display: inline-flex; align-items: center; gap: 6px; color: var(--ok); font-size: 13px; }
.footer-note { margin-top: 14px; font-size: 12px; color: var(--text-mute); line-height: 1.6; }

/* Backup recovery email */
.recovery { display: flex; align-items: center; gap: 12px; }
.email-input {
  display: flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
  flex: 1; max-width: 360px;
}
.email-input input {
  flex: 1; border: none; outline: none; background: transparent;
  font-size: 13px; color: var(--text); font-family: inherit;
}
.email-input :deep(svg) { color: var(--text-mute); }

/* Callouts */
.callout-info {
  margin-top: 14px;
  padding: 12px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  font-size: 12px; color: var(--text-dim); line-height: 1.55;
  display: flex; gap: 10px; align-items: flex-start;
}
.callout-info :deep(svg) { color: var(--text-mute); margin-top: 2px; flex-shrink: 0; }
.callout-bad {
  padding: 14px;
  background: rgba(226, 48, 48, 0.06);
  border: 1px solid rgba(226, 48, 48, 0.20);
  border-radius: 6px;
  display: flex; gap: 10px;
  font-size: 13px; color: var(--text-dim); line-height: 1.5;
}
.callout-bad :deep(svg) { color: var(--bad); margin-top: 2px; flex-shrink: 0; }
.callout-warn {
  padding: 14px;
  background: rgba(232, 154, 31, 0.06);
  border: 1px solid rgba(232, 154, 31, 0.24);
  border-left: 3px solid var(--warn);
  border-radius: 6px;
  display: flex; gap: 12px; align-items: flex-start;
  font-size: 13px; color: var(--text-dim); line-height: 1.6;
}
.callout-warn :deep(svg) { color: var(--warn); margin-top: 2px; flex-shrink: 0; }
.cw-body { flex: 1; }
.cw-body b { color: var(--text); }

.callout-bad-strong {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px;
  background: rgba(226, 48, 48, 0.04);
  border: 1px solid rgba(226, 48, 48, 0.18);
  border-left: 3px solid var(--bad);
  border-radius: 6px;
}
.callout-bad-strong :deep(svg) { color: var(--bad); flex-shrink: 0; margin-top: 2px; }
.cb-text { flex: 1; font-size: 13px; }
.cb-title { font-weight: 600; }
.cb-text :deep(.mono) { color: var(--text-mute); margin-top: 4px; }

/* MFA */
.mfa-on { display: flex; align-items: center; gap: 14px; }
.mfa-shield { width: 44px; height: 44px; border-radius: 10px; background: rgba(31, 138, 91, 0.12); color: var(--ok); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.mfa-on h3 { font-family: var(--font-display); font-weight: 600; font-size: 16px; margin: 0; }
.mfa-on p { margin: 4px 0 0 0; font-size: 13px; color: var(--text-mute); }

.mfa-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 20px; border-bottom: 1px solid var(--border); }
.mfa-list { list-style: none; padding: 0; margin: 0; }
.mfa-list > li {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
}
.mfa-list > li:last-child { border-bottom: none; }
.mfa-icon {
  width: 36px; height: 36px; border-radius: 8px;
  background: var(--bg); border: 1px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-dim); flex-shrink: 0;
}
.mfa-text { flex: 1; min-width: 0; }
.mfa-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mfa-name { font-size: 13px; font-weight: 500; }
.mfa-trigger { display: inline-flex; }

.mfa-add {
  padding: 16px 20px;
  background: var(--bg);
  display: flex; gap: 8px;
}

/* Portaled MFA action menu */
.mfa-menu {
  position: fixed;
  min-width: 240px;
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  z-index: 100;
}
.mfa-menu button {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border-radius: 5px;
  background: transparent; border: none;
  cursor: pointer;
  font-family: inherit; font-size: 13px; text-align: left;
  color: var(--text);
}
.mfa-menu button:hover:not(:disabled) { background: var(--row-hover); }
.mfa-menu button:disabled { opacity: 0.5; cursor: not-allowed; color: var(--text-mute); }
.mfa-menu .danger { color: var(--bad); }
.mfa-menu .sep { display: block; height: 1px; background: var(--border); margin: 4px 0; }

/* Recovery codes */
.codes-summary {
  display: flex; align-items: center; gap: 18px;
  padding: 16px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
  margin-bottom: 16px;
}
.codes-tile {
  width: 40px; height: 40px; border-radius: 8px;
  background: var(--surface);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-dim); flex-shrink: 0;
}
.codes-meta { flex: 1; }
.codes-title { font-size: 14px; font-weight: 500; }

.codes-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
  margin-top: 18px;
  padding: 18px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
}
.code {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 5px;
  font-family: var(--font-mono); font-size: 14px; font-weight: 600;
}
.code.used { opacity: 0.35; text-decoration: line-through; }
.code-idx { font-size: 10px; color: var(--text-mute); min-width: 16px; }
.code-val { flex: 1; letter-spacing: 0.04em; }

/* History */
.hist-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.hist-intro { font-size: 13px; color: var(--text-mute); max-width: 540px; line-height: 1.5; }
.history { width: 100%; border-collapse: collapse; }
.history thead th {
  text-align: left;
  padding: 12px 22px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  font-weight: 500;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.history tbody td {
  padding: 12px 22px;
  font-size: 12px;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}
.history tbody tr:last-child td { border-bottom: none; }
.history .unknown { color: var(--warn); }

/* Modal body */
.modal-body { margin: 0 0 14px 0; font-size: 13px; line-height: 1.6; color: var(--text-dim); }
.modal-stack { display: flex; flex-direction: column; gap: 14px; }
.def-block { padding: 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; font-size: 12px; color: var(--text-dim); line-height: 1.55; }
.def-body { margin-top: 6px; }
.def-block dl { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.def-block dl > div { display: flex; gap: 12px; }
.def-block dt { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-mute); width: 110px; flex-shrink: 0; }
.def-block dd { margin: 0; font-size: 13px; color: var(--text); }

/* MFA setup wizard inner */
.wait-key { text-align: center; padding: 12px 0; }
.key-circle {
  width: 120px; height: 120px; border-radius: 999px;
  background: rgba(212, 255, 58, 0.10);
  border: 2px solid var(--accent);
  margin: 0 auto 18px auto;
  display: inline-flex; align-items: center; justify-content: center;
  animation: keyPulse 1.4s ease-in-out infinite;
}
.key-title { font-family: var(--font-display); font-weight: 600; font-size: 18px; }
@keyframes keyPulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.04); opacity: 0.85; } }

.qr-setup { display: flex; gap: 18px; align-items: center; padding: 20px; background: var(--bg); border-radius: 8px; margin-top: 8px; margin-bottom: 14px; }
.qr-big {
  width: 84px; height: 84px;
  display: grid; grid-template-columns: repeat(21, 1fr); gap: 0;
  background: #fff; padding: 6px; border-radius: 6px; flex-shrink: 0;
}
.qr-big span { display: block; aspect-ratio: 1; }
.qr-meta { flex: 1; }
.qr-key { font-family: var(--font-mono); font-size: 14px; font-weight: 600; letter-spacing: 0.04em; margin: 6px 0 10px 0; }

.success { display: flex; align-items: center; gap: 14px; }
.success-tile { width: 44px; height: 44px; border-radius: 10px; background: var(--accent); color: var(--accent-fg); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.success h3 { font-family: var(--font-display); font-weight: 600; font-size: 18px; margin: 0; }
.success-note { margin-top: 20px; padding: 14px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; font-size: 13px; color: var(--text-dim); line-height: 1.6; }

.check-row { display: flex; align-items: center; gap: 10px; font-size: 13px; cursor: pointer; }
.check-row input { width: 16px; height: 16px; accent-color: var(--text); }

/* Period / format chip groups */
.period { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.period button {
  padding: 8px 0; border-radius: 6px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text);
  font-family: inherit; font-size: 12px; font-weight: 500;
  cursor: pointer;
}
.period button.active { background: var(--text); color: var(--bg); border-color: var(--text); }

.seg { display: flex; border: 1px solid var(--border); border-radius: 6px; padding: 2px; background: var(--surface); }
.seg button {
  flex: 1; padding: 6px 0; border: none; border-radius: 4px;
  background: transparent; color: var(--text);
  font-family: inherit; font-size: 12px; font-weight: 500;
  cursor: pointer;
}
.seg button.active { background: var(--text); color: var(--bg); }

.delivery { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.delivery button {
  padding: 12px; border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface); color: var(--text);
  font-family: inherit; cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  text-align: left;
}
.delivery button.active { border-color: var(--text); background: var(--bg); }
.d-text { flex: 1; }
.d-l { font-size: 13px; font-weight: 500; }
</style>
