feat: polish review pack and homepage presentation
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 54s

This commit is contained in:
Ahmed Darrazi 2026-05-29 03:33:05 +02:00
parent e828e59c12
commit 46bd1f71ae
4 changed files with 176 additions and 54 deletions

View File

@ -133,7 +133,7 @@ const canonicalPath = localizedPath('/platform/review-packs', locale);
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
>
<div
class="rounded-[2rem] border border-neutral-300 bg-linear-to-br from-neutral-100 to-yellow-100/70 p-6 md:p-10 dark:border-neutral-700 dark:from-neutral-900 dark:to-neutral-800"
class="rounded-[2rem] border border-neutral-300 bg-linear-to-br from-neutral-100 to-neutral-200/70 p-6 md:p-10 dark:border-neutral-700 dark:from-neutral-900 dark:to-neutral-800"
>
<div class="max-w-(--breakpoint-md)">
<h2
@ -245,6 +245,49 @@ const canonicalPath = localizedPath('/platform/review-packs', locale);
</p>
</div>
<figure
class="mt-8 overflow-hidden rounded-3xl border border-neutral-300 bg-white shadow-sm dark:border-neutral-700 dark:bg-neutral-950/80"
>
<figcaption
class="flex flex-wrap items-center justify-between gap-3 border-b border-neutral-200 bg-neutral-50 px-6 py-4 dark:border-neutral-800 dark:bg-neutral-900/70"
>
<span
class="text-[11px] font-semibold tracking-[0.16em] text-neutral-500 uppercase dark:text-neutral-400"
>
{copy.decisionSampleBadge}
</span>
<span
class="inline-flex items-center gap-2 rounded-full border border-yellow-200/60 bg-yellow-100/70 px-3 py-1 text-xs font-semibold text-yellow-800 dark:border-yellow-900/40 dark:bg-yellow-500/10 dark:text-yellow-300"
>
<span
class="h-1.5 w-1.5 rounded-full bg-yellow-500 dark:bg-yellow-400"
></span>
{copy.decisionSampleStatusValue}
</span>
</figcaption>
<div class="px-6 py-5">
<h3
class="text-base font-semibold text-balance text-neutral-800 dark:text-neutral-200"
>
{copy.decisionSampleTitle}
</h3>
<dl class="mt-4 grid gap-3">
{
copy.decisionSampleRows.map((row: any) => (
<div class="grid gap-1 border-t border-neutral-100 pt-3 first:border-0 first:pt-0 sm:grid-cols-[7rem_1fr] sm:gap-4 dark:border-neutral-800">
<dt class="text-xs font-semibold tracking-[0.14em] text-neutral-500 uppercase dark:text-neutral-400">
{row.label}
</dt>
<dd class="text-sm leading-6 text-neutral-700 dark:text-neutral-300">
{row.value}
</dd>
</div>
))
}
</dl>
</div>
</figure>
<div class="mt-8 grid gap-4 sm:grid-cols-2">
{
copy.decisionCards.map((card: any) => (
@ -346,7 +389,7 @@ const canonicalPath = localizedPath('/platform/review-packs', locale);
<div class="mt-8 grid gap-6 lg:grid-cols-2">
{
copy.audienceCards.map((card: any) => (
<article class="rounded-[2rem] border border-neutral-300 bg-linear-to-br from-neutral-100 to-yellow-100/60 p-6 shadow-xs dark:border-neutral-700 dark:from-neutral-900 dark:to-neutral-800">
<article class="rounded-[2rem] border border-neutral-300 bg-linear-to-br from-neutral-100 to-neutral-200/70 p-6 shadow-xs dark:border-neutral-700 dark:from-neutral-900 dark:to-neutral-800">
<h3 class="text-2xl font-semibold text-balance text-neutral-800 dark:text-neutral-200">
{card.title}
</h3>
@ -362,47 +405,77 @@ const canonicalPath = localizedPath('/platform/review-packs', locale);
<section
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
>
<div class="max-w-(--breakpoint-md)">
<h2
class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200"
>
{copy.comparisonTitle}
</h2>
<p
class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400"
>
{copy.comparisonSubtitle}
</p>
</div>
<div
class="rounded-[2rem] bg-linear-to-br from-neutral-900 to-neutral-700 p-6 md:p-10 dark:from-neutral-950 dark:to-neutral-800"
>
<div class="max-w-(--breakpoint-md)">
<h2 class="text-2xl font-bold text-balance text-neutral-50 md:text-3xl">
{copy.comparisonTitle}
</h2>
<p class="mt-3 max-w-prose text-pretty text-neutral-300 md:text-lg">
{copy.comparisonSubtitle}
</p>
</div>
<div class="mt-8 grid gap-4">
{
copy.comparisonRows.map((row: any) => (
<article class="rounded-[2rem] border border-neutral-300 bg-neutral-100/70 p-6 dark:border-neutral-700 dark:bg-white/[0.04]">
<h3 class="text-lg font-semibold text-neutral-800 dark:text-neutral-200">
{row.title}
</h3>
<div class="mt-4 grid gap-4 md:grid-cols-2">
<div class="rounded-2xl bg-neutral-200/80 p-5 dark:bg-neutral-900/70">
<p class="text-xs font-semibold tracking-[0.18em] text-neutral-500 uppercase dark:text-neutral-400">
<div class="mt-8 hidden gap-4 px-2 md:grid md:grid-cols-[9rem_1fr_1fr]">
<span></span>
<span
class="text-xs font-semibold tracking-[0.18em] text-neutral-400 uppercase"
>
{copy.comparisonRawLabel}
</span>
<span
class="text-xs font-semibold tracking-[0.18em] text-yellow-300 uppercase"
>
{copy.comparisonStoryLabel}
</span>
</div>
<div class="mt-3 grid gap-3">
{
copy.comparisonRows.map((row: any) => (
<article class="grid gap-3 rounded-3xl border border-white/10 bg-white/[0.03] p-4 md:grid-cols-[9rem_1fr_1fr] md:items-stretch">
<h3 class="self-center text-sm font-semibold text-neutral-100">
{row.title}
</h3>
<div class="rounded-2xl bg-white/[0.04] p-4">
<p class="text-[11px] font-semibold tracking-[0.18em] text-neutral-400 uppercase md:hidden">
{copy.comparisonRawLabel}
</p>
<p class="mt-3 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
<p class="mt-2 text-sm leading-6 text-neutral-400 md:mt-0">
{row.rawExport}
</p>
</div>
<div class="rounded-2xl bg-yellow-100/80 p-5 dark:bg-yellow-500/10">
<p class="text-xs font-semibold tracking-[0.18em] text-neutral-700 uppercase dark:text-yellow-200">
<div class="rounded-2xl border border-yellow-400/30 bg-yellow-500/10 p-4">
<p class="text-[11px] font-semibold tracking-[0.18em] text-yellow-300 uppercase md:hidden">
{copy.comparisonStoryLabel}
</p>
<p class="mt-3 text-sm leading-6 text-neutral-700 dark:text-neutral-200">
{row.reviewStory}
</p>
<div class="flex gap-2.5 md:items-start">
<span class="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-yellow-400 text-neutral-900">
<svg
class="h-3 w-3"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="20 6 9 17 4 12" />
</svg>
</span>
<p class="text-sm leading-6 text-neutral-100">
{row.reviewStory}
</p>
</div>
</div>
</div>
</article>
))
}
</article>
))
}
</div>
</div>
</section>

View File

@ -23,7 +23,7 @@ type NavItem = {
>
{/* Navigation container */}
<nav
class="relative mx-2 w-full rounded-[36px] border border-yellow-100/40 bg-yellow-50/60 px-4 py-3 backdrop-blur-md md:flex md:items-center md:justify-between md:px-6 md:py-0 lg:px-8 xl:mx-auto dark:border-neutral-700/40 dark:bg-neutral-800/80 dark:backdrop-blur-md"
class="relative mx-2 w-full rounded-[36px] border border-yellow-100/60 bg-yellow-50/95 px-4 py-3 shadow-sm backdrop-blur-lg md:flex md:items-center md:justify-between md:px-6 md:py-0 lg:px-8 xl:mx-auto dark:border-neutral-700/60 dark:bg-neutral-900/95 dark:backdrop-blur-lg"
aria-label="Global"
>
<div class="flex items-center justify-between">

View File

@ -9,17 +9,17 @@ interface Props {
}
// Define CSS classes for the hyperlink button
const baseClasses =
'inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-center text-sm font-medium text-neutral-600 shadow-xs outline-hidden ring-zinc-500 focus-visible:ring-3 transition duration-300';
const borderClasses = 'border border-neutral-200';
const bgColorClasses = 'bg-neutral-300';
'inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-center text-sm font-medium text-neutral-800 shadow-xs outline-hidden ring-zinc-500 focus-visible:ring-3 transition duration-300';
const borderClasses = 'border border-neutral-300';
const bgColorClasses = 'bg-white';
const hoverClasses =
'hover:bg-neutral-400/50 hover:text-neutral-600 active:text-neutral-700';
'hover:border-neutral-400 hover:bg-neutral-100 hover:text-neutral-900 active:bg-neutral-200';
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50';
const fontSizeClasses = '2xl:text-base';
const ringClasses = 'ring-zinc-500';
const darkClasses =
'dark:border-neutral-700 dark:bg-zinc-700 dark:text-neutral-300 dark:ring-zinc-200 dark:hover:bg-zinc-600 dark:focus:outline-hidden';
'dark:border-neutral-600 dark:bg-transparent dark:text-neutral-200 dark:ring-zinc-200 dark:hover:border-neutral-500 dark:hover:bg-neutral-800 dark:hover:text-white dark:focus:outline-hidden';
---
{/* Styled hyperlink */}

View File

@ -130,7 +130,7 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'Für MSPs',
title: 'Wiederholbare Governance-Services pro Kunde aufbauen',
content:
'Zeige MSPs, wie Tenantial Reviews, Evidence, Drift und Accepted Risks pro Kundenumgebung in einen wiederholbaren Ablauf bringt.',
'Reviews, Evidence, Drift und Accepted Risks werden pro Kundenumgebung zu einem wiederholbaren Ablauf, den dein Team verlässlich liefern kann.',
cta: 'MSP-Use-Case ansehen',
href: '/use-cases/msp',
},
@ -138,15 +138,15 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'Für interne IT',
title: 'Kontrolle, Evidence und Audit-Kontext zusammenhalten',
content:
'Zeige internen Teams, wie Tenantial Policy-Drift, Change-Kontext, Backups und Review-Vorbereitung für Microsoft 365 lesbar macht.',
'Policy-Drift, Change-Kontext, Backups und Review-Vorbereitung bleiben für Microsoft 365 an einem Ort lesbar und nachvollziehbar.',
cta: 'Interne IT ansehen',
href: '/use-cases/mittelstand',
},
{
eyebrow: 'Für Reviews',
title: 'Review Packs als Gesprächsgrundlage zeigen',
title: 'Review Packs als Gesprächsgrundlage nutzen',
content:
'Zeige, wie Review Packs Evidence, Findings, Accepted Risks und nächste Schritte in eine customer-safe Review-Story übersetzen.',
'Review Packs übersetzen Evidence, Findings, Accepted Risks und nächste Schritte in eine customer-safe Review-Story für jeden Termin.',
cta: 'Review-Pack-Story ansehen',
href: '/platform/review-packs',
},
@ -812,6 +812,31 @@ export const siteCopy: Record<Locale, any> = {
'Sorgt dafür, dass Service-Review, Audit-Vorbereitung und Management-Gespräch auf dieselbe Story schauen.',
},
],
decisionSampleBadge: 'Beispiel-Decision-Summary',
decisionSampleTitle: 'Conditional Access: Legacy-Auth-Ausnahme',
decisionSampleStatusLabel: 'Status',
decisionSampleStatusValue: 'Akzeptiertes Risiko',
decisionSampleRows: [
{
label: 'Begründung',
value:
'Altsystem benötigt Basic Auth bis zur geplanten Migration in Q3.',
},
{
label: 'Auswirkung',
value:
'Erhöhte Angriffsfläche für ein klar abgegrenztes Servicekonto.',
},
{
label: 'Evidence',
value: 'Policy-Snapshot #142 · Change vom 12.05. · Finding F-07',
},
{
label: 'Nächster Schritt',
value:
'Security prüft die Kompensationsmaßnahme vor dem nächsten Review.',
},
],
boundaryTitle: 'Customer-safe Review statt interner Rohdetails',
boundarySubtitle:
'Die öffentliche Review-Pack-Story erklärt klar, was in eine kundenfähige Unterlage gehört und was standardmäßig intern bleibt.',
@ -1337,7 +1362,7 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'For MSPs',
title: 'Build repeatable governance services per customer',
content:
'Show MSP teams how Tenantial turns reviews, evidence, drift, and accepted risks into a repeatable customer workflow.',
'Turn reviews, evidence, drift, and accepted risks into a repeatable workflow your team can deliver across every customer environment.',
cta: 'Explore MSP use case',
href: '/use-cases/msp',
},
@ -1345,15 +1370,15 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'For internal IT',
title: 'Keep control, evidence, and audit context together',
content:
'Show internal teams how Tenantial makes policy drift, change context, backups, and review prep readable for Microsoft 365.',
'Policy drift, change context, backups, and review prep stay readable in one place across your Microsoft 365 estate.',
cta: 'Explore internal IT use case',
href: '/use-cases/mittelstand',
},
{
eyebrow: 'For reviews',
title: 'Show review packs as the conversation baseline',
title: 'Make review packs the conversation baseline',
content:
'Show how review packs translate evidence, findings, accepted risks, and next actions into a customer-safe review story.',
'Review packs translate evidence, findings, accepted risks, and next actions into a customer-safe review story for every meeting.',
cta: 'Explore review-pack story',
href: '/platform/review-packs',
},
@ -2012,6 +2037,30 @@ export const siteCopy: Record<Locale, any> = {
'Keeps service reviews, audit preparation, and management conversations anchored in the same story.',
},
],
decisionSampleBadge: 'Example decision summary',
decisionSampleTitle: 'Conditional Access: legacy auth exception',
decisionSampleStatusLabel: 'Status',
decisionSampleStatusValue: 'Accepted risk',
decisionSampleRows: [
{
label: 'Reasoning',
value:
'Legacy system needs basic auth until the planned Q3 migration.',
},
{
label: 'Impact',
value: 'Elevated exposure for a clearly scoped service account.',
},
{
label: 'Evidence',
value: 'Policy snapshot #142 · change from May 12 · finding F-07',
},
{
label: 'Next action',
value:
'Security reviews the compensating control before the next review.',
},
],
boundaryTitle: 'Customer-safe review instead of internal raw detail',
boundarySubtitle:
'The public review-pack story should make it obvious what belongs in a customer-facing review and what stays internal by default.',
@ -2451,7 +2500,7 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Backups & Versionen behalten',
content:
'Policy-Zustände nachvollziehbar sichern und Änderungen historisch vergleichbar machen.',
svg: 'dashboard',
svg: 'books',
},
{
heading: 'Auditfähige Reviews vorbereiten',
@ -2469,13 +2518,13 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Provider-Berechtigungen transparent machen',
content:
'Microsoft Graph und Provider-Zugriffe verständlich erklären, prüfen und capability-orientiert einordnen.',
svg: 'frame',
svg: 'groups',
},
{
heading: 'Entscheidungen nachvollziehbar machen',
content:
'Findings, Exceptions, Accepted Risks und Follow-ups in einen prüfbaren Governance-Kontext bringen.',
svg: 'dashboard',
svg: 'checkCircle',
},
],
en: [
@ -2489,7 +2538,7 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Keep backups & versions',
content:
'Preserve policy states in a traceable way and compare changes historically.',
svg: 'dashboard',
svg: 'books',
},
{
heading: 'Prepare audit-ready reviews',
@ -2507,13 +2556,13 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Make provider permissions transparent',
content:
'Explain, verify, and classify Microsoft Graph and provider access by capability.',
svg: 'frame',
svg: 'groups',
},
{
heading: 'Make decisions traceable',
content:
'Put findings, exceptions, accepted risks, and follow-ups into a reviewable governance context.',
svg: 'dashboard',
svg: 'checkCircle',
},
],
};