Bump dashboard + pricing typography to readable desktop scale

Cards, status row, account bar, and pricing tiers were rendering at
.78–.94rem with 18–20px padding — felt microscopic on 1440+ viewports.
Lift every token ~12–15% and widen card padding so the Crimson Pro /
IBM Plex editorial character reads as intended.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 16:58:23 +02:00
parent f609da6ad7
commit 302bb44f70
3 changed files with 303 additions and 174 deletions
+4 -3
View File
@@ -242,14 +242,15 @@ body {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 18px 20px; gap: .55rem;
padding: 26px 28px;
border: 1px solid var(--dbn-line); border: 1px solid var(--dbn-line);
border-radius: 8px; border-radius: 10px;
background: rgba(255, 255, 255, 0.82); background: rgba(255, 255, 255, 0.82);
color: var(--dbn-ink); color: var(--dbn-ink);
text-decoration: none; text-decoration: none;
overflow: hidden; overflow: hidden;
min-height: 200px; min-height: 260px;
cursor: pointer; cursor: pointer;
} }
+43 -43
View File
@@ -227,9 +227,9 @@ $langSuffix = $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '';
/* card footer links always sit above the div onclick */ /* card footer links always sit above the div onclick */
.dash-card-footer a, .dash-card-footer a,
.dash-card-footer button { position: relative; z-index: 1; } .dash-card-footer button { position: relative; z-index: 1; }
.dash-card-footer { padding-top: 0.65rem; margin-top: auto; border-top: 1px solid rgba(0,0,0,.07); display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; } .dash-card-footer { padding-top: 0.85rem; margin-top: auto; border-top: 1px solid rgba(0,0,0,.07); display: flex; align-items: center; gap: 0.65rem; flex-wrap: wrap; }
/* pill badges on acct bar */ /* pill badges on acct bar */
.dash-tier-badge { display: inline-flex; align-items: center; font-size: .72rem; font-weight: 700; padding: 2px 10px; border-radius: 999px; text-transform: uppercase; letter-spacing: .06em; border: 1px solid currentColor; flex-shrink: 0; } .dash-tier-badge { display: inline-flex; align-items: center; font-size: .82rem; font-weight: 700; padding: 3px 12px; border-radius: 999px; text-transform: uppercase; letter-spacing: .06em; border: 1px solid currentColor; flex-shrink: 0; }
</style> </style>
</head> </head>
<body data-authenticated="true" class="lt-app"> <body data-authenticated="true" class="lt-app">
@@ -258,68 +258,68 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
?> ?>
<!-- ── Account overview bar ─────────────────────────────────────── --> <!-- ── Account overview bar ─────────────────────────────────────── -->
<div style="background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:.9rem 1.25rem; margin:0 0 1.25rem; display:flex; align-items:center; justify-content:space-between; gap:.75rem; flex-wrap:wrap;"> <div style="background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:1.1rem 1.5rem; margin:0 0 1.25rem; display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap;">
<div style="display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; min-width:0;"> <div style="display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; min-width:0;">
<span class="dash-tier-badge" style="background:<?= $tierLabel[1] ?>; color:<?= $tierLabel[2] ?>;"><?= htmlspecialchars($tierLabel[0]) ?></span> <span class="dash-tier-badge" style="background:<?= $tierLabel[1] ?>; color:<?= $tierLabel[2] ?>;"><?= htmlspecialchars($tierLabel[0]) ?></span>
<?php if (!empty($dashDetail['trial_active'])): ?> <?php if (!empty($dashDetail['trial_active'])): ?>
<span class="dash-tier-badge" style="background:#fef3c7; color:#92400e;"><?= htmlspecialchars(sprintf($dl['trial_badge'], (int)$dashDetail['trial_days_remaining'])) ?></span> <span class="dash-tier-badge" style="background:#fef3c7; color:#92400e;"><?= htmlspecialchars(sprintf($dl['trial_badge'], (int)$dashDetail['trial_days_remaining'])) ?></span>
<?php endif; ?> <?php endif; ?>
<span style="font-size:.95rem; font-weight:700; color:#00205B; white-space:nowrap;"><?= number_format($eff) ?> <?= $uiLang === 'no' ? 'kred.' : 'credits' ?></span> <span style="font-size:1.08rem; font-weight:700; color:#00205B; white-space:nowrap;"><?= number_format($eff) ?> <?= $uiLang === 'no' ? 'kred.' : 'credits' ?></span>
<span style="color:#9ca3af; font-size:.8rem; white-space:nowrap;"><?= (int)$dashDetail['balance'] ?> <?= $uiLang === 'no' ? 'månedlige' : 'monthly' ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalt' : 'prepaid' ?></span> <span style="color:#9ca3af; font-size:.92rem; white-space:nowrap;"><?= (int)$dashDetail['balance'] ?> <?= $uiLang === 'no' ? 'månedlige' : 'monthly' ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalt' : 'prepaid' ?></span>
<?php if ($dashNextBilling): ?> <?php if ($dashNextBilling): ?>
<span style="color:#6b7280; font-size:.8rem; white-space:nowrap;"><?= htmlspecialchars($dl[$dashNextBillingKey]) ?>: <strong style="color:#374151;"><?= htmlspecialchars($dashNextBilling) ?></strong></span> <span style="color:#6b7280; font-size:.92rem; white-space:nowrap;"><?= htmlspecialchars($dl[$dashNextBillingKey]) ?>: <strong style="color:#374151;"><?= htmlspecialchars($dashNextBilling) ?></strong></span>
<?php endif; ?> <?php endif; ?>
<?php if ($dashEmail): ?> <?php if ($dashEmail): ?>
<span style="color:#d1d5db; font-size:.8rem; white-space:nowrap; display:none;" class="dash-email-sep">·</span> <span style="color:#d1d5db; font-size:.92rem; white-space:nowrap; display:none;" class="dash-email-sep">·</span>
<span style="color:#9ca3af; font-size:.8rem; white-space:nowrap;"><?= htmlspecialchars($dl['signed_in_as']) ?>: <strong style="color:#374151;"><?= htmlspecialchars($dashEmail) ?></strong></span> <span style="color:#9ca3af; font-size:.92rem; white-space:nowrap;"><?= htmlspecialchars($dl['signed_in_as']) ?>: <strong style="color:#374151;"><?= htmlspecialchars($dashEmail) ?></strong></span>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div style="display:flex; align-items:center; gap:.5rem; flex-shrink:0; flex-wrap:wrap;"> <div style="display:flex; align-items:center; gap:.5rem; flex-shrink:0; flex-wrap:wrap;">
<?php if ($isPaid): ?> <?php if ($isPaid): ?>
<a href="/billing.php" style="color:#6b7280; font-size:.8rem; text-decoration:none; border:1px solid #e5e7eb; padding:5px 12px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['manage_plan']) ?></a> <a href="/billing.php" style="color:#6b7280; font-size:.92rem; text-decoration:none; border:1px solid #e5e7eb; padding:7px 14px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['manage_plan']) ?></a>
<?php else: ?> <?php else: ?>
<a href="/pricing.php" style="background:#00205B; color:#fff; font-size:.8rem; font-weight:700; text-decoration:none; padding:5px 12px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['upgrade_plan']) ?> →</a> <a href="/pricing.php" style="background:#00205B; color:#fff; font-size:.92rem; font-weight:700; text-decoration:none; padding:7px 14px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['upgrade_plan']) ?> →</a>
<?php endif; ?> <?php endif; ?>
<a href="/pricing.php#topup" style="background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; font-size:.8rem; font-weight:600; text-decoration:none; padding:5px 12px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['top_up']) ?> →</a> <a href="/pricing.php#topup" style="background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; font-size:.92rem; font-weight:600; text-decoration:none; padding:7px 14px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['top_up']) ?> →</a>
</div> </div>
</div> </div>
<!-- ── Status cards row ─────────────────────────────────────────── --> <!-- ── Status cards row ─────────────────────────────────────────── -->
<section class="dashboard-status-row" style="display:grid; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); gap:1rem; margin:0 0 1.25rem;"> <section class="dashboard-status-row" style="display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:1.1rem; margin:0 0 1.25rem;">
<div class="status-card" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem;"> <div class="status-card" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.3rem 1.5rem;">
<p style="margin:0; color:#6b7280; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('credits_available', $uiLang)) ?></p> <p style="margin:0; color:#6b7280; font-size:.95rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('credits_available', $uiLang)) ?></p>
<p style="margin:.35rem 0 0; font-size:1.8rem; font-weight:700; color:#00205B;"><?= number_format($eff, 0, ',', ' ') ?></p> <p style="margin:.35rem 0 0; font-size:2rem; font-weight:700; color:#00205B;"><?= number_format($eff, 0, ',', ' ') ?></p>
<p style="margin:0; color:#6b7280; font-size:.85rem;"><?= (int)$dashDetail['balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalte' : 'prepaid' ?> · <a href="/billing.php"><?= htmlspecialchars(dbnToolsT('details_link', $uiLang)) ?></a></p> <p style="margin:0; color:#6b7280; font-size:.95rem;"><?= (int)$dashDetail['balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalte' : 'prepaid' ?> · <a href="/billing.php"><?= htmlspecialchars(dbnToolsT('details_link', $uiLang)) ?></a></p>
</div> </div>
<?php if ($isPaid): ?> <?php if ($isPaid): ?>
<a class="status-card" href="/min-sak.php" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none; color:inherit;"> <a class="status-card" href="/min-sak.php" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.3rem 1.5rem; text-decoration:none; color:inherit;">
<p style="margin:0; color:#6b7280; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('my_case', $uiLang)) ?></p> <p style="margin:0; color:#6b7280; font-size:.95rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('my_case', $uiLang)) ?></p>
<p style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?> →</p> <p style="margin:.35rem 0 0; font-size:1.55rem; font-weight:700; color:#00205B;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?> →</p>
<?php <?php
$used = (int)$dashDetail['storage_used_bytes']; $used = (int)$dashDetail['storage_used_bytes'];
$quota = (int)$dashDetail['storage_quota_bytes']; $quota = (int)$dashDetail['storage_quota_bytes'];
$usedMb = $used > 0 ? round($used / 1048576, 1) : 0; $usedMb = $used > 0 ? round($used / 1048576, 1) : 0;
$quotaMb = $quota > 0 ? round($quota / 1048576, 0) : 0; $quotaMb = $quota > 0 ? round($quota / 1048576, 0) : 0;
?> ?>
<p style="margin:0; color:#6b7280; font-size:.85rem;"><?= $usedMb ?> MB / <?= $quotaMb ?> MB</p> <p style="margin:0; color:#6b7280; font-size:.95rem;"><?= $usedMb ?> MB / <?= $quotaMb ?> MB</p>
</a> </a>
<?php else: ?> <?php else: ?>
<a class="status-card" href="/pricing.php" style="background:linear-gradient(135deg,#00205B,#003478); color:#fff; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none;"> <a class="status-card" href="/pricing.php" style="background:linear-gradient(135deg,#00205B,#003478); color:#fff; border-radius:10px; padding:1.3rem 1.5rem; text-decoration:none;">
<p style="margin:0; opacity:.85; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?></p> <p style="margin:0; opacity:.85; font-size:.95rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?></p>
<p style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('upload_documents', $uiLang)) ?> →</p> <p style="margin:.35rem 0 0; font-size:1.55rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('upload_documents', $uiLang)) ?> →</p>
<p style="margin:0; opacity:.85; font-size:.85rem;"><?= htmlspecialchars(dbnToolsT('upgrade_from_plus', $uiLang)) ?></p> <p style="margin:0; opacity:.85; font-size:.95rem;"><?= htmlspecialchars(dbnToolsT('upgrade_from_plus', $uiLang)) ?></p>
</a> </a>
<?php endif; ?> <?php endif; ?>
<a id="corpusSummaryCard" class="status-card" href="/dashboard/" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none; color:inherit; display:block;"> <a id="corpusSummaryCard" class="status-card" href="/dashboard/" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.3rem 1.5rem; text-decoration:none; color:inherit; display:block;">
<p style="margin:0; color:#6b7280; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('my_corpus', $uiLang)) ?></p> <p style="margin:0; color:#6b7280; font-size:.95rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('my_corpus', $uiLang)) ?></p>
<p id="corpusDocCount" style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;">—</p> <p id="corpusDocCount" style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;">—</p>
<p id="corpusUpdated" style="margin:0; color:#6b7280; font-size:.85rem;"><?= htmlspecialchars(dbnToolsT('open_corpus', $uiLang)) ?> →</p> <p id="corpusUpdated" style="margin:0; color:#6b7280; font-size:.95rem;"><?= htmlspecialchars(dbnToolsT('open_corpus', $uiLang)) ?> →</p>
</a> </a>
<?php if ($showSurveyCta): ?> <?php if ($showSurveyCta): ?>
<a class="status-card" href="https://dobetternorge.no/survey.php" style="background:#fef3c7; color:#92400e; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none;"> <a class="status-card" href="https://dobetternorge.no/survey.php" style="background:#fef3c7; color:#92400e; border-radius:10px; padding:1.3rem 1.5rem; text-decoration:none;">
<p style="margin:0; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em; opacity:.85;"><?= htmlspecialchars(dbnToolsT('earn_credits_eyebrow', $uiLang)) ?></p> <p style="margin:0; font-size:.95rem; text-transform:uppercase; letter-spacing:.06em; opacity:.85;"><?= htmlspecialchars(dbnToolsT('earn_credits_eyebrow', $uiLang)) ?></p>
<p style="margin:.35rem 0 0; font-size:1.2rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('survey_btn', $uiLang)) ?> →</p> <p style="margin:.35rem 0 0; font-size:1.2rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('survey_btn', $uiLang)) ?> →</p>
<p style="margin:0; font-size:.85rem; opacity:.85;"><?= htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?></p> <p style="margin:0; font-size:.95rem; opacity:.85;"><?= htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?></p>
</a> </a>
<?php endif; ?> <?php endif; ?>
</section> </section>
@@ -337,16 +337,16 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
onclick="location.href='<?= $wbUrl ?>'" onclick="location.href='<?= $wbUrl ?>'"
onkeydown="if(event.key==='Enter'||event.key===' ')location.href='<?= $wbUrl ?>'"> onkeydown="if(event.key==='Enter'||event.key===' ')location.href='<?= $wbUrl ?>'">
<div style="display:flex; align-items:center; gap:.55rem; margin-bottom:.55rem; flex-wrap:wrap;"> <div style="display:flex; align-items:center; gap:.55rem; margin-bottom:.55rem; flex-wrap:wrap;">
<span style="font-size:1.5rem; line-height:1; flex-shrink:0;" aria-hidden="true">🗂️</span> <span style="font-size:1.7rem; line-height:1; flex-shrink:0;" aria-hidden="true">🗂️</span>
<span style="font-size:1rem; font-weight:700; color:#111827; flex:1; min-width:0;"><?= htmlspecialchars($workbench['label']) ?></span> <span style="font-size:1.08rem; font-weight:700; color:#111827; flex:1; min-width:0;"><?= htmlspecialchars($workbench['label']) ?></span>
<code onclick="event.stopPropagation();" data-copy-slug="dbn.case_workbench_plan" <code onclick="event.stopPropagation();" data-copy-slug="dbn.case_workbench_plan"
title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>" title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>"
style="font-size:.68rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#64748b; padding:1px 7px; border-radius:4px; cursor:pointer; white-space:nowrap; flex-shrink:0;">dbn.case_workbench_plan</code> style="font-size:.78rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#64748b; padding:3px 9px; border-radius:4px; cursor:pointer; white-space:nowrap; flex-shrink:0;">dbn.case_workbench_plan</code>
</div> </div>
<p style="margin:0; font-size:.84rem; color:#4b5563; line-height:1.5;"><?= htmlspecialchars($workbench['description']) ?></p> <p style="margin:0; font-size:.98rem; color:#4b5563; line-height:1.55;"><?= htmlspecialchars($workbench['description']) ?></p>
<div class="dash-card-footer"> <div class="dash-card-footer">
<a href="<?= $wbUrl ?>" onclick="event.stopPropagation();" <a href="<?= $wbUrl ?>" onclick="event.stopPropagation();"
style="margin-left:auto; color:#00205B; font-size:.82rem; font-weight:700; text-decoration:none; white-space:nowrap;"> style="margin-left:auto; color:#00205B; font-size:.94rem; font-weight:700; text-decoration:none; white-space:nowrap;">
<?= htmlspecialchars(dbnToolsT('enter_workbench', $uiLang)) ?> → <?= htmlspecialchars(dbnToolsT('enter_workbench', $uiLang)) ?> →
</a> </a>
</div> </div>
@@ -363,28 +363,28 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
onclick="location.href='<?= $cardUrl ?>'" onclick="location.href='<?= $cardUrl ?>'"
onkeydown="if(event.key==='Enter'||event.key===' ')location.href='<?= $cardUrl ?>'"> onkeydown="if(event.key==='Enter'||event.key===' ')location.href='<?= $cardUrl ?>'">
<div style="display:flex; align-items:center; gap:.55rem; margin-bottom:.55rem; flex-wrap:wrap;"> <div style="display:flex; align-items:center; gap:.55rem; margin-bottom:.55rem; flex-wrap:wrap;">
<span style="font-size:1.5rem; line-height:1; flex-shrink:0;" aria-hidden="true"><?= $emoji ?></span> <span style="font-size:1.7rem; line-height:1; flex-shrink:0;" aria-hidden="true"><?= $emoji ?></span>
<span style="font-size:1rem; font-weight:700; color:#111827; flex:1; min-width:0;"><?= htmlspecialchars($item['label']) ?></span> <span style="font-size:1.08rem; font-weight:700; color:#111827; flex:1; min-width:0;"><?= htmlspecialchars($item['label']) ?></span>
<?php if ($mcpSlug): ?> <?php if ($mcpSlug): ?>
<code onclick="event.stopPropagation();" data-copy-slug="<?= htmlspecialchars($mcpSlug) ?>" <code onclick="event.stopPropagation();" data-copy-slug="<?= htmlspecialchars($mcpSlug) ?>"
title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>" title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>"
style="font-size:.68rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#64748b; padding:1px 7px; border-radius:4px; cursor:pointer; white-space:nowrap; flex-shrink:0;"><?= htmlspecialchars($mcpSlug) ?></code> style="font-size:.78rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#64748b; padding:3px 9px; border-radius:4px; cursor:pointer; white-space:nowrap; flex-shrink:0;"><?= htmlspecialchars($mcpSlug) ?></code>
<?php endif; ?> <?php endif; ?>
</div> </div>
<p style="margin:0; font-size:.84rem; color:#4b5563; line-height:1.5;"><?= htmlspecialchars($item['description']) ?></p> <p style="margin:0; font-size:.98rem; color:#4b5563; line-height:1.55;"><?= htmlspecialchars($item['description']) ?></p>
<div class="dash-card-footer"> <div class="dash-card-footer">
<?php if ($docs): ?> <?php if ($docs): ?>
<a href="<?= htmlspecialchars($docs[0] . $langSuffix) ?>" onclick="event.stopPropagation();" <a href="<?= htmlspecialchars($docs[0] . $langSuffix) ?>" onclick="event.stopPropagation();"
style="color:#00205B; font-size:.78rem; font-weight:600; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['about_link']) ?></a> style="color:#00205B; font-size:.88rem; font-weight:600; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['about_link']) ?></a>
<span style="color:#d1d5db;" aria-hidden="true">·</span> <span style="color:#d1d5db;" aria-hidden="true">·</span>
<a href="<?= htmlspecialchars($docs[1] . $langSuffix) ?>" onclick="event.stopPropagation();" <a href="<?= htmlspecialchars($docs[1] . $langSuffix) ?>" onclick="event.stopPropagation();"
style="color:#374151; font-size:.78rem; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['guide_link']) ?></a> style="color:#374151; font-size:.88rem; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['guide_link']) ?></a>
<span style="color:#d1d5db;" aria-hidden="true">·</span> <span style="color:#d1d5db;" aria-hidden="true">·</span>
<a href="<?= htmlspecialchars($docs[2] . $langSuffix) ?>" onclick="event.stopPropagation();" <a href="<?= htmlspecialchars($docs[2] . $langSuffix) ?>" onclick="event.stopPropagation();"
style="color:#374151; font-size:.78rem; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['tech_link']) ?></a> style="color:#374151; font-size:.88rem; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['tech_link']) ?></a>
<?php endif; ?> <?php endif; ?>
<a href="<?= $cardUrl ?>" onclick="event.stopPropagation();" <a href="<?= $cardUrl ?>" onclick="event.stopPropagation();"
style="margin-left:auto; color:#00205B; font-size:.82rem; font-weight:700; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['open_tool']) ?> →</a> style="margin-left:auto; color:#00205B; font-size:.94rem; font-weight:700; text-decoration:none; white-space:nowrap;"><?= htmlspecialchars($dl['open_tool']) ?> →</a>
</div> </div>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
+187 -59
View File
@@ -6,7 +6,6 @@ require_once __DIR__ . '/includes/FreeTier.php';
require_once __DIR__ . '/includes/PricingCatalog.php'; require_once __DIR__ . '/includes/PricingCatalog.php';
$uiLang = dbnToolsCurrentLanguage(); $uiLang = dbnToolsCurrentLanguage();
$isNorwegian = $uiLang === 'no';
$isAuthed = dbnToolsIsAuthenticated(); $isAuthed = dbnToolsIsAuthenticated();
$currentTier = $isAuthed ? dbnToolsCurrentTier() : 'free'; $currentTier = $isAuthed ? dbnToolsCurrentTier() : 'free';
$surveyDone = false; $surveyDone = false;
@@ -34,7 +33,8 @@ function credits(int $amount): string
return PricingCatalog::formatCredits($amount); return PricingCatalog::formatCredits($amount);
} }
$copy = $isNorwegian ? [ $copy = match ($uiLang) {
'no' => [
'title' => 'Priser - DBN Tools', 'title' => 'Priser - DBN Tools',
'description' => 'NOK-priser, kreditter og abonnement for tools.dobetternorge.no.', 'description' => 'NOK-priser, kreditter og abonnement for tools.dobetternorge.no.',
'eyebrow' => 'NOK-priser for DBN Tools', 'eyebrow' => 'NOK-priser for DBN Tools',
@@ -63,7 +63,110 @@ $copy = $isNorwegian ? [
'status_canceled' => 'Betalingen ble avbrutt. Ingen endringer er gjort.', 'status_canceled' => 'Betalingen ble avbrutt. Ingen endringer er gjort.',
'connecting' => 'Kobler til Stripe...', 'connecting' => 'Kobler til Stripe...',
'checkout_error' => 'Kunne ikke starte betaling. Prøv igjen.', 'checkout_error' => 'Kunne ikke starte betaling. Prøv igjen.',
] : [ 'per_month' => '/ mnd',
'starter_tier' => 'Startnivå',
'credit_unit' => 'kreditt',
'credits_unit' => 'kreditter',
'custom_terms' => 'Tilpasset avtale',
'more_users' => 'Flere brukere',
'custom_credits' => 'Tilpassede kreditter',
'onboarding_support' => 'Onboarding og støtte',
'agreed_directly' => 'Avtales direkte',
'paid_runs_per_hour' => 'betalte kjøringer per time',
'table_cost' => 'Kostnad',
'table_tools' => 'Verktøy',
'variable' => 'variabel',
'transcribe_cost' => 'transcribe: 1 kreditt per startet lydminutt, minst 5',
],
'uk' => [
'title' => 'Ціни - DBN Tools',
'description' => 'Ціни в NOK, кредити та підписки для tools.dobetternorge.no.',
'eyebrow' => 'Ціни в NOK для DBN Tools',
'headline' => 'Кредити, які мають сенс',
'subhead' => 'Щомісячні кредити витрачаються першими. Передоплачені кредити додаються зверху і не закінчуються.',
'trial' => 'Plus включає 14-денний пробний період. Картка обов\'язкова, скасування в будь-який час.',
'survey_title' => 'Отримайте 25 додаткових кредитів',
'survey_text' => 'Дайте відповідь на п\'ять коротких запитань про те, як ви використовуєте інструменти.',
'survey_cta' => 'Пройти опитування',
'current' => 'Ваш план',
'choose' => 'Вибрати',
'login' => 'Увійти для вибору',
'available' => 'Доступно',
'topups_title' => 'Додаткові кредити',
'topups_lead' => 'Поповнення — це одноразові покупки. Вони не закінчуються і витрачаються після щомісячних кредитів.',
'buy' => 'Купити',
'login_buy' => 'Увійти для покупки',
'tool_costs' => 'Вартість інструментів',
'tool_costs_lead' => 'Кредити знімаються лише тоді, коли інструмент завершує роботу з дійсним результатом.',
'organisation' => 'Організація',
'organisation_price' => 'Зв\'язатися',
'organisation_text' => 'Для консультантів, волонтерських організацій та великих сімейних команд, яким потрібно більше користувачів, особливі умови або підтримка.',
'contact' => 'Зв\'яжіться з нами',
'billing_note' => 'Stripe обробляє картки, підписки та квитанції. Місцеві кредити DBN залишаються авторитетними для доступу.',
'status_success' => 'Оплату підтверджено. Ваш обліковий запис оновиться після обробки вебхука Stripe.',
'status_canceled' => 'Оплату скасовано. Жодних змін не внесено.',
'connecting' => 'Підключення до Stripe...',
'checkout_error' => 'Не вдалося розпочати оплату. Спробуйте ще раз.',
'per_month' => '/ міс',
'starter_tier' => 'Стартовий рівень',
'credit_unit' => 'кредит',
'credits_unit' => 'кредитів',
'custom_terms' => 'Індивідуальні умови',
'more_users' => 'Більше користувачів',
'custom_credits' => 'Індивідуальні кредити',
'onboarding_support' => 'Підтримка та онбординг',
'agreed_directly' => 'Погоджується безпосередньо',
'paid_runs_per_hour' => 'платних запусків на годину',
'table_cost' => 'Вартість',
'table_tools' => 'Інструменти',
'variable' => 'змінна',
'transcribe_cost' => 'transcribe: 1 кредит за розпочату хвилину аудіо, мінімум 5',
],
'pl' => [
'title' => 'Cennik - DBN Tools',
'description' => 'Ceny w NOK, kredyty i subskrypcje dla tools.dobetternorge.no.',
'eyebrow' => 'Ceny w NOK dla DBN Tools',
'headline' => 'Kredyty, które mają sens',
'subhead' => 'Miesięczne kredyty są wydawane jako pierwsze. Opłacone z góry kredyty są dodawane na wierzchu i nigdy nie wygasają.',
'trial' => 'Plus zawiera 14-dniowy okres próbny. Wymagana karta, anulowanie w dowolnym momencie.',
'survey_title' => 'Zdobądź 25 dodatkowych kredytów',
'survey_text' => 'Odpowiedz na pięć krótkich pytań dotyczących korzystania z narzędzi.',
'survey_cta' => 'Wypełnij ankietę',
'current' => 'Twój plan',
'choose' => 'Wybierz',
'login' => 'Zaloguj się, aby wybrać',
'available' => 'Dostępny',
'topups_title' => 'Dodatkowe kredyty',
'topups_lead' => 'Doładowania to jednorazowe zakupy. Nigdy nie wygasają i są wydawane po miesięcznych kredytach.',
'buy' => 'Kup',
'login_buy' => 'Zaloguj się, aby kupić',
'tool_costs' => 'Koszty narzędzi',
'tool_costs_lead' => 'Kredyty są pobierane tylko wtedy, gdy narzędzie kończy pracę z prawidłowym wynikiem.',
'organisation' => 'Organizacja',
'organisation_price' => 'Kontakt',
'organisation_text' => 'Dla doradców, organizacji wolontariackich i większych zespołów rodzinnych potrzebujących więcej użytkowników, niestandardowych warunków lub wsparcia.',
'contact' => 'Skontaktuj się z nami',
'billing_note' => 'Stripe obsługuje karty, subskrypcje i paragony. Lokalne kredyty DBN pozostają miarodajne dla dostępu.',
'status_success' => 'Płatność potwierdzona. Twoje konto zostanie zaktualizowane po przetworzeniu webhooka Stripe.',
'status_canceled' => 'Płatność została anulowana. Nie wprowadzono żadnych zmian.',
'connecting' => 'Łączenie ze Stripe...',
'checkout_error' => 'Nie można rozpocząć płatności. Spróbuj ponownie.',
'per_month' => '/ mies',
'starter_tier' => 'Poziom startowy',
'credit_unit' => 'kredyt',
'credits_unit' => 'kredytów',
'custom_terms' => 'Warunki niestandardowe',
'more_users' => 'Więcej użytkowników',
'custom_credits' => 'Niestandardowe kredyty',
'onboarding_support' => 'Wsparcie i onboarding',
'agreed_directly' => 'Uzgadniane bezpośrednio',
'paid_runs_per_hour' => 'płatnych uruchomień na godzinę',
'table_cost' => 'Koszt',
'table_tools' => 'Narzędzia',
'variable' => 'zmienny',
'transcribe_cost' => 'transcribe: 1 kredyt za rozpoczętą minutę audio, minimum 5',
],
default => [
'title' => 'Pricing - DBN Tools', 'title' => 'Pricing - DBN Tools',
'description' => 'NOK pricing, credits, and subscriptions for tools.dobetternorge.no.', 'description' => 'NOK pricing, credits, and subscriptions for tools.dobetternorge.no.',
'eyebrow' => 'NOK pricing for DBN Tools', 'eyebrow' => 'NOK pricing for DBN Tools',
@@ -92,21 +195,49 @@ $copy = $isNorwegian ? [
'status_canceled' => 'Payment was canceled. No changes were made.', 'status_canceled' => 'Payment was canceled. No changes were made.',
'connecting' => 'Connecting to Stripe...', 'connecting' => 'Connecting to Stripe...',
'checkout_error' => 'Could not start checkout. Please try again.', 'checkout_error' => 'Could not start checkout. Please try again.',
]; 'per_month' => '/ mo',
'starter_tier' => 'Starter tier',
'credit_unit' => 'credit',
'credits_unit' => 'credits',
'custom_terms' => 'Custom terms',
'more_users' => 'More users',
'custom_credits' => 'Custom credits',
'onboarding_support' => 'Onboarding and support',
'agreed_directly' => 'Agreed directly',
'paid_runs_per_hour' => 'paid runs per hour',
'table_cost' => 'Cost',
'table_tools' => 'Tools',
'variable' => 'variable',
'transcribe_cost' => 'transcribe: 1 credit per started audio minute, minimum 5',
],
};
$plans = PricingCatalog::plans(); $plans = PricingCatalog::plans();
$topups = PricingCatalog::topups(); $topups = PricingCatalog::topups();
$planFeaturesNo = [
$planFeatures = [
'no' => [
'free' => ['30 kreditter per måned', 'Verktøy på innlimt tekst', 'Juridisk korpussøk', 'Ingen Min Sak-lagring'], 'free' => ['30 kreditter per måned', 'Verktøy på innlimt tekst', 'Juridisk korpussøk', 'Ingen Min Sak-lagring'],
'plus' => ['250 kreditter per måned', '500 MB Min Sak-lagring', '1 bruker', '14 dagers prøveperiode'], 'plus' => ['250 kreditter per måned', '500 MB Min Sak-lagring', '1 bruker', '14 dagers prøveperiode'],
'pro' => ['900 kreditter per måned', '5 GB Min Sak-lagring', '3 brukere', 'Full Azure-modellrute'], 'pro' => ['900 kreditter per måned', '5 GB Min Sak-lagring', '3 brukere', 'Full Azure-modellrute'],
]; ],
$planFeaturesEn = [ 'uk' => [
'free' => ['30 кредитів на місяць', 'Інструменти для вставленого тексту', 'Пошук юридичного корпусу', 'Без збереження «Моя справа»'],
'plus' => ['250 кредитів на місяць', '500 МБ сховища «Моя справа»', '1 користувач', '14-денний пробний період'],
'pro' => ['900 кредитів на місяць', '5 ГБ сховища «Моя справа»', '3 користувачі', 'Повний маршрут моделі Azure'],
],
'pl' => [
'free' => ['30 kredytów miesięcznie', 'Narzędzia na wklejonym tekście', 'Wyszukiwanie zasobów prawnych', 'Brak zapisu Mojej Sprawy'],
'plus' => ['250 kredytów miesięcznie', '500 MB pamięci Mojej Sprawy', '1 użytkownik', '14-dniowy okres próbny'],
'pro' => ['900 kredytów miesięcznie', '5 GB pamięci Mojej Sprawy', '3 użytkownicy', 'Pełna trasa modelu Azure'],
],
'en' => [
'free' => ['30 credits per month', 'Tools on pasted text', 'Legal corpus search', 'No My Case storage'], 'free' => ['30 credits per month', 'Tools on pasted text', 'Legal corpus search', 'No My Case storage'],
'plus' => ['250 credits per month', '500 MB My Case storage', '1 user', '14-day trial'], 'plus' => ['250 credits per month', '500 MB My Case storage', '1 user', '14-day trial'],
'pro' => ['900 credits per month', '5 GB My Case storage', '3 users', 'Full Azure model route'], 'pro' => ['900 credits per month', '5 GB My Case storage', '3 users', 'Full Azure model route'],
],
]; ];
$planFeatures = $isNorwegian ? $planFeaturesNo : $planFeaturesEn; $planFeatureSet = $planFeatures[$uiLang] ?? $planFeatures['en'];
$toolCostRows = [ $toolCostRows = [
['0', 'search, corpus-search, clarify-only gates'], ['0', 'search, corpus-search, clarify-only gates'],
@@ -115,7 +246,7 @@ $toolCostRows = [
['3', 'barnevernet, advocate, korrespond, legal-analysis'], ['3', 'barnevernet, advocate, korrespond, legal-analysis'],
['4', 'discrepancy'], ['4', 'discrepancy'],
['6', 'deep-research'], ['6', 'deep-research'],
[$isNorwegian ? 'variabel' : 'variable', $isNorwegian ? 'transcribe: 1 kreditt per startet lydminutt, minst 5' : 'transcribe: 1 credit per started audio minute, minimum 5'], [$copy['variable'], $copy['transcribe_cost']],
]; ];
?> ?>
<!doctype html> <!doctype html>
@@ -135,56 +266,57 @@ $toolCostRows = [
<style> <style>
:root { --dbn-navy:#00205B; --dbn-red:#BA0C2F; --dbn-ink:#111827; --dbn-muted:#5b6472; --dbn-line:#d9dee8; --dbn-soft:#f7f9fc; --dbn-green:#0f766e; } :root { --dbn-navy:#00205B; --dbn-red:#BA0C2F; --dbn-ink:#111827; --dbn-muted:#5b6472; --dbn-line:#d9dee8; --dbn-soft:#f7f9fc; --dbn-green:#0f766e; }
body { margin:0; background:#fbfcfe; color:var(--dbn-ink); font-family:'IBM Plex Sans', system-ui, sans-serif; } body { margin:0; background:#fbfcfe; color:var(--dbn-ink); font-family:'IBM Plex Sans', system-ui, sans-serif; }
.pricing-shell { max-width:1180px; margin:0 auto; padding:28px 20px 64px; } .pricing-shell { max-width:1240px; margin:0 auto; padding:36px 24px 80px; }
.lang-bar { display:flex; justify-content:flex-end; gap:8px; font-size:0.86rem; margin-bottom:18px; }
.lang-bar a { color:var(--dbn-muted); text-decoration:none; padding:4px 8px; border-radius:6px; }
.lang-bar a.is-active { background:var(--dbn-navy); color:#fff; }
.pricing-hero { display:grid; grid-template-columns:minmax(0, 1.2fr) minmax(280px, .8fr); gap:28px; align-items:end; padding:28px 0 34px; border-bottom:1px solid var(--dbn-line); } .pricing-hero { display:grid; grid-template-columns:minmax(0, 1.2fr) minmax(280px, .8fr); gap:28px; align-items:end; padding:28px 0 34px; border-bottom:1px solid var(--dbn-line); }
.eyebrow { margin:0 0 8px; color:var(--dbn-red); font-size:.82rem; font-weight:700; text-transform:uppercase; letter-spacing:.08em; } .eyebrow { margin:0 0 8px; color:var(--dbn-red); font-size:.94rem; font-weight:700; text-transform:uppercase; letter-spacing:.1em; }
h1 { margin:0; font-family:'Crimson Pro', serif; font-size:clamp(2.2rem, 4vw, 4rem); line-height:1; letter-spacing:0; } h1 { margin:0; font-family:'Crimson Pro', serif; font-size:clamp(2.2rem, 4vw, 4rem); line-height:1; letter-spacing:0; }
.hero-copy { margin:14px 0 0; color:var(--dbn-muted); font-size:1.05rem; max-width:680px; } .hero-copy { margin:14px 0 0; color:var(--dbn-muted); font-size:1.15rem; max-width:720px; }
.hero-note { background:#fff; border:1px solid var(--dbn-line); border-left:5px solid var(--dbn-red); border-radius:8px; padding:18px 18px; color:#263244; } .hero-note { background:#fff; border:1px solid var(--dbn-line); border-left:5px solid var(--dbn-red); border-radius:8px; padding:22px 22px; color:#263244; font-size:1.02rem; }
.status-pill { display:inline-flex; margin:22px 0 0; padding:9px 13px; border-radius:8px; font-size:.92rem; background:#fff7ed; color:#9a3412; border:1px solid #fed7aa; } .status-pill { display:inline-flex; margin:22px 0 0; padding:11px 16px; border-radius:8px; font-size:1rem; background:#fff7ed; color:#9a3412; border:1px solid #fed7aa; }
.status-pill.success { background:#ecfdf5; color:#065f46; border-color:#a7f3d0; } .status-pill.success { background:#ecfdf5; color:#065f46; border-color:#a7f3d0; }
.survey-banner { margin:26px 0 0; display:flex; align-items:center; justify-content:space-between; gap:16px; background:var(--dbn-navy); color:#fff; border-radius:8px; padding:18px 20px; } .survey-banner { margin:26px 0 0; display:flex; align-items:center; justify-content:space-between; gap:16px; background:var(--dbn-navy); color:#fff; border-radius:8px; padding:22px 24px; }
.survey-banner h2 { margin:0 0 4px; font-size:1.1rem; } .survey-banner h2 { margin:0 0 4px; font-size:1.25rem; }
.survey-banner p { margin:0; color:rgba(255,255,255,.82); } .survey-banner p { margin:0; color:rgba(255,255,255,.82); }
.btn, .pricing-cta { border:0; display:inline-flex; align-items:center; justify-content:center; min-height:42px; padding:0 15px; border-radius:8px; font-weight:700; text-decoration:none; cursor:pointer; line-height:1.1; } .btn, .pricing-cta { border:0; display:inline-flex; align-items:center; justify-content:center; min-height:48px; padding:0 18px; border-radius:8px; font-weight:700; font-size:.98rem; text-decoration:none; cursor:pointer; line-height:1.1; }
.btn-primary { background:var(--dbn-navy); color:#fff; } .btn-primary { background:var(--dbn-navy); color:#fff; }
.btn-primary:hover { background:#001740; } .btn-primary:hover { background:#001740; }
.btn-light { background:#fff; color:var(--dbn-navy); } .btn-light { background:#fff; color:var(--dbn-navy); }
.btn-muted { background:#edf1f7; color:#263244; } .btn-muted { background:#edf1f7; color:#263244; }
.btn-current { background:#dcfce7; color:#166534; cursor:default; } .btn-current { background:#dcfce7; color:#166534; cursor:default; }
.plans-grid { display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:16px; margin:28px 0; } .plans-grid { display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:16px; margin:28px 0; }
.plan-card, .topup-card, .cost-panel { background:#fff; border:1px solid var(--dbn-line); border-radius:8px; padding:20px; } .plan-card, .topup-card, .cost-panel { background:#fff; border:1px solid var(--dbn-line); border-radius:10px; padding:26px; }
.plan-card { display:flex; flex-direction:column; min-height:390px; position:relative; } .plan-card { display:flex; flex-direction:column; min-height:440px; position:relative; }
.plan-card.highlight { border-color:var(--dbn-navy); box-shadow:0 10px 30px rgba(0,32,91,.10); } .plan-card.highlight { border-color:var(--dbn-navy); box-shadow:0 10px 30px rgba(0,32,91,.10); }
.plan-badge { position:absolute; top:14px; right:14px; background:#e8eef8; color:var(--dbn-navy); border-radius:999px; padding:4px 9px; font-size:.74rem; font-weight:800; } .plan-badge { position:absolute; top:16px; right:16px; background:#e8eef8; color:var(--dbn-navy); border-radius:999px; padding:5px 11px; font-size:.85rem; font-weight:800; }
.plan-name { margin:0; font-size:1.35rem; font-family:'Crimson Pro', serif; } .plan-name { margin:0; font-size:1.55rem; font-family:'Crimson Pro', serif; }
.plan-price { margin:16px 0 4px; display:flex; align-items:baseline; gap:6px; } .plan-price { margin:18px 0 6px; display:flex; align-items:baseline; gap:6px; }
.plan-price strong { font-size:2rem; color:var(--dbn-navy); } .plan-price strong { font-size:2.3rem; color:var(--dbn-navy); }
.plan-meta { margin:0 0 16px; color:var(--dbn-muted); font-size:.92rem; } .plan-meta { margin:0 0 18px; color:var(--dbn-muted); font-size:1.04rem; }
.plan-list { margin:0 0 22px; padding:0; list-style:none; flex:1; } .plan-list { margin:0 0 22px; padding:0; list-style:none; flex:1; }
.plan-list li { padding:8px 0; border-bottom:1px dashed #eef1f6; font-size:.94rem; } .plan-list li { padding:10px 0; border-bottom:1px dashed #eef1f6; font-size:1.05rem; }
.fine-print { margin:10px 0 0; color:var(--dbn-muted); font-size:.84rem; } .fine-print { margin:10px 0 0; color:var(--dbn-muted); font-size:.94rem; }
.section-head { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; margin:34px 0 14px; } .section-head { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; margin:42px 0 18px; }
.section-head h2 { margin:0; font-family:'Crimson Pro', serif; font-size:1.9rem; } .section-head h2 { margin:0; font-family:'Crimson Pro', serif; font-size:2.2rem; }
.section-head p { margin:0; color:var(--dbn-muted); max-width:640px; } .section-head p { margin:0; color:var(--dbn-muted); max-width:640px; font-size:1.05rem; }
.topup-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:16px; } .topup-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:16px; }
.topup-card { display:grid; gap:10px; } .topup-card { display:grid; gap:12px; }
.topup-price { color:var(--dbn-navy); font-size:1.8rem; font-weight:800; } .topup-price { color:var(--dbn-navy); font-size:2.1rem; font-weight:800; }
.topup-credits { font-weight:700; } .topup-credits { font-weight:700; font-size:1.08rem; }
.topup-rate { color:var(--dbn-muted); font-size:.9rem; } .topup-rate { color:var(--dbn-muted); font-size:1rem; }
.cost-panel { margin-top:16px; overflow:auto; } .cost-panel { margin-top:16px; overflow:auto; }
.cost-table { width:100%; border-collapse:collapse; min-width:620px; } .cost-table { width:100%; border-collapse:collapse; min-width:620px; }
.cost-table th, .cost-table td { text-align:left; padding:11px 10px; border-bottom:1px solid #edf1f7; } .cost-table th, .cost-table td { text-align:left; padding:13px 12px; border-bottom:1px solid #edf1f7; }
.cost-table th { color:var(--dbn-muted); font-size:.84rem; text-transform:uppercase; letter-spacing:.06em; } .cost-table td { font-size:1rem; }
.billing-note { margin-top:18px; color:var(--dbn-muted); font-size:.92rem; } .cost-table th { color:var(--dbn-muted); font-size:.94rem; text-transform:uppercase; letter-spacing:.06em; }
.billing-note { margin-top:18px; color:var(--dbn-muted); font-size:1.02rem; }
@media (max-width:980px) { .pricing-hero { grid-template-columns:1fr; } .plans-grid { grid-template-columns:repeat(2, minmax(0,1fr)); } } @media (max-width:980px) { .pricing-hero { grid-template-columns:1fr; } .plans-grid { grid-template-columns:repeat(2, minmax(0,1fr)); } }
@media (max-width:680px) { .pricing-shell { padding-inline:14px; } .plans-grid, .topup-grid { grid-template-columns:1fr; } .survey-banner, .section-head { align-items:flex-start; flex-direction:column; } .plan-card { min-height:0; } } @media (max-width:680px) { .pricing-shell { padding-inline:14px; } .plans-grid, .topup-grid { grid-template-columns:1fr; } .survey-banner, .section-head { align-items:flex-start; flex-direction:column; } .plan-card { min-height:0; } }
</style> </style>
</head> </head>
<body> <body>
<?php require_once __DIR__ . '/includes/nav.php'; ?>
<main class="pricing-shell">
<div class="dbn-context-bar" role="note"> <div class="dbn-context-bar" role="note">
<span class="dbn-context-bar__tag"><?= htmlspecialchars(dbnToolsT('context_bar_tag', $uiLang)) ?></span> <span class="dbn-context-bar__tag"><?= htmlspecialchars(dbnToolsT('context_bar_tag', $uiLang)) ?></span>
<nav class="dbn-context-bar__links" aria-label="About"> <nav class="dbn-context-bar__links" aria-label="About">
@@ -193,12 +325,6 @@ $toolCostRows = [
<a href="/privacy.php<?= $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '' ?>"><?= htmlspecialchars(dbnToolsT('context_bar_privacy', $uiLang)) ?></a> <a href="/privacy.php<?= $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '' ?>"><?= htmlspecialchars(dbnToolsT('context_bar_privacy', $uiLang)) ?></a>
</nav> </nav>
</div> </div>
<main class="pricing-shell">
<nav class="lang-bar" aria-label="Language">
<?php foreach (['no', 'en'] as $lc): ?>
<a href="?lang=<?= h($lc) ?>" class="<?= $lc === $uiLang ? 'is-active' : '' ?>"><?= h(dbnToolsLanguageLabel($lc)) ?></a>
<?php endforeach; ?>
</nav>
<header class="pricing-hero"> <header class="pricing-hero">
<div> <div>
@@ -235,20 +361,20 @@ $toolCostRows = [
<h2 class="plan-name"><?= h($plan['name']) ?></h2> <h2 class="plan-name"><?= h($plan['name']) ?></h2>
<p class="plan-price"> <p class="plan-price">
<strong><?= h(nok((int)$plan['price_nok'])) ?></strong> <strong><?= h(nok((int)$plan['price_nok'])) ?></strong>
<span><?= $sku === 'free' ? '' : ($isNorwegian ? '/ mnd' : '/ mo') ?></span> <span><?= $sku === 'free' ? '' : h($copy['per_month']) ?></span>
</p> </p>
<p class="plan-meta"> <p class="plan-meta">
<?php if ($sku === 'free'): ?> <?php if ($sku === 'free'): ?>
<?= $isNorwegian ? 'Startnivå' : 'Starter tier' ?> <?= h($copy['starter_tier']) ?>
<?php else: ?> <?php else: ?>
<?= h(sprintf('%.2f', (float)$plan['effective_credit_cost'])) ?> kr / <?= $isNorwegian ? 'kreditt' : 'credit' ?> <?= h(sprintf('%.2f', (float)$plan['effective_credit_cost'])) ?> kr / <?= h($copy['credit_unit']) ?>
<?php endif; ?> <?php endif; ?>
</p> </p>
<ul class="plan-list"> <ul class="plan-list">
<?php foreach ($planFeatures[$sku] as $feature): ?> <?php foreach ($planFeatureSet[$sku] as $feature): ?>
<li><?= h($feature) ?></li> <li><?= h($feature) ?></li>
<?php endforeach; ?> <?php endforeach; ?>
<li><?= (int)$plan['hourly_cap'] ?> <?= $isNorwegian ? 'betalte kjøringer per time' : 'paid runs per hour' ?></li> <li><?= (int)$plan['hourly_cap'] ?> <?= h($copy['paid_runs_per_hour']) ?></li>
</ul> </ul>
<?php if ($sku === 'free'): ?> <?php if ($sku === 'free'): ?>
<?php if (!$isAuthed): ?> <?php if (!$isAuthed): ?>
@@ -274,12 +400,12 @@ $toolCostRows = [
<span class="plan-badge"><?= h($copy['organisation']) ?></span> <span class="plan-badge"><?= h($copy['organisation']) ?></span>
<h2 class="plan-name"><?= h($copy['organisation']) ?></h2> <h2 class="plan-name"><?= h($copy['organisation']) ?></h2>
<p class="plan-price"><strong><?= h($copy['organisation_price']) ?></strong></p> <p class="plan-price"><strong><?= h($copy['organisation_price']) ?></strong></p>
<p class="plan-meta"><?= $isNorwegian ? 'Tilpasset avtale' : 'Custom terms' ?></p> <p class="plan-meta"><?= h($copy['custom_terms']) ?></p>
<ul class="plan-list"> <ul class="plan-list">
<li><?= $isNorwegian ? 'Flere brukere' : 'More users' ?></li> <li><?= h($copy['more_users']) ?></li>
<li><?= $isNorwegian ? 'Tilpassede kreditter' : 'Custom credits' ?></li> <li><?= h($copy['custom_credits']) ?></li>
<li><?= $isNorwegian ? 'Onboarding og støtte' : 'Onboarding and support' ?></li> <li><?= h($copy['onboarding_support']) ?></li>
<li><?= $isNorwegian ? 'Avtales direkte' : 'Agreed directly' ?></li> <li><?= h($copy['agreed_directly']) ?></li>
</ul> </ul>
<a class="pricing-cta btn-muted" href="<?= h($orgUrl) ?>"><?= h($copy['contact']) ?></a> <a class="pricing-cta btn-muted" href="<?= h($orgUrl) ?>"><?= h($copy['contact']) ?></a>
<p class="fine-print"><?= h($copy['organisation_text']) ?></p> <p class="fine-print"><?= h($copy['organisation_text']) ?></p>
@@ -298,8 +424,8 @@ $toolCostRows = [
<article class="topup-card"> <article class="topup-card">
<h3 class="plan-name"><?= h($topup['name']) ?></h3> <h3 class="plan-name"><?= h($topup['name']) ?></h3>
<div class="topup-price"><?= h(nok((int)$topup['price_nok'])) ?></div> <div class="topup-price"><?= h(nok((int)$topup['price_nok'])) ?></div>
<div class="topup-credits"><?= h(credits((int)$topup['credits'])) ?> <?= $isNorwegian ? 'kreditter' : 'credits' ?></div> <div class="topup-credits"><?= h(credits((int)$topup['credits'])) ?> <?= h($copy['credits_unit']) ?></div>
<div class="topup-rate"><?= h(sprintf('%.2f', (float)$topup['cost_per_credit'])) ?> kr / <?= $isNorwegian ? 'kreditt' : 'credit' ?></div> <div class="topup-rate"><?= h(sprintf('%.2f', (float)$topup['cost_per_credit'])) ?> kr / <?= h($copy['credit_unit']) ?></div>
<?php if ($isAuthed): ?> <?php if ($isAuthed): ?>
<button type="button" class="pricing-cta btn-primary" data-sku="<?= h($topup['sku']) ?>"><?= h($copy['buy']) ?></button> <button type="button" class="pricing-cta btn-primary" data-sku="<?= h($topup['sku']) ?>"><?= h($copy['buy']) ?></button>
<?php else: ?> <?php else: ?>
@@ -321,8 +447,8 @@ $toolCostRows = [
<table class="cost-table"> <table class="cost-table">
<thead> <thead>
<tr> <tr>
<th><?= $isNorwegian ? 'Kostnad' : 'Cost' ?></th> <th><?= h($copy['table_cost']) ?></th>
<th><?= $isNorwegian ? 'Verktøy' : 'Tools' ?></th> <th><?= h($copy['table_tools']) ?></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -339,6 +465,8 @@ $toolCostRows = [
</section> </section>
</main> </main>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<script> <script>
(function() { (function() {
const connecting = <?= json_encode($copy['connecting'], JSON_UNESCAPED_UNICODE) ?>; const connecting = <?= json_encode($copy['connecting'], JSON_UNESCAPED_UNICODE) ?>;