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" class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
> >
<div <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)"> <div class="max-w-(--breakpoint-md)">
<h2 <h2
@ -245,6 +245,49 @@ const canonicalPath = localizedPath('/platform/review-packs', locale);
</p> </p>
</div> </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"> <div class="mt-8 grid gap-4 sm:grid-cols-2">
{ {
copy.decisionCards.map((card: any) => ( 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"> <div class="mt-8 grid gap-6 lg:grid-cols-2">
{ {
copy.audienceCards.map((card: any) => ( 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"> <h3 class="text-2xl font-semibold text-balance text-neutral-800 dark:text-neutral-200">
{card.title} {card.title}
</h3> </h3>
@ -362,47 +405,77 @@ const canonicalPath = localizedPath('/platform/review-packs', locale);
<section <section
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full" 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)"> <div
<h2 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"
class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200" >
> <div class="max-w-(--breakpoint-md)">
{copy.comparisonTitle} <h2 class="text-2xl font-bold text-balance text-neutral-50 md:text-3xl">
</h2> {copy.comparisonTitle}
<p </h2>
class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400" <p class="mt-3 max-w-prose text-pretty text-neutral-300 md:text-lg">
> {copy.comparisonSubtitle}
{copy.comparisonSubtitle} </p>
</p> </div>
</div>
<div class="mt-8 grid gap-4"> <div class="mt-8 hidden gap-4 px-2 md:grid md:grid-cols-[9rem_1fr_1fr]">
{ <span></span>
copy.comparisonRows.map((row: any) => ( <span
<article class="rounded-[2rem] border border-neutral-300 bg-neutral-100/70 p-6 dark:border-neutral-700 dark:bg-white/[0.04]"> class="text-xs font-semibold tracking-[0.18em] text-neutral-400 uppercase"
<h3 class="text-lg font-semibold text-neutral-800 dark:text-neutral-200"> >
{row.title} {copy.comparisonRawLabel}
</h3> </span>
<div class="mt-4 grid gap-4 md:grid-cols-2"> <span
<div class="rounded-2xl bg-neutral-200/80 p-5 dark:bg-neutral-900/70"> class="text-xs font-semibold tracking-[0.18em] text-yellow-300 uppercase"
<p class="text-xs font-semibold tracking-[0.18em] text-neutral-500 uppercase dark:text-neutral-400"> >
{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} {copy.comparisonRawLabel}
</p> </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} {row.rawExport}
</p> </p>
</div> </div>
<div class="rounded-2xl bg-yellow-100/80 p-5 dark:bg-yellow-500/10"> <div class="rounded-2xl border border-yellow-400/30 bg-yellow-500/10 p-4">
<p class="text-xs font-semibold tracking-[0.18em] text-neutral-700 uppercase dark:text-yellow-200"> <p class="text-[11px] font-semibold tracking-[0.18em] text-yellow-300 uppercase md:hidden">
{copy.comparisonStoryLabel} {copy.comparisonStoryLabel}
</p> </p>
<p class="mt-3 text-sm leading-6 text-neutral-700 dark:text-neutral-200"> <div class="flex gap-2.5 md:items-start">
{row.reviewStory} <span class="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-yellow-400 text-neutral-900">
</p> <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>
</div> </article>
</article> ))
)) }
} </div>
</div> </div>
</section> </section>

View File

@ -23,7 +23,7 @@ type NavItem = {
> >
{/* Navigation container */} {/* Navigation container */}
<nav <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" aria-label="Global"
> >
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">

View File

@ -9,17 +9,17 @@ interface Props {
} }
// Define CSS classes for the hyperlink button // Define CSS classes for the hyperlink button
const baseClasses = 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'; '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-200'; const borderClasses = 'border border-neutral-300';
const bgColorClasses = 'bg-neutral-300'; const bgColorClasses = 'bg-white';
const hoverClasses = 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 disableClasses = 'disabled:pointer-events-none disabled:opacity-50';
const fontSizeClasses = '2xl:text-base'; const fontSizeClasses = '2xl:text-base';
const ringClasses = 'ring-zinc-500'; const ringClasses = 'ring-zinc-500';
const darkClasses = 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 */} {/* Styled hyperlink */}

View File

@ -130,7 +130,7 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'Für MSPs', eyebrow: 'Für MSPs',
title: 'Wiederholbare Governance-Services pro Kunde aufbauen', title: 'Wiederholbare Governance-Services pro Kunde aufbauen',
content: 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', cta: 'MSP-Use-Case ansehen',
href: '/use-cases/msp', href: '/use-cases/msp',
}, },
@ -138,15 +138,15 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'Für interne IT', eyebrow: 'Für interne IT',
title: 'Kontrolle, Evidence und Audit-Kontext zusammenhalten', title: 'Kontrolle, Evidence und Audit-Kontext zusammenhalten',
content: 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', cta: 'Interne IT ansehen',
href: '/use-cases/mittelstand', href: '/use-cases/mittelstand',
}, },
{ {
eyebrow: 'Für Reviews', eyebrow: 'Für Reviews',
title: 'Review Packs als Gesprächsgrundlage zeigen', title: 'Review Packs als Gesprächsgrundlage nutzen',
content: 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', cta: 'Review-Pack-Story ansehen',
href: '/platform/review-packs', 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.', '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', boundaryTitle: 'Customer-safe Review statt interner Rohdetails',
boundarySubtitle: boundarySubtitle:
'Die öffentliche Review-Pack-Story erklärt klar, was in eine kundenfähige Unterlage gehört und was standardmäßig intern bleibt.', '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', eyebrow: 'For MSPs',
title: 'Build repeatable governance services per customer', title: 'Build repeatable governance services per customer',
content: 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', cta: 'Explore MSP use case',
href: '/use-cases/msp', href: '/use-cases/msp',
}, },
@ -1345,15 +1370,15 @@ export const siteCopy: Record<Locale, any> = {
eyebrow: 'For internal IT', eyebrow: 'For internal IT',
title: 'Keep control, evidence, and audit context together', title: 'Keep control, evidence, and audit context together',
content: 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', cta: 'Explore internal IT use case',
href: '/use-cases/mittelstand', href: '/use-cases/mittelstand',
}, },
{ {
eyebrow: 'For reviews', eyebrow: 'For reviews',
title: 'Show review packs as the conversation baseline', title: 'Make review packs the conversation baseline',
content: 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', cta: 'Explore review-pack story',
href: '/platform/review-packs', 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.', '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', boundaryTitle: 'Customer-safe review instead of internal raw detail',
boundarySubtitle: boundarySubtitle:
'The public review-pack story should make it obvious what belongs in a customer-facing review and what stays internal by default.', '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', heading: 'Backups & Versionen behalten',
content: content:
'Policy-Zustände nachvollziehbar sichern und Änderungen historisch vergleichbar machen.', 'Policy-Zustände nachvollziehbar sichern und Änderungen historisch vergleichbar machen.',
svg: 'dashboard', svg: 'books',
}, },
{ {
heading: 'Auditfähige Reviews vorbereiten', heading: 'Auditfähige Reviews vorbereiten',
@ -2469,13 +2518,13 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Provider-Berechtigungen transparent machen', heading: 'Provider-Berechtigungen transparent machen',
content: content:
'Microsoft Graph und Provider-Zugriffe verständlich erklären, prüfen und capability-orientiert einordnen.', 'Microsoft Graph und Provider-Zugriffe verständlich erklären, prüfen und capability-orientiert einordnen.',
svg: 'frame', svg: 'groups',
}, },
{ {
heading: 'Entscheidungen nachvollziehbar machen', heading: 'Entscheidungen nachvollziehbar machen',
content: content:
'Findings, Exceptions, Accepted Risks und Follow-ups in einen prüfbaren Governance-Kontext bringen.', 'Findings, Exceptions, Accepted Risks und Follow-ups in einen prüfbaren Governance-Kontext bringen.',
svg: 'dashboard', svg: 'checkCircle',
}, },
], ],
en: [ en: [
@ -2489,7 +2538,7 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Keep backups & versions', heading: 'Keep backups & versions',
content: content:
'Preserve policy states in a traceable way and compare changes historically.', 'Preserve policy states in a traceable way and compare changes historically.',
svg: 'dashboard', svg: 'books',
}, },
{ {
heading: 'Prepare audit-ready reviews', heading: 'Prepare audit-ready reviews',
@ -2507,13 +2556,13 @@ export const featuresByLocale: Record<Locale, Feature[]> = {
heading: 'Make provider permissions transparent', heading: 'Make provider permissions transparent',
content: content:
'Explain, verify, and classify Microsoft Graph and provider access by capability.', 'Explain, verify, and classify Microsoft Graph and provider access by capability.',
svg: 'frame', svg: 'groups',
}, },
{ {
heading: 'Make decisions traceable', heading: 'Make decisions traceable',
content: content:
'Put findings, exceptions, accepted risks, and follow-ups into a reviewable governance context.', 'Put findings, exceptions, accepted risks, and follow-ups into a reviewable governance context.',
svg: 'dashboard', svg: 'checkCircle',
}, },
], ],
}; };