Merge visual review into full shell and preserve bilingual layout

This commit is contained in:
Codex
2026-09-13 14:55:58 +02:00
69 changed files with 1419 additions and 346 deletions
@@ -369,20 +369,20 @@ export function CatalogSyncDrawer({
key={phase}
aria-current={current ? "step" : undefined}
aria-label={`${t(label)}, ${t(status)}`}
className={`flex items-center gap-2 text-xs ${failedOutcome ? "font-semibold text-destructive" : interruptedOutcome ? "font-semibold text-amber-600" : terminalOutcome || current ? "font-semibold text-foreground" : "text-muted-foreground"}`}
className={`flex items-center gap-2 text-xs ${failedOutcome ? "font-semibold text-destructive" : interruptedOutcome ? "font-semibold text-warning" : terminalOutcome || current ? "font-semibold text-foreground" : "text-muted-foreground"}`}
>
{confirmationSkipped ? (
<Minus aria-hidden="true" className="size-3.5 text-muted-foreground" />
) : failedOutcome ? (
<X aria-hidden="true" className="size-3.5 text-destructive" />
) : interruptedOutcome ? (
<AlertTriangle aria-hidden="true" className="size-3.5 text-amber-600" />
<AlertTriangle aria-hidden="true" className="size-3.5 text-warning" />
) : terminalOutcome ? (
<Minus aria-hidden="true" className="size-3.5 text-muted-foreground" />
) : complete ? (
<CheckCircle2 aria-hidden="true" className="size-3.5 text-[oklch(var(--success))]" />
) : current ? (
<LoaderCircle aria-hidden="true" className="size-3.5 animate-spin text-amber-600" />
<LoaderCircle aria-hidden="true" className="size-3.5 animate-spin text-warning" />
) : (
<Circle aria-hidden="true" className="size-3.5" />
)}
@@ -396,16 +396,16 @@ export function CatalogSyncDrawer({
<div className="mt-5 grid grid-cols-3 gap-2">
{(["tables", "columns", "relationships"] as const).map((name) => (
<div key={name} className="rounded-md border border-border bg-muted/25 px-3 py-2">
<p className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">{name}</p>
<p className="text-xs font-semibold text-muted-foreground">{name}</p>
<p className="mt-1 text-lg font-semibold tabular-nums">{run.counts[name] ?? ""}</p>
</div>
))}
</div>
{run.state === "awaiting_confirmation" && run.plannedDiff ? (
<section className="mt-5 rounded-md border border-amber-500/50 bg-amber-500/8 p-4" aria-label={t("Destructive changes")}>
<section className="mt-5 rounded-md border border-warning/50 bg-warning/8 p-4" aria-label={t("Destructive changes")}>
<div className="flex gap-3">
<AlertTriangle className="mt-0.5 size-5 shrink-0 text-amber-600" />
<AlertTriangle className="mt-0.5 size-5 shrink-0 text-warning" />
<div>
<h3 className="font-semibold">{t("Confirm")} {deletionCount} {t("removals")}</h3>
<p className="mt-1 text-sm text-muted-foreground">{t("The database will be scanned again before anything is removed.")}</p>
@@ -520,8 +520,8 @@ export function DatabaseColumns({
<h3 className="mt-1 font-heading text-2xl font-semibold">{active.name}</h3>
<p className="mt-1 text-sm text-muted-foreground">{t("Physical schema fields are read-only. Review fields can be edited.")}</p>
{stale ? (
staleBannerOpen ? <div className="mt-4 rounded-md border border-amber-500/50 bg-amber-500/8 p-4 text-sm"><p className="font-semibold">{t("Newer column metadata is available.")}</p><p className="mt-1 text-muted-foreground">{t("Your draft is preserved until you reload.")}</p><div className="mt-3 flex gap-2"><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button><Button type="button" variant="ghost" onClick={() => setStaleBannerOpen(false)}>{t("Keep editing")}</Button></div></div>
: <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-amber-500/40 bg-amber-500/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this metadata can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button></div>
staleBannerOpen ? <div className="mt-4 rounded-md border border-warning/50 bg-warning/8 p-4 text-sm"><p className="font-semibold">{t("Newer column metadata is available.")}</p><p className="mt-1 text-muted-foreground">{t("Your draft is preserved until you reload.")}</p><div className="mt-3 flex gap-2"><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button><Button type="button" variant="ghost" onClick={() => setStaleBannerOpen(false)}>{t("Keep editing")}</Button></div></div>
: <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-warning/40 bg-warning/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this metadata can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button></div>
) : null}
<ColumnMetadataFieldMatrix
column={active}
@@ -656,8 +656,8 @@ export function DatabaseColumns({
<p className="thot-label">{table.name} {t("· column")}</p>
<h3 className="mt-1 break-words font-heading text-xl font-semibold">{active.name}</h3>
{stale ? (
staleBannerOpen ? <div className="mt-4 rounded-md border border-amber-500/50 bg-amber-500/8 p-4 text-sm"><p className="font-semibold">{t("Newer column metadata is available.")}</p><p className="mt-1 text-muted-foreground">{t("Your draft is preserved until you reload.")}</p><div className="mt-3 flex gap-2"><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button><Button type="button" variant="ghost" onClick={() => setStaleBannerOpen(false)}>{t("Keep editing")}</Button></div></div>
: <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-amber-500/40 bg-amber-500/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this metadata can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button></div>
staleBannerOpen ? <div className="mt-4 rounded-md border border-warning/50 bg-warning/8 p-4 text-sm"><p className="font-semibold">{t("Newer column metadata is available.")}</p><p className="mt-1 text-muted-foreground">{t("Your draft is preserved until you reload.")}</p><div className="mt-3 flex gap-2"><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button><Button type="button" variant="ghost" onClick={() => setStaleBannerOpen(false)}>{t("Keep editing")}</Button></div></div>
: <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-warning/40 bg-warning/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this metadata can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadColumn()}>{t("Reload latest")}</Button></div>
) : null}
<ColumnMetadataFieldMatrix
column={active}
@@ -104,9 +104,9 @@ type StateTone = "neutral" | "info" | "success" | "warning" | "danger";
const stateToneClass: Record<StateTone, string> = {
neutral: "bg-muted-foreground/55",
info: "bg-sky-500",
success: "bg-emerald-500",
warning: "bg-amber-500",
info: "bg-muted-foreground/55",
success: "bg-success",
warning: "bg-warning",
danger: "bg-destructive",
};
@@ -128,7 +128,7 @@ function StateCell({
<span className={`size-1.5 shrink-0 rounded-full ${stateToneClass[tone]}`} aria-hidden="true" />
<span className="truncate">{t(label)}</span>
</span>
<span className={`truncate pl-3 text-[11px] ${detailClassName}`}>{t(detail)}</span>
<span className={`truncate pl-3 text-xs ${detailClassName}`}>{t(detail)}</span>
</div>
);
}
@@ -588,7 +588,7 @@ export function DatabaseRelationships({
<dt className="thot-label">{t("Column mapping")}</dt>
<dd className="mt-2 overflow-hidden rounded-md border border-border">
<table className="w-full text-left text-xs">
<thead className="bg-muted/60 font-mono uppercase tracking-wide text-muted-foreground"><tr><th className="px-3 py-2">{t("Source")}</th><th className="px-3 py-2">{t("Target")}</th></tr></thead>
<thead className="bg-muted/60 font-sans text-muted-foreground"><tr><th className="px-3 py-2">{t("Source")}</th><th className="px-3 py-2">{t("Target")}</th></tr></thead>
<tbody>{activeRelationship.columns.map((pair) => <tr key={`${pair.sourceColumnName}:${pair.targetColumnName}`} className="border-t border-border"><td className="px-3 py-2 font-mono">{pair.sourceColumnName}</td><td className="px-3 py-2 font-mono">{pair.targetColumnName}</td></tr>)}</tbody>
</table>
</dd>
@@ -603,12 +603,12 @@ export function DatabaseTables({
<h3 className="mt-1 break-words font-heading text-xl font-semibold">{activeTable.name}</h3>
{stale ? (
staleBannerOpen ? (
<div className="mt-4 rounded-md border border-amber-500/50 bg-amber-500/8 p-4 text-sm">
<div className="mt-4 rounded-md border border-warning/50 bg-warning/8 p-4 text-sm">
<p className="font-semibold">{t("A newer table description is available.")}</p>
<p className="mt-1 text-muted-foreground">{t("Your draft is preserved until you reload the current metadata.")}</p>
<div className="mt-3 flex gap-2"><Button type="button" variant="outline" onClick={() => void reloadTable()}>{t("Reload latest")}</Button><Button type="button" variant="ghost" onClick={() => setStaleBannerOpen(false)}>{t("Keep editing")}</Button></div>
</div>
) : <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-amber-500/40 bg-amber-500/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this description can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadTable()}>{t("Reload latest")}</Button></div>
) : <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-warning/40 bg-warning/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this description can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadTable()}>{t("Reload latest")}</Button></div>
) : null}
<div className="mt-5 grid gap-4">
<label className="grid gap-1.5 text-sm font-semibold">{t("Physical table name")}<input className="h-9 rounded-md border border-input bg-muted/35 px-3 font-mono text-xs" value={activeTable.name} readOnly /></label>
@@ -654,12 +654,12 @@ export function DatabaseTables({
<p className="text-sm text-muted-foreground">{t("Only review metadata can be changed.")}</p>
{stale ? (
staleBannerOpen ? (
<div className="mt-4 rounded-md border border-amber-500/50 bg-amber-500/8 p-4 text-sm">
<div className="mt-4 rounded-md border border-warning/50 bg-warning/8 p-4 text-sm">
<p className="font-semibold">{t("A newer table description is available.")}</p>
<p className="mt-1 text-muted-foreground">{t("Your draft is preserved until you reload the current metadata.")}</p>
<div className="mt-3 flex gap-2"><Button type="button" variant="outline" onClick={() => void reloadTable()}>{t("Reload latest")}</Button><Button type="button" variant="ghost" onClick={() => setStaleBannerOpen(false)}>{t("Keep editing")}</Button></div>
</div>
) : <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-amber-500/40 bg-amber-500/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this description can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadTable()}>{t("Reload latest")}</Button></div>
) : <div className="mt-4 flex items-center justify-between gap-3 rounded-md border border-warning/40 bg-warning/8 px-4 py-3 text-sm"><span>{t("Reload the latest value before this description can be saved.")}</span><Button type="button" variant="outline" onClick={() => void reloadTable()}>{t("Reload latest")}</Button></div>
) : null}
<div className="mt-5 grid gap-4">
<label className="grid gap-1.5 text-sm font-semibold">{t("Physical table name")}<input className="h-9 rounded-md border border-input bg-muted/35 px-3 font-mono text-xs" value={activeTable.name} readOnly /></label>
@@ -328,7 +328,7 @@ export function DescriptionGenerationDrawer({
{apparentlyStale ? (
<p
id="description-generation-unlock-help"
className="mb-3 rounded-md border border-amber-500/35 bg-amber-500/8 px-3 py-2 text-xs leading-5 text-muted-foreground"
className="mb-3 rounded-md border border-warning/35 bg-warning/8 px-3 py-2 text-xs leading-5 text-muted-foreground"
>
{t("Unlock interrupts apparently stale work and marks the run interrupted. A live worker will prevent it.")}</p>
) : null}
@@ -411,7 +411,7 @@ export function DescriptionGenerationDrawer({
<thead>
<tr className="border-b border-border bg-muted/45">
{progressCounters.map(([name, label]) => (
<th key={name} scope="col" className="border-l border-border px-2 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-muted-foreground first:border-l-0">
<th key={name} scope="col" className="border-l border-border px-2 py-1.5 text-left text-xs font-semibold text-muted-foreground first:border-l-0">
{t(label)}
</th>
))}
@@ -25,7 +25,7 @@ test("uses operational body text for every available-command list", () => {
}
expect(styles).toMatch(
/\.thot-fleet-action-selector__select\s*\{[^}]*font-size:\s*0\.9375rem;/s,
/\.thot-fleet-action-selector__select\s*\{[^}]*font-size:\s*var\(--text-control\);/s,
);
expect(styles).toMatch(
/\.thot-fleet-action-selector\s*\{[^}]*flex:\s*0 0 auto;/s,
@@ -14,6 +14,9 @@
}
.thot-fleet-ledger__workspace {
--fleet-block-gutter: var(--thoth-content-gutter, 1.25rem);
--fleet-block-padding: var(--thoth-content-padding, 1rem);
--fleet-summary-inset: calc(var(--fleet-block-gutter) + var(--fleet-block-padding));
display: flex;
flex: 1;
flex-direction: column;
@@ -54,12 +57,12 @@
.thot-fleet-ledger__kpi-label {
margin: 0;
color: oklch(var(--muted-foreground));
font-family: var(--font-mono);
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: 0.08em;
font-family: var(--font-sans);
font-size: var(--text-meta);
font-weight: 600;
letter-spacing: normal;
line-height: 1.35;
text-transform: uppercase;
text-transform: none;
}
.thot-fleet-ledger__title,
@@ -75,7 +78,7 @@
margin: 0;
font-family: var(--font-sans);
font-size: 1rem;
font-weight: 700;
font-weight: 600;
letter-spacing: -0.0125em;
line-height: 1.25;
white-space: nowrap;
@@ -85,7 +88,7 @@
max-width: 68ch;
margin: 0.45rem 0 0;
color: oklch(var(--muted-foreground));
font-size: 0.875rem;
font-size: var(--text-control);
line-height: 1.55;
}
@@ -101,6 +104,12 @@
.thot-fleet-ledger__header-status {
grid-area: status;
min-width: 0;
padding-inline: var(--fleet-summary-inset);
}
.thot-fleet-ledger .thot-administration-header,
.thot-fleet-ledger .thot-administration-toolbar {
padding-inline: var(--fleet-summary-inset);
}
.thot-fleet-ledger__header-status > .thot-fleet-ledger__status {
@@ -175,7 +184,6 @@
.thot-fleet-ledger__status-mark {
width: 0.45rem;
height: 0.45rem;
margin-top: 0.33rem;
flex: 0 0 auto;
border-radius: 999px;
background: var(--fleet-status-color);
@@ -191,15 +199,18 @@
.thot-fleet-ledger__status-label {
grid-column: 1 / -1;
font-size: 0.625rem;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: var(--text-meta);
}
.thot-fleet-ledger__status-value {
overflow: hidden;
color: oklch(var(--foreground));
font-family: var(--font-mono);
font-size: 0.75rem;
font-weight: 700;
font-family: var(--font-sans);
font-size: var(--text-meta);
font-weight: 600;
line-height: 1.45;
text-overflow: ellipsis;
white-space: nowrap;
@@ -208,7 +219,7 @@
.thot-fleet-ledger__status-detail {
overflow: hidden;
color: oklch(var(--muted-foreground));
font-size: 0.75rem;
font-size: var(--text-meta);
line-height: 1.45;
text-overflow: ellipsis;
white-space: nowrap;
@@ -217,25 +228,25 @@
.thot-fleet-ledger__kpi-section {
display: grid;
gap: 0.625rem;
padding: 0.75rem 1.25rem 0;
margin-inline: var(--fleet-block-gutter);
padding: 0.75rem var(--fleet-block-padding) 0;
border-bottom: 1px solid oklch(var(--border));
background: oklch(var(--card));
}
.thot-fleet-ledger__kpi-strip {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
grid-template-columns: repeat(auto-fit, minmax(min(100%, 6rem), 1fr));
margin: 0;
}
.thot-fleet-ledger__kpi {
min-width: 0;
padding: 0 1rem 0.75rem 0;
padding: 0 0.75rem 0.75rem 0;
}
.thot-fleet-ledger__kpi + .thot-fleet-ledger__kpi {
padding-left: 1rem;
border-left: 1px solid oklch(var(--border));
padding-left: 0;
}
.thot-fleet-ledger__kpi-value,
@@ -246,18 +257,18 @@
.thot-fleet-ledger__kpi-value {
margin-top: 0.2rem;
color: oklch(var(--foreground));
font-family: var(--font-mono);
font-size: clamp(1.25rem, 2vw, 1.625rem);
font-family: var(--font-sans);
font-size: var(--text-body);
font-variant-numeric: tabular-nums;
font-weight: 700;
letter-spacing: -0.035em;
font-weight: 600;
letter-spacing: normal;
line-height: 1.2;
}
.thot-fleet-ledger__kpi-detail {
margin-top: 0.15rem;
color: oklch(var(--muted-foreground));
font-size: 0.75rem;
font-size: var(--text-meta);
line-height: 1.4;
}
@@ -269,7 +280,8 @@
min-height: 3rem;
gap: 0.5rem;
align-items: center;
padding: 0.5rem 1rem;
margin-inline: var(--fleet-block-gutter);
padding: 0.5rem var(--fleet-block-padding);
border-bottom: 1px solid oklch(var(--border));
background: oklch(var(--card));
}
@@ -280,7 +292,7 @@
color: oklch(var(--primary));
background: oklch(var(--primary) / 0.08);
box-shadow: 0 1px 2px oklch(var(--primary) / 0.1);
font-weight: 700;
font-weight: 600;
}
.thot-fleet-back-action:not(:disabled):hover {
@@ -326,7 +338,7 @@
color: oklch(var(--muted-foreground));
background: transparent;
font: inherit;
font-size: 0.8125rem;
font-size: var(--text-control);
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
@@ -346,8 +358,8 @@
max-width: 18rem;
overflow: hidden;
color: oklch(var(--foreground));
font-size: 0.8125rem;
font-weight: 650;
font-size: var(--text-control);
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -392,14 +404,14 @@
display: grid;
min-width: 7.5rem;
color: oklch(var(--foreground));
font-family: var(--font-mono);
font-size: 0.6875rem;
font-family: var(--font-sans);
font-size: var(--text-meta);
line-height: 1.35;
}
.thot-fleet-action-selector__scope span {
color: oklch(var(--muted-foreground));
font-size: 0.625rem;
font-size: var(--text-meta);
}
.thot-fleet-action-selector__select {
@@ -413,7 +425,7 @@
border-radius: var(--radius);
color: oklch(var(--foreground));
background: oklch(var(--background));
font-size: 0.9375rem;
font-size: var(--text-control);
outline: none;
}
@@ -430,7 +442,7 @@
.thot-fleet-action-selector__reason {
flex-basis: 100%;
color: oklch(var(--muted-foreground));
font-size: 0.75rem;
font-size: var(--text-meta);
line-height: 1.4;
}
@@ -438,9 +450,9 @@
min-width: 0;
overflow: hidden;
color: oklch(var(--muted-foreground));
font-family: var(--font-mono);
font-size: 0.6875rem;
letter-spacing: 0.025em;
font-family: var(--font-sans);
font-size: var(--text-meta);
letter-spacing: normal;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -453,7 +465,7 @@
min-width: 0;
min-height: 18rem;
margin-block-end: 30px;
margin-inline-start: 20px;
margin-inline: var(--fleet-block-gutter);
overflow: hidden;
border-inline-end: 1px solid oklch(var(--border));
background: oklch(var(--card));
@@ -613,6 +625,10 @@
background: oklch(var(--muted) / 0.32);
}
.thot-fleet-ledger .thot-fleet-grid-toolbar {
padding-inline: var(--fleet-block-padding);
}
@media (min-width: 48rem) {
.thot-fleet-grid-toolbar > .thot-fleet-action-selector {
flex: 0 0 auto;
@@ -628,10 +644,10 @@
}
.thot-fleet-ledger-grid .ag-header-cell-text {
font-family: var(--font-mono);
font-size: 0.6875rem;
letter-spacing: 0.055em;
text-transform: uppercase;
font-family: var(--font-sans);
font-size: var(--text-meta);
letter-spacing: normal;
text-transform: none;
}
.thot-fleet-ledger-grid .ag-row {
@@ -647,7 +663,8 @@
.thot-fleet-ledger-grid .ag-row.ag-row-selected .ag-cell,
.thot-fleet-ledger-grid .ag-row.ag-row-selected .ag-grid-pinned-left-cells,
.thot-fleet-ledger-grid .ag-row.ag-row-selected .ag-grid-pinned-right-cells {
background-color: oklch(93% 0.002 17.2) !important;
background-color: oklch(var(--nav-active)) !important;
color: oklch(var(--nav-active-foreground));
}
.thot-fleet-icon-action {
@@ -672,7 +689,7 @@
background: oklch(24% 0.014 258);
box-shadow: 0 8px 20px -10px oklch(12% 0.02 258 / 0.72);
content: attr(data-tooltip);
font-size: 0.6875rem;
font-size: var(--text-meta);
font-weight: 600;
line-height: 1.25;
text-align: center;
@@ -701,8 +718,8 @@
background: oklch(var(--popover));
box-shadow: var(--shadow-sm);
content: attr(data-tooltip);
font-size: 0.6875rem;
font-weight: 650;
font-size: var(--text-meta);
font-weight: 600;
line-height: 1.2;
opacity: 0;
pointer-events: none;
@@ -765,7 +782,7 @@
}
.thot-fleet-ledger__drawer-title {
font-size: 1.35rem;
font-size: var(--text-section);
line-height: 1.18;
}
@@ -819,17 +836,24 @@
padding-inline: 1.5rem;
}
.thot-fleet-ledger__kpi-section {
padding-inline: 1.5rem;
}
.thot-fleet-ledger__navigation,
.thot-fleet-ledger__command-bar {
padding-inline: 1.25rem;
}
}
/* Share the KPI grid's first, middle and last column anchors on wide work areas. */
@container fleet-workspace (min-width: 48rem) {
.thot-fleet-ledger__header-status .thot-fleet-ledger__status-list {
grid-template-columns: repeat(5, minmax(0, 1fr));
column-gap: 0;
}
.thot-fleet-ledger__header-status .thot-fleet-ledger__status-item:nth-child(-n + 2) {
grid-column: span 2;
}
}
@container fleet-surface (max-width: 47.999rem) {
.thot-fleet-ledger__drawer,
.thot-fleet-ledger .thot-fleet-ledger__context-panel {
@@ -949,19 +973,6 @@
min-height: 28rem;
}
.thot-fleet-ledger__kpi-strip {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.thot-fleet-ledger__kpi:nth-child(odd) {
padding-left: 0;
border-left: 0;
}
.thot-fleet-ledger__kpi:nth-child(n + 3) {
padding-top: 0.75rem;
border-top: 1px solid oklch(var(--border));
}
.thot-fleet-ledger__navigation {
flex-wrap: wrap;
@@ -163,9 +163,11 @@ export function FleetLedgerRealStatus({
className="thot-fleet-ledger__status-item"
data-tone={item.tone ?? "neutral"}
>
<span className="thot-fleet-ledger__status-mark" aria-hidden="true" />
<span className="thot-fleet-ledger__status-copy">
<span className="thot-fleet-ledger__status-label">{item.label}</span>
<span className="thot-fleet-ledger__status-label">
<span>{item.label}</span>
<span className="thot-fleet-ledger__status-mark" aria-hidden="true" />
</span>
<strong className="thot-fleet-ledger__status-value">{item.value}</strong>
{item.detail ? (
<span className="thot-fleet-ledger__status-detail">{item.detail}</span>
@@ -197,7 +199,7 @@ export function FleetLedgerKpiStrip({
aria-labelledby={titleId}
>
<h2 id={titleId} className="thot-fleet-ledger__section-label">
{title ?? t("Fleet summary")}
{title ?? t("Catalog summary")}
</h2>
<dl className="thot-fleet-ledger__kpi-strip">
{items.map((item) => (
@@ -212,7 +212,7 @@ export function SensitiveDataReviewDrawer({
: t("no sensitive match")}{t(". Coverage:")} {t(suggestion.coverage.replace("_", " "))}{t(". Observed values:")} {suggestion.observedValues}.
</p>
</div>
<span className={`rounded px-2 py-0.5 text-[11px] font-semibold ${changedFromCurrent ? "bg-amber-500/12 text-amber-800 dark:text-amber-300" : "bg-muted text-muted-foreground"}`}>
<span className={`rounded px-2 py-0.5 text-xs font-semibold ${changedFromCurrent ? "bg-warning/12 text-amber-800 dark:text-amber-300" : "bg-muted text-muted-foreground"}`}>
{changedFromCurrent ? t("Change") : t("No change")}
</span>
</li>
@@ -268,7 +268,7 @@ export function SensitivityAnalysisHistoryDrawer({
<thead>
<tr className="border-b border-border bg-muted/45">
{progressCounters.map(([name, label]) => (
<th key={name} scope="col" className="border-l border-border px-2 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-muted-foreground first:border-l-0">
<th key={name} scope="col" className="border-l border-border px-2 py-1.5 text-left text-xs font-semibold text-muted-foreground first:border-l-0">
{t(label)}
</th>
))}