diff --git a/internal/cloudcp/portal/dist/build_manifest.json b/internal/cloudcp/portal/dist/build_manifest.json index 7b2181efb8..c6b39b5999 100644 --- a/internal/cloudcp/portal/dist/build_manifest.json +++ b/internal/cloudcp/portal/dist/build_manifest.json @@ -1,5 +1,5 @@ { - "source_hash": "640380cc9fd2cd2036a3ac2a9f949e018d3adfb7a6953cc3aa46c8d799980740", + "source_hash": "4b50b53b4369c0b8f2a17ddf8868a1eb8739772cb7a500616a2ba36f1449199d", "build_inputs": [ "package.json", "tsconfig.json", diff --git a/internal/cloudcp/portal/dist/portal_app.css b/internal/cloudcp/portal/dist/portal_app.css index b62540a6d0..a79298257c 100644 --- a/internal/cloudcp/portal/dist/portal_app.css +++ b/internal/cloudcp/portal/dist/portal_app.css @@ -161,18 +161,14 @@ header .logout-btn:hover, display: block; } .portal-shell-head { - padding: 20px 22px 18px; + padding: 16px 22px 14px; border-bottom: 1px solid var(--line); - background: - linear-gradient( - 180deg, - rgba(248, 250, 252, 0.98), - rgba(245, 248, 250, 0.96)); + background: rgba(246, 248, 251, 0.96); } .portal-shell-head-main { display: flex; flex-direction: column; - gap: 10px; + gap: 8px; } .portal-shell-head-kicker, .account-panel-kicker, @@ -195,24 +191,20 @@ header .logout-btn:hover, .intro-card p { margin: 0; font-size: 12px; - line-height: 1.45; + line-height: 1.5; color: var(--ink-soft); } -.portal-shell-head p strong { - color: var(--ink); - font-weight: 700; -} .portal-shell-head-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; - gap: 16px; - align-items: end; + gap: 14px; + align-items: center; } .portal-shell-head-copy { display: flex; flex-direction: column; - gap: 6px; - max-width: 860px; + gap: 4px; + max-width: 760px; } .portal-shell-head-brand-row { display: flex; @@ -222,8 +214,8 @@ header .logout-btn:hover, } .portal-shell-head-title { margin: 0; - font-size: 32px; - line-height: 0.98; + font-size: 28px; + line-height: 1; letter-spacing: -0.06em; font-weight: 800; color: var(--ink); @@ -241,35 +233,31 @@ header .logout-btn:hover, letter-spacing: 0.08em; text-transform: uppercase; } -.portal-shell-head-stats { +.portal-shell-head-summary { display: flex; - align-items: stretch; + align-items: center; gap: 8px; align-self: start; + flex-wrap: wrap; } -.portal-shell-head-stat { - display: flex; - flex-direction: column; - justify-content: center; - min-width: 132px; - min-height: 0; - padding: 10px 12px; +.portal-shell-summary-pill { + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 32px; + padding: 0 12px; border: 1px solid var(--line); - border-radius: 12px; + border-radius: 999px; background: rgba(255, 255, 255, 0.92); + font-size: 12px; + color: var(--ink-soft); } -.portal-shell-head-stat-label { +.portal-shell-summary-pill strong { + color: var(--ink); font-size: 11px; - font-weight: 700; - color: var(--ink-muted); - letter-spacing: 0.06em; - text-transform: uppercase; -} -.portal-shell-head-stat-value { - font-size: 14px; font-weight: 800; - line-height: 1.25; - letter-spacing: -0.04em; + letter-spacing: 0.08em; + text-transform: uppercase; } .portal-command-strip, .intro-card, @@ -1796,9 +1784,6 @@ header .logout-btn:hover, .portal-account-bar-row { align-items: start; } - .portal-shell-head-stats { - flex-wrap: wrap; - } .workspace-row { grid-template-columns: 1fr; } diff --git a/internal/cloudcp/portal/dist/portal_app.js b/internal/cloudcp/portal/dist/portal_app.js index d78f12e24f..a799d4fd01 100644 --- a/internal/cloudcp/portal/dist/portal_app.js +++ b/internal/cloudcp/portal/dist/portal_app.js @@ -1764,9 +1764,8 @@ function renderOverviewBand(accounts) { var hosted = hasHostedAccounts(accounts); var workspaceTotal = countWorkspaces(accounts); - var statusText = hosted ? "Hosted access is active on this account." : "No hosted workspace access is attached to this account yet."; var summary = hosted ? "Hosted operations, operator access, and commercial account services." : "Billing, license recovery, refunds, and privacy actions until hosted access is attached."; - return '
Pulse Account

' + (hosted ? "Operator console" : "Account console") + '

' + (hosted ? "Operator ready" : "Self-hosted only") + "

" + statusText + " " + summary + '

Hosted access' + (hosted ? "Active" : "Not attached") + '
Accounts' + (accounts.length === 1 ? "1 account" : String(accounts.length) + " accounts") + '
Workspace fleet' + (workspaceTotal ? workspaceCountLabel(workspaceTotal) : "0 workspaces") + "
"; + return '
Pulse Account

' + (hosted ? "Operator console" : "Account console") + '

' + (hosted ? "Operator ready" : "Self-hosted only") + "

" + summary + '

Hosted access' + (hosted ? "Active" : "Not attached") + 'Accounts' + (accounts.length === 1 ? "1 account" : String(accounts.length) + " accounts") + 'Workspace fleet' + (workspaceTotal ? workspaceCountLabel(workspaceTotal) : "0 workspaces") + "
"; } function renderPrimaryAccountBar(account) { var workspaceLabel = workspaceCountLabel((account.workspaces || []).length); diff --git a/internal/cloudcp/portal/frontend/src/shell_view.test.ts b/internal/cloudcp/portal/frontend/src/shell_view.test.ts index d64797a47f..57bffa9143 100644 --- a/internal/cloudcp/portal/frontend/src/shell_view.test.ts +++ b/internal/cloudcp/portal/frontend/src/shell_view.test.ts @@ -127,7 +127,7 @@ describe('shell view', function() { expect(html).toContain('portal-shell-head-kicker">Pulse Account'); expect(html).toContain('portal-shell-head-title">Operator console'); - expect(html).toContain('Hosted access is active on this account.'); + expect(html).toContain('Hosted operations, operator access, and commercial account services.'); expect(html).toContain('portal-shell-nav'); expect(html).toContain('Overview'); expect(html).toContain('Workspaces'); @@ -198,7 +198,7 @@ describe('shell view', function() { expect(html).toContain('Pulse Account'); expect(html).toContain('Self-hosted only'); expect(html).toContain('portal-shell-head-title">Account console'); - expect(html).toContain('No hosted workspace access is attached to this account yet.'); + expect(html).toContain('Billing, license recovery, refunds, and privacy actions until hosted access is attached.'); expect(html).toContain('Use these tools for self-hosted billing, license recovery, refunds, and privacy.'); expect(html).toContain('Account services'); expect(html).not.toContain('Self-hosted licenses and billing'); diff --git a/internal/cloudcp/portal/frontend/src/shell_view.ts b/internal/cloudcp/portal/frontend/src/shell_view.ts index abe5efb9f9..e5386fa1ec 100644 --- a/internal/cloudcp/portal/frontend/src/shell_view.ts +++ b/internal/cloudcp/portal/frontend/src/shell_view.ts @@ -196,7 +196,6 @@ function renderAttentionPanel(workspaces: PortalWorkspaceSummary[]): string { function renderOverviewBand(accounts: PortalAccountSummary[]): string { var hosted = hasHostedAccounts(accounts); var workspaceTotal = countWorkspaces(accounts); - var statusText = hosted ? 'Hosted access is active on this account.' : 'No hosted workspace access is attached to this account yet.'; var summary = hosted ? 'Hosted operations, operator access, and commercial account services.' : 'Billing, license recovery, refunds, and privacy actions until hosted access is attached.'; @@ -211,21 +210,12 @@ function renderOverviewBand(accounts: PortalAccountSummary[]): string { '

' + (hosted ? 'Operator console' : 'Account console') + '

' + '' + (hosted ? 'Operator ready' : 'Self-hosted only') + '' + '' + - '

' + statusText + ' ' + summary + '

' + + '

' + summary + '

' + '' + - '
' + - '
' + - 'Hosted access' + - '' + (hosted ? 'Active' : 'Not attached') + '' + - '
' + - '
' + - 'Accounts' + - '' + (accounts.length === 1 ? '1 account' : String(accounts.length) + ' accounts') + '' + - '
' + - '
' + - 'Workspace fleet' + - '' + (workspaceTotal ? workspaceCountLabel(workspaceTotal) : '0 workspaces') + '' + - '
' + + '
' + + 'Hosted access' + (hosted ? 'Active' : 'Not attached') + '' + + 'Accounts' + (accounts.length === 1 ? '1 account' : String(accounts.length) + ' accounts') + '' + + 'Workspace fleet' + (workspaceTotal ? workspaceCountLabel(workspaceTotal) : '0 workspaces') + '' + '
' + '
' + '' + diff --git a/internal/cloudcp/portal/frontend/src/styles.css b/internal/cloudcp/portal/frontend/src/styles.css index ae75a97d32..0fefffdcd3 100644 --- a/internal/cloudcp/portal/frontend/src/styles.css +++ b/internal/cloudcp/portal/frontend/src/styles.css @@ -159,16 +159,15 @@ header .logout-btn:hover, } .portal-shell-head { - padding: 20px 22px 18px; + padding: 16px 22px 14px; border-bottom: 1px solid var(--line); - background: - linear-gradient(180deg, rgba(248, 250, 252, 0.98), rgba(245, 248, 250, 0.96)); + background: rgba(246, 248, 251, 0.96); } .portal-shell-head-main { display: flex; flex-direction: column; - gap: 10px; + gap: 8px; } .portal-shell-head-kicker, @@ -195,27 +194,22 @@ header .logout-btn:hover, .intro-card p { margin: 0; font-size: 12px; - line-height: 1.45; + line-height: 1.5; color: var(--ink-soft); } -.portal-shell-head p strong { - color: var(--ink); - font-weight: 700; -} - .portal-shell-head-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; - gap: 16px; - align-items: end; + gap: 14px; + align-items: center; } .portal-shell-head-copy { display: flex; flex-direction: column; - gap: 6px; - max-width: 860px; + gap: 4px; + max-width: 760px; } .portal-shell-head-brand-row { @@ -227,8 +221,8 @@ header .logout-btn:hover, .portal-shell-head-title { margin: 0; - font-size: 32px; - line-height: 0.98; + font-size: 28px; + line-height: 1; letter-spacing: -0.06em; font-weight: 800; color: var(--ink); @@ -248,38 +242,33 @@ header .logout-btn:hover, text-transform: uppercase; } -.portal-shell-head-stats { +.portal-shell-head-summary { display: flex; - align-items: stretch; + align-items: center; gap: 8px; align-self: start; + flex-wrap: wrap; } -.portal-shell-head-stat { - display: flex; - flex-direction: column; - justify-content: center; - min-width: 132px; - min-height: 0; - padding: 10px 12px; +.portal-shell-summary-pill { + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 32px; + padding: 0 12px; border: 1px solid var(--line); - border-radius: 12px; + border-radius: 999px; background: rgba(255, 255, 255, 0.92); + font-size: 12px; + color: var(--ink-soft); } -.portal-shell-head-stat-label { +.portal-shell-summary-pill strong { + color: var(--ink); font-size: 11px; - font-weight: 700; - color: var(--ink-muted); - letter-spacing: 0.06em; - text-transform: uppercase; -} - -.portal-shell-head-stat-value { - font-size: 14px; font-weight: 800; - line-height: 1.25; - letter-spacing: -0.04em; + letter-spacing: 0.08em; + text-transform: uppercase; } .portal-command-strip, @@ -2037,10 +2026,6 @@ header .logout-btn:hover, align-items: start; } - .portal-shell-head-stats { - flex-wrap: wrap; - } - .workspace-row { grid-template-columns: 1fr; }