diff --git a/account.php b/account.php
new file mode 100644
index 0000000..4d54400
--- /dev/null
+++ b/account.php
@@ -0,0 +1,230 @@
+ ['Free', '#f3f4f6', '#374151'],
+ 'plus' => ['Plus', '#ddd6fe', '#5b21b6'],
+ 'pro' => ['Pro', '#bfdbfe', '#1e40af'],
+ 'caveau' => ['CaveauAI', '#d1fae5', '#065f46'],
+];
+$tierLabel = $tierLabels[$tier] ?? $tierLabels['free'];
+
+$monthlyAllowance = $detail ? FreeTier::monthlyAllowance($tier) : 0;
+$creditsUsed = $detail ? max(0, $monthlyAllowance - (int)$detail['balance']) : 0;
+
+// Team (Caveau sessions only)
+$teamMembers = [];
+if (!$isSso && !empty($authUser['client_id'])) {
+ try {
+ $db = dbnToolsDb();
+ $stmt = $db->prepare(
+ "SELECT cu.email, cu.role, cu.created_at
+ FROM client_users cu
+ WHERE cu.client_id = ?
+ ORDER BY FIELD(cu.role,'owner','admin','editor','viewer'), cu.created_at ASC"
+ );
+ $stmt->execute([(int)$authUser['client_id']]);
+ $teamMembers = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ } catch (Throwable $e) {
+ // non-fatal
+ }
+}
+
+// Renewal date label
+$renewalLabel = '';
+if ($detail) {
+ if (!empty($detail['trial_active']) && !empty($detail['trial_expires_at'])) {
+ $renewalLabel = date('d M Y', strtotime((string)$detail['trial_expires_at']))
+ . ' (' . (int)$detail['trial_days_remaining'] . ' ' . dbnToolsT('trial_days_left', $uiLang) . ')';
+ } elseif (!empty($detail['subscription_period_end'])) {
+ $renewalLabel = date('d M Y', strtotime((string)$detail['subscription_period_end']));
+ }
+}
+?>
+
+
+
+
+
+
= htmlspecialchars(dbnToolsT('account_title', $uiLang)) ?>
+
= htmlspecialchars($email) ?>
+
+
+
+
+
+ = htmlspecialchars(dbnToolsT('account_credits', $uiLang)) ?>
+
+
+ = htmlspecialchars($tierLabel[0]) ?>
+
+
+
+ = htmlspecialchars(dbnToolsT('trial_active_label', $uiLang)) ?>
+
+
+
+
+ = number_format((int)$detail['balance'] + (int)$detail['bonus_balance'], 0, ',', ' ') ?>
+
+
+ = (int)$detail['balance'] ?> = htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?>
+ · = (int)$detail['bonus_balance'] ?> = htmlspecialchars(dbnToolsT('credits_bonus', $uiLang)) ?>
+
+
+
+
+ = htmlspecialchars(dbnToolsT('renewal_date', $uiLang)) ?>
+ = htmlspecialchars($renewalLabel) ?>
+
+
+
+
+ Upgrade plan →
+
+
+
+
+
+
+ = htmlspecialchars(dbnToolsT('account_profile', $uiLang)) ?>
+
+
+ Email
+ = htmlspecialchars($email) ?>
+
+
+ Login
+
+
+ = htmlspecialchars(dbnToolsT('login_method_sso', $uiLang)) ?>
+
+ = htmlspecialchars(dbnToolsT('login_method_email', $uiLang)) ?>
+
+
+
+
+
+ Role
+ = htmlspecialchars($role) ?>
+
+
+
+
+
+
+ = htmlspecialchars(dbnToolsT('account_team', $uiLang)) ?>
+
+
+ = htmlspecialchars(dbnToolsT('team_single_sso', $uiLang)) ?>
+
+ —
+
+
+
+
+ | Email |
+ Role |
+ Added |
+
+
+
+
+
+ | = htmlspecialchars($member['email']) ?> |
+ = htmlspecialchars($member['role']) ?> |
+ = htmlspecialchars(date('d M Y', strtotime((string)$member['created_at']))) ?> |
+
+
+
+
+
+
+
+
+
+
+ = htmlspecialchars(dbnToolsT('account_usage', $uiLang)) ?>
+
+
+ = htmlspecialchars(dbnToolsT('usage_credits_used', $uiLang)) ?>
+ = $creditsUsed ?> / = $monthlyAllowance ?>
+
+
+ 0): ?>
+
+
+
+ 0):
+ $storagePct = min(100, round((int)$detail['storage_used_bytes'] / (int)$detail['storage_quota_bytes'] * 100));
+ $usedMb = round((int)$detail['storage_used_bytes'] / 1048576, 1);
+ $quotaMb = round((int)$detail['storage_quota_bytes'] / 1048576, 0);
+ ?>
+
+ = htmlspecialchars(dbnToolsT('usage_storage_used', $uiLang)) ?>
+ = $usedMb ?> / = $quotaMb ?> MB
+
+
+
+
+
+ = htmlspecialchars(dbnToolsT('usage_log_coming', $uiLang)) ?>
+
+
+
+
+
+ = htmlspecialchars(dbnToolsT('earn_credits_eyebrow', $uiLang)) ?>
+ = htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?>
+
+ →
+
+
+
+
+
+
+
+
+
+
diff --git a/api/corpus-summary.php b/api/corpus-summary.php
new file mode 100644
index 0000000..c0eedd3
--- /dev/null
+++ b/api/corpus-summary.php
@@ -0,0 +1,45 @@
+ 'auth_required']);
+ exit;
+}
+
+try {
+ $tenant = dbnToolsEnsureDashboardTenant();
+} catch (Throwable $e) {
+ http_response_code(503);
+ echo json_encode(['error' => 'tenant_unavailable']);
+ exit;
+}
+
+try {
+ $db = dbnToolsDb();
+ $stmt = $db->prepare(
+ "SELECT COUNT(*) AS doc_count, MAX(created_at) AS last_updated
+ FROM client_documents
+ WHERE client_id = ? AND status = 'ready'"
+ );
+ $stmt->execute([(int)$tenant['client_id']]);
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ echo json_encode([
+ 'doc_count' => (int)($row['doc_count'] ?? 0),
+ 'last_updated' => $row['last_updated'] ?? null,
+ ]);
+} catch (Throwable $e) {
+ http_response_code(500);
+ echo json_encode(['error' => 'db_error']);
+}
diff --git a/assets/css/tools.css b/assets/css/tools.css
index a0a229e..61e168c 100644
--- a/assets/css/tools.css
+++ b/assets/css/tools.css
@@ -8814,3 +8814,362 @@ body.lt-landing {
color: rgba(22,19,15,0.80);
text-decoration: underline;
}
+
+/* ═══════════════════════════════════════════════════════════════
+ UNIFIED SITE NAVBAR — .dbn-nav
+ ═══════════════════════════════════════════════════════════════ */
+.dbn-nav {
+ display: flex;
+ align-items: center;
+ gap: 0;
+ background: var(--dbn-blue, #00205b);
+ height: 52px;
+ padding: 0 1.5rem;
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+ border-bottom: 2px solid var(--dbn-red, #ba0c2f);
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 0.875rem;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
+}
+
+/* Brand */
+.dbn-nav__brand {
+ display: flex;
+ align-items: center;
+ gap: 0.45rem;
+ color: #fff;
+ text-decoration: none;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+.dbn-nav__brandmark { font-size: 1.15rem; }
+.dbn-nav__brandname { font-size: 0.9rem; }
+
+/* Centre links */
+.dbn-nav__links {
+ display: flex;
+ align-items: stretch;
+ gap: 0;
+ margin-left: 1.75rem;
+}
+.dbn-nav__link {
+ display: flex;
+ align-items: center;
+ gap: 0.3rem;
+ color: rgba(255, 255, 255, 0.80);
+ padding: 0 1rem;
+ height: 52px;
+ text-decoration: none;
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 0.875rem;
+ white-space: nowrap;
+ transition: color 0.12s, background 0.12s;
+}
+.dbn-nav__link:hover,
+.dbn-nav__link.is-active {
+ color: #fff;
+ background: rgba(255, 255, 255, 0.10);
+}
+.dbn-nav__caret { font-size: 0.65rem; opacity: 0.7; }
+
+/* Dropdown */
+.dbn-nav__dropdown { position: relative; display: flex; align-items: stretch; }
+.dbn-nav__panel {
+ display: none;
+ position: absolute;
+ top: calc(100% + 2px);
+ left: 0;
+ min-width: 240px;
+ background: #fff;
+ border: 1px solid var(--line, #d8dde7);
+ border-radius: 10px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.08);
+ padding: 0.4rem 0;
+ z-index: 1100;
+ animation: navPanelIn 0.12s ease;
+}
+@keyframes navPanelIn {
+ from { opacity: 0; transform: translateY(-4px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+.dbn-nav__dropdown.is-open .dbn-nav__panel { display: block; }
+.dbn-nav__panel-item {
+ display: grid;
+ grid-template-columns: 2.5rem 1fr;
+ grid-template-rows: auto auto;
+ align-items: center;
+ padding: 0.55rem 1rem;
+ color: var(--dbn-ink, #16130f);
+ text-decoration: none;
+ font-size: 0.85rem;
+ gap: 0 0;
+ transition: background 0.1s;
+}
+.dbn-nav__panel-item:hover { background: var(--soft-teal, #e7f5f2); }
+.dbn-nav__panel-badge {
+ grid-row: 1 / 3;
+ font-size: 0.7rem;
+ font-weight: 700;
+ color: var(--dbn-blue, #00205b);
+ background: rgba(0, 32, 91, 0.08);
+ border-radius: 4px;
+ padding: 2px 5px;
+ text-align: center;
+ align-self: center;
+ font-family: 'JetBrains Mono', monospace;
+ letter-spacing: 0;
+}
+.dbn-nav__panel-label {
+ grid-column: 2;
+ font-weight: 600;
+ color: var(--dbn-ink, #16130f);
+ line-height: 1.2;
+}
+.dbn-nav__panel-sub {
+ grid-column: 2;
+ font-size: 0.75rem;
+ color: var(--muted, #667085);
+ line-height: 1.2;
+}
+
+/* Right side */
+.dbn-nav__right {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ margin-left: auto;
+}
+
+/* Language switcher */
+.dbn-nav__langs {
+ display: flex;
+ gap: 1px;
+}
+.dbn-nav__lang {
+ color: rgba(255, 255, 255, 0.55);
+ font-size: 0.7rem;
+ font-weight: 700;
+ text-decoration: none;
+ padding: 3px 5px;
+ border-radius: 3px;
+ letter-spacing: 0.04em;
+ transition: color 0.12s, background 0.12s;
+}
+.dbn-nav__lang:hover { color: #fff; background: rgba(255,255,255,0.12); }
+.dbn-nav__lang.is-active { color: #fff; background: rgba(255,255,255,0.18); }
+
+/* Auth right */
+.dbn-nav__account-link {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ text-decoration: none;
+ padding: 0 0.25rem;
+ gap: 1px;
+}
+.dbn-nav__username {
+ color: #fff;
+ font-size: 0.8rem;
+ font-weight: 600;
+ line-height: 1;
+ max-width: 120px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.dbn-nav__account-badge {
+ color: rgba(255, 255, 255, 0.58);
+ font-size: 0.68rem;
+ line-height: 1;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+}
+.dbn-nav__account-link:hover .dbn-nav__username { color: #fff; }
+.dbn-nav__account-link:hover .dbn-nav__account-badge { color: rgba(255,255,255,0.82); }
+
+.dbn-nav__logout,
+.dbn-nav__login {
+ color: rgba(255, 255, 255, 0.80);
+ font-size: 0.82rem;
+ text-decoration: none;
+ padding: 0.3rem 0.65rem;
+ border-radius: 5px;
+ white-space: nowrap;
+ transition: color 0.12s, background 0.12s;
+}
+.dbn-nav__logout:hover { color: #fff; background: rgba(255,255,255,0.12); }
+.dbn-nav__login {
+ border: 1px solid rgba(255, 255, 255, 0.30);
+ font-weight: 600;
+}
+.dbn-nav__login:hover { color: #fff; background: rgba(255,255,255,0.12); border-color: rgba(255,255,255,0.50); }
+
+/* Push down any direct sibling so nav doesn't overlap */
+.dbn-nav + .app-shell,
+.dbn-nav + main,
+.dbn-nav + .dash-shell { margin-top: 0; }
+
+/* ═══════════════════════════════════════════════════════════════
+ ACCOUNT PAGE — .account-*
+ ═══════════════════════════════════════════════════════════════ */
+.account-shell {
+ max-width: 780px;
+ margin: 2.5rem auto 4rem;
+ padding: 0 1.5rem;
+}
+.account-hero {
+ margin-bottom: 2rem;
+}
+.account-hero h1 {
+ font-family: 'Crimson Pro', serif;
+ font-size: 2rem;
+ font-weight: 700;
+ color: var(--dbn-blue, #00205b);
+ margin: 0 0 0.25rem;
+}
+.account-hero p {
+ color: var(--muted, #667085);
+ font-size: 0.9rem;
+ margin: 0;
+}
+.account-section {
+ background: #fff;
+ border: 1px solid var(--line, #d8dde7);
+ border-radius: 12px;
+ padding: 1.5rem;
+ margin-bottom: 1.25rem;
+}
+.account-section__title {
+ font-size: 0.72rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--muted, #667085);
+ margin: 0 0 1rem;
+ padding-bottom: 0.6rem;
+ border-bottom: 1px solid var(--line, #d8dde7);
+}
+.account-row {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.55rem 0;
+ border-bottom: 1px solid var(--line, #d8dde7);
+ font-size: 0.88rem;
+}
+.account-row:last-child { border-bottom: none; }
+.account-row__label { color: var(--muted, #667085); flex-shrink: 0; }
+.account-row__value { font-weight: 600; color: var(--dbn-ink, #16130f); text-align: right; }
+.account-row__value a { color: var(--dbn-blue, #00205b); text-decoration: none; }
+.account-row__value a:hover { text-decoration: underline; }
+
+.account-tier-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 3px 10px;
+ border-radius: 999px;
+ font-size: 0.78rem;
+ font-weight: 700;
+ letter-spacing: 0.03em;
+}
+
+.account-credits-big {
+ font-family: 'Crimson Pro', serif;
+ font-size: 2.5rem;
+ font-weight: 700;
+ color: var(--dbn-blue, #00205b);
+ line-height: 1;
+ margin: 0.5rem 0 0.25rem;
+}
+.account-credits-sub {
+ font-size: 0.83rem;
+ color: var(--muted, #667085);
+ margin: 0 0 1rem;
+}
+
+.account-upgrade-cta {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ background: var(--dbn-blue, #00205b);
+ color: #fff;
+ text-decoration: none;
+ padding: 0.55rem 1.1rem;
+ border-radius: 8px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ margin-top: 0.75rem;
+ transition: background 0.15s;
+}
+.account-upgrade-cta:hover { background: #001845; }
+
+.account-team-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.85rem;
+}
+.account-team-table th {
+ text-align: left;
+ color: var(--muted, #667085);
+ font-weight: 600;
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ padding: 0.4rem 0;
+ border-bottom: 1px solid var(--line, #d8dde7);
+}
+.account-team-table td {
+ padding: 0.6rem 0;
+ border-bottom: 1px solid var(--line, #d8dde7);
+ color: var(--dbn-ink, #16130f);
+}
+.account-team-table tr:last-child td { border-bottom: none; }
+.account-role-pill {
+ display: inline-block;
+ padding: 1px 8px;
+ border-radius: 4px;
+ font-size: 0.72rem;
+ font-weight: 600;
+ background: rgba(0,32,91,0.08);
+ color: var(--dbn-blue, #00205b);
+}
+
+.account-usage-bar-wrap {
+ height: 8px;
+ background: var(--line, #d8dde7);
+ border-radius: 4px;
+ overflow: hidden;
+ margin-top: 0.4rem;
+}
+.account-usage-bar {
+ height: 100%;
+ background: var(--dbn-blue, #00205b);
+ border-radius: 4px;
+ transition: width 0.5s ease;
+}
+
+.account-survey-cta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ background: #fef3c7;
+ border-radius: 8px;
+ padding: 0.9rem 1.1rem;
+ margin-top: 0.75rem;
+ text-decoration: none;
+ color: #92400e;
+}
+.account-survey-cta strong { display: block; font-size: 0.9rem; }
+.account-survey-cta span { font-size: 0.8rem; opacity: 0.85; }
+.account-survey-cta:hover { background: #fde68a; }
+
diff --git a/dashboard.php b/dashboard.php
index a865694..bb8bbed 100644
--- a/dashboard.php
+++ b/dashboard.php
@@ -47,68 +47,50 @@ window.DBN_TOOLS_AUTHENTICATED = true;
window.DBN_TOOLS_LANG = = json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
+
+