agnes-the-ai-analyst/app/web/static/style-custom.css
Monika Feigler caae12d02f
fix(web): UI consistency — code tokens, label-qualifier, radio cards, Keboola edit-modal JS (#347)
* fix(web): UI consistency — code tokens, label-qualifier, radio card selected state

I-UI-01: Add .sync-option-card:has(input:checked) rule — border + background
feedback when a radio option card is selected. Add class sync-option-card to
all 14 radio label cards in admin_tables.html.

I-UI-02: Add .label-qualifier / .optional to style-custom.css. Remove the
duplicate local definition from admin_tables.html <style> block.

I-UI-03: Migrate inline code rule to design tokens (--font-mono, --text-sm,
--border-light, --border, --radius-sm). Add background + border so inline
code is visually distinct across all pages.

I-UI-05 (partial): Replace hardcoded #c4c4c4 / #fafafa in .btn-google:hover
with var(--border) / var(--background) so theme overrides apply.

* fix(web): expose entire Keboola edit-modal JS to all instance types

openEditKeboolaModal, closeEditKeboolaModal, saveKeboolaTabEdit,
onEditKbStrategyChange and helpers were still inside {% if keboola %}
but called from always-rendered HTML (openEditModal dispatcher,
Escape key handler, modal overlay click, Cancel/Save buttons).

Removed the Phase F2 if-guard entirely — only prefillFromKeboolaTable
stays conditional (its callers are inside {% if keboola %} HTML blocks).

* fix(ui): promote .form-textarea to global CSS with design tokens

Removes the local hardcoded .form-textarea definition from admin_tables.html
and adds it globally to style-custom.css using design tokens, making
description textareas visually consistent with other form fields.

* fix(ui): restore .form-textarea to local style block for visual consistency

Tokens --text-sm (12px) and --radius-md (6px) differ from the local override
values (13px, 8px) used by .form-input on this page, causing a visible mismatch.
.form-textarea rejoins the shared local selector so all three classes render
identically; global .form-textarea in style-custom.css remains as a baseline
for other pages.

* fix(ui): use textarea.form-textarea in global CSS to override .form-group textarea

.form-group textarea (specificity 0,1,1) was overriding .form-textarea (0,1,0)
with a legacy monospace font and different padding. Raising the selector to
textarea.form-textarea matches specificity and wins via source order, making
description textareas consistent with other form inputs. Local admin_tables.html
overrides for .form-textarea removed — styling now comes entirely from global CSS.

* fix(ui): add border:none to .code-block code + add CHANGELOG entries

Fixes light-gray border leaking into dark .code-block backgrounds.
Adds required CHANGELOG.md entries for all user-visible changes in this PR.

* fix(ui): add --border-dark token + reset border-radius in .code-block code

- Adds --border-dark: #C4C4C4 design token for hover border states
- Uses var(--border-dark) in both .btn-google:hover rules so hover border
  is visually distinct from the base border (was a no-op with var(--border))
- Adds border-radius: 0 to .code-block code override to fully reset the
  new global code border-radius on dark code-block backgrounds

* fix(ui): reset code border/bg inside .use-case-prompt dark container

Adds .plugin-detail .use-case-prompt code override to prevent the new
global code border and background from leaking into the dark #1e1e2e
pre block in marketplace_plugin_detail.html.

* fix(ui): reset code border in all dark-background containers

Global code { border } leaks into dark-themed containers across templates.
Adds border: none (+ border-radius: 0 where needed) to:
- marketplace_plugin_detail.html: lead-rendered pre code, sample-assistant-body code/pre code
- marketplace_item_detail.html: same three selectors
- home_onboarded.html, home_not_onboarded.html, admin_welcome.html: inline code on hero dark backgrounds

* fix(ui): uniform form typography — chip-input font, data-package desc textarea, orphan endif

- .chip-input container gets font-family/size tokens so inner input
  inherits correctly (inline `font: inherit` was pulling browser default)
- cdp-desc / edp-desc switched from form-input to form-textarea so
  description fields render Inter, not monospace
- Removed orphan {% endif %} left in admin_tables.html after rebase
  (caused TemplateSyntaxError breaking all admin-tables tests in CI)
- .item-detail .use-case-prompt code: border/bg reset for dark container

* fix: relax test_keboola_discover_buttons assertion + CHANGELOG bullet for #347

The test_keboola_discover_buttons_hidden_on_bigquery_instance test
asserted bare-string `prefillFromKeboolaTable` not in the rendered
HTML on a non-Keboola instance. That made sense when the function
DEFINITION lived behind the keboola Jinja guard. #347 moves
several Keboola edit-modal helpers out from under the guard so
they're now defined as dead code on every instance, but the actual
call sites (`onclick="prefillFromKeboolaTable(...)"` + the
Discover buttons themselves) still respect the guard — which is
what actually matters for runtime behavior.

Updated the assertions to match `onclick="<fn>(` so they pin the
call-site contract, not the function-definition substring.

---------

Co-authored-by: ZdenekSrotyr <zdenek.srotyr@keboola.com>
2026-05-19 16:30:19 +02:00

4453 lines
100 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Design System - Data Analyst Portal v2 */
:root {
/* Colors */
--primary: #0073D1;
--primary-light: rgba(0, 115, 209, 0.1);
--primary-dark: #005BA3;
--text-primary: #1A253C;
--text-secondary: #6B7280;
--background: #F5F7FA;
--surface: #FFFFFF;
--border: #E5E7EB;
--border-light: #F3F4F6;
--border-dark: #C4C4C4;
--success: #10B77F;
--warning: #F59F0A;
--error: #EA580C;
/* Typography */
--font-primary: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
/* Font sizes */
--text-xs: 10px;
--text-sm: 12px;
--text-base: 14px;
--text-md: 16px;
--text-lg: 18px;
--text-xl: 24px;
--text-2xl: 30px;
/* Font weights */
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
--font-extrabold: 800;
/* Extra text colors — referenced by inline page styles that should
eventually migrate (home hero used --hp-text-muted, etc.) */
--text-muted: #9CA3AF;
--text-disabled: #D1D5DB;
/* Spacing — 4-multiple scale. Gaps filled to cover dashboard /home
inline-style values (28px / 40px / 48px / 64px). */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-7: 28px;
--space-8: 32px;
--space-9: 40px;
--space-10: 48px;
--space-12: 64px;
/* Border radius */
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-xl: 12px;
--radius-2xl: 16px;
--radius-full: 9999px;
/* Shadows. --shadow-card is the canonical card elevation;
--shadow-elevated lifts hero blocks (home install-hero used a
custom blue-tinted shadow before this token landed). */
--shadow-sm: rgba(0, 0, 0, 0.05) 0px 1px 2px 0px;
--shadow-md: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.1) 0px 1px 2px -1px;
--shadow-card: 0 1px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 2px -1px rgba(0, 0, 0, 0.05);
--shadow-elevated: 0 8px 24px rgba(0, 86, 163, 0.18);
/* Focus + interaction */
--focus-ring: 0 0 0 3px rgba(0, 115, 209, 0.25);
--transition-fast: 120ms ease;
--transition-base: 200ms ease;
--transition-slow: 320ms ease;
/* Layout — outer chrome widths. Pages should reference these instead
of hardcoding 1280 / 1200 / 800 to keep /home and /dashboard in
step. */
--width-narrow: 800px;
--width-app: 1280px;
--width-wide: 1400px;
/* Legacy token aliases — keep absorbed style.css rules rendering
until Task 16 cleanup. Each legacy name maps onto its modern
equivalent so every absorbed selector resolves to a real value
instead of falling back to `unset`. */
--bg: var(--background);
--card-bg: var(--surface);
--text: var(--text-primary);
--text-light: var(--text-secondary);
--secondary: var(--text-secondary);
--radius: var(--radius-lg);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-primary);
background-color: var(--background);
color: var(--text-primary);
font-size: var(--text-base);
line-height: 1.5;
min-height: 100vh;
}
/* =====================================================
Absorbed from the deleted app/web/static/style.css.
Kept verbatim here so legacy classes used by login/auth
pages, error pages, and password-setup pages keep
rendering. Individual rules retire in the design-pass
migration tasks; final cleanup in Task 16 removes the
ones no template references after that sweep.
===================================================== */
.container {
/* Canonical page-shell width. Bumped from the legacy 800px (which
was a relic of single-form login pages) to --width-app (1280px)
— generous enough for admin dashboards, dense tables, and
catalog grids without feeling wasteful at 1440-1600px screens.
Pages that genuinely need narrower line-length (setup wizards,
single-form flows) opt in via .container--narrow. */
max-width: var(--width-app);
margin: 0 auto;
padding: var(--space-6) var(--space-5);
}
/* Width modifiers — opt-in alternatives to the default page shell. */
.container--narrow { max-width: var(--width-narrow); } /* 800px — long-form reading, setup flows */
.container--wide { max-width: var(--width-wide); } /* 1400px — admin index lists, marketplace grids */
.container--full { max-width: none; padding-left: var(--space-4); padding-right: var(--space-4); }
/* Dashboard */
.dashboard h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 24px;
color: var(--text);
}
/* Header */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
margin-bottom: 24px;
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
gap: 16px;
}
a.logo { text-decoration: none; color: inherit; display: block; }
a.logo:hover h1 { color: var(--primary); }
.logo h1 {
font-size: 1.375rem;
font-weight: 600;
color: var(--text);
margin: 0;
letter-spacing: -0.02em;
}
.logo .subtitle {
font-size: 0.8125rem;
color: var(--text-light);
margin-top: 2px;
}
nav {
display: flex;
align-items: center;
gap: 15px;
}
.user-info {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.875rem;
color: var(--text-light);
}
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 20px;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
border-radius: var(--radius);
border: none;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
}
.btn-primary {
background-color: var(--primary);
color: white;
font-weight: 500;
transition: all 0.15s ease;
}
.btn-primary:hover {
background-color: var(--primary-dark);
box-shadow: 0 2px 8px rgba(26, 115, 232, 0.3);
}
.btn-primary:active {
transform: scale(0.98);
}
.btn-secondary {
background-color: transparent;
color: var(--secondary);
border: 1px solid var(--border);
}
.btn-secondary:hover {
background-color: var(--bg);
}
.btn-sm {
padding: 6px 12px;
font-size: 0.75rem;
}
.btn-google {
background-color: white;
color: var(--text);
border: 1px solid var(--border);
padding: 14px 28px;
font-size: 1rem;
font-weight: 500;
width: 100%;
max-width: 280px;
transition: all 0.2s ease;
}
.btn-google:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
border-color: var(--border-dark);
background-color: var(--background);
}
.btn-google:active {
transform: scale(0.98);
}
.google-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
/* Cards */
.card {
background: var(--card-bg);
border-radius: var(--radius);
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
border: 1px solid var(--border);
}
.card h3 {
font-size: 1rem;
font-weight: 600;
margin-bottom: 16px;
color: var(--text);
letter-spacing: -0.01em;
}
.card p {
color: var(--text-light);
margin-bottom: 12px;
}
.card-error {
border-left: 4px solid var(--error);
background-color: #fff8f8;
}
.card-highlight {
border-left: 4px solid var(--primary);
background-color: #f8faff;
}
.card-ai {
border-left: 4px solid var(--success);
background-color: #f8fdf8;
}
/* Manual setup collapsible */
.manual-setup {
margin-top: 16px;
}
.manual-setup summary {
cursor: pointer;
font-size: 0.875rem;
color: var(--text-light);
padding: 8px 0;
}
.manual-setup summary:hover {
color: var(--text);
}
.manual-setup .card {
margin-top: 12px;
margin-bottom: 0;
}
.card-error h3 {
color: var(--error);
}
.error-message {
color: var(--error);
font-weight: 500;
}
/* Info grid */
.info-grid {
display: grid;
gap: 12px;
}
.info-item {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.info-item .label {
font-weight: 500;
min-width: 140px;
}
.info-item .value {
color: var(--text-light);
}
/* Badges */
.badge {
display: inline-block;
padding: 2px 8px;
font-size: 0.75rem;
font-weight: 500;
border-radius: 4px;
background-color: var(--bg);
color: var(--text-light);
margin-right: 4px;
}
.badge-analyst {
background-color: #e8f5e9;
color: #2e7d32;
}
.badge-privileged {
background-color: #fff3e0;
color: #ef6c00;
}
.badge-admin {
background-color: #e3f2fd;
color: #1565c0;
}
/* Code blocks */
code {
font-family: var(--font-mono);
font-size: var(--text-sm);
background: var(--border-light);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 1px var(--space-1);
color: var(--text-primary);
}
/* Label qualifiers — optional, default, hint text next to form labels */
.label-qualifier,
.optional {
font-size: var(--text-sm);
font-weight: var(--font-normal);
color: var(--text-secondary);
margin-left: var(--space-1);
}
/* Radio option cards — visual selected state */
.sync-option-card:has(input:checked) {
border-color: var(--primary) !important;
background: var(--primary-light) !important;
}
.code-block {
background-color: #1a1a2e;
color: #e4e4e7;
padding: 14px 16px;
border-radius: var(--radius);
overflow-x: auto;
margin: 12px 0;
border: 1px solid #2d2d44;
}
.code-block code,
.code-block pre {
background: transparent;
border: none;
border-radius: 0;
padding: 0;
font-size: 0.8125rem;
color: inherit;
line-height: 1.6;
}
.code-block pre {
margin: 0;
white-space: pre-wrap;
word-break: break-all;
}
.code-block.code-compact {
padding: 10px 14px;
margin: 6px 0;
}
/* Command rows for Quick Start */
.command-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.command-row .command-label {
font-weight: 500;
font-size: 0.875rem;
min-width: 70px;
color: var(--text-light);
}
.command-row .code-block {
flex: 1;
margin: 0;
}
/* SSH config details/summary */
.ssh-config-details {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid var(--border);
}
.ssh-config-details summary {
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
color: var(--primary);
padding: 4px 0;
user-select: none;
}
.ssh-config-details summary:hover {
color: var(--primary-dark);
}
.ssh-config-details[open] summary {
margin-bottom: 8px;
}
.ssh-config-details .help-text {
margin-top: 4px;
margin-bottom: 4px;
}
/* Forms */
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-weight: 500;
margin-bottom: 8px;
}
.form-group textarea {
width: 100%;
padding: 12px 14px;
font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", monospace;
font-size: 0.8125rem;
line-height: 1.5;
border: 1px solid var(--border);
border-radius: var(--radius);
resize: vertical;
min-height: 100px;
background-color: var(--card-bg);
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-group textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.15);
}
.form-group textarea::placeholder {
color: #9aa0a6;
}
.help-text {
font-size: 0.8125rem;
color: var(--text-light);
margin-top: 8px;
line-height: 1.5;
}
.help-text strong {
color: var(--text);
font-weight: 500;
}
/* Flash messages */
.flash-messages {
margin-bottom: 24px;
}
.flash {
padding: 14px 18px;
border-radius: var(--radius);
margin-bottom: 12px;
font-size: 0.875rem;
font-weight: 500;
}
.flash-success {
background-color: #e8f5e9;
color: #2e7d32;
border: 1px solid #c8e6c9;
}
.flash-error {
background-color: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
.flash-info {
background-color: #e3f2fd;
color: #1565c0;
border: 1px solid #bbdefb;
}
.flash-warning {
background-color: #fff3e0;
color: #ef6c00;
border: 1px solid #ffe0b2;
}
/* Login page */
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
padding: 20px;
}
.login-card {
background: var(--card-bg);
border-radius: 12px;
padding: 48px 40px;
text-align: center;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
max-width: 640px;
width: 100%;
border: 1px solid var(--border);
}
.login-card h2 {
margin-bottom: 12px;
font-size: 1.75rem;
font-weight: 600;
color: var(--text);
}
.login-description {
color: var(--text-light);
margin-bottom: 32px;
font-size: 1rem;
line-height: 1.5;
}
.login-steps {
margin-top: 24px;
padding-top: 20px;
border-top: 1px solid var(--border);
text-align: left;
}
.login-steps .step {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.875rem;
color: var(--text-light);
padding: 6px 0;
}
.login-steps .step-num,
.steps-horizontal .step-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
background-color: var(--primary);
color: white;
border-radius: 50%;
font-size: 0.75rem;
font-weight: 600;
flex-shrink: 0;
}
/* Inline steps for compact layout */
.steps-inline {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border);
}
.steps-inline .step-item {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.8125rem;
color: var(--text-light);
}
/* Form row for inline layout */
.form-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.form-group-inline {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 0;
}
.form-group-inline label {
margin-bottom: 0;
}
/* AI hint inline */
.ai-hint {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid var(--border);
font-size: 0.8125rem;
color: var(--text-light);
}
.ai-hint code {
display: inline;
font-size: 0.75rem;
color: var(--primary);
}
/* Manual setup compact */
.manual-content {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
padding: 12px 0;
}
.manual-content code {
font-size: 0.75rem;
}
.manual-content .help-text {
margin: 0;
font-size: 0.75rem;
}
.login-note {
font-size: 0.8125rem;
color: var(--text-light);
margin-top: 12px;
}
/* Error page */
.error-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
}
.error-card {
text-align: center;
}
.error-card h2 {
font-size: 3rem;
margin-bottom: 16px;
}
.error-card p {
color: var(--text-light);
margin-bottom: 24px;
}
/* Footer */
footer {
margin-top: 48px;
padding-top: 20px;
padding-bottom: 8px;
border-top: 1px solid var(--border);
text-align: center;
color: var(--text-light);
font-size: 0.8125rem;
}
/* Username box and copy buttons */
.username-box {
background: #f0f9ff;
border: 2px solid #0ea5e9;
border-radius: 8px;
padding: 16px;
margin: 16px 0;
}
.username-display {
display: flex;
align-items: center;
gap: 12px;
margin-top: 8px;
}
.username-display code {
font-size: 20px;
font-weight: bold;
color: #0369a1;
background: white;
padding: 8px 16px;
border-radius: 6px;
flex-grow: 1;
}
.btn-copy {
background: #0ea5e9;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.2s ease;
}
.btn-copy:hover {
background: #0284c7;
}
.btn-copy:active {
transform: scale(0.98);
}
.btn-copy.copied {
background: #22c55e;
}
.btn-copy-inline {
background: #0ea5e9;
color: white;
border: none;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-left: 8px;
transition: background 0.15s ease;
}
.btn-copy-inline:hover {
background: #0284c7;
}
.btn-copy-block {
background: #0ea5e9;
color: white;
border: none;
padding: 10px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
width: 100%;
transition: all 0.2s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.btn-copy-block:hover {
background: #0284c7;
box-shadow: 0 2px 8px rgba(14, 165, 233, 0.3);
}
.btn-copy-block:active {
transform: scale(0.98);
}
.btn-copy-block.copied {
background: #22c55e;
}
.username-preview {
font-size: 18px;
color: #64748b;
}
.info-box {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 12px;
margin-bottom: 16px;
}
.card-success {
border-left: 4px solid var(--success);
background-color: #f8fdf8;
}
.help-details {
margin-top: 12px;
font-size: 0.875rem;
}
.help-details summary {
cursor: pointer;
color: var(--primary);
padding: 4px 0;
font-weight: 500;
}
.help-details summary:hover {
color: var(--primary-dark);
}
.help-details ul {
margin-top: 8px;
margin-left: 20px;
color: var(--text-light);
}
.help-details li {
margin-bottom: 4px;
}
/* Claude Code Setup Card on Dashboard */
.cc-setup-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
margin-bottom: 24px;
}
.cc-setup-card h3 {
color: white;
margin-bottom: 12px;
}
.cc-setup-card .cc-instruction {
color: rgba(255, 255, 255, 0.95);
margin-bottom: 16px;
font-size: 0.9375rem;
line-height: 1.6;
}
.cc-setup-card .cc-helper-text {
color: rgba(255, 255, 255, 0.8);
font-size: 0.8125rem;
margin-top: 12px;
margin-bottom: 0;
}
.cc-setup-card .btn-copy-block {
background: white;
color: #667eea;
font-weight: 600;
padding: 12px 24px;
border-radius: 6px;
border: none;
cursor: pointer;
font-size: 14px;
width: 100%;
transition: all 0.2s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.cc-setup-card .btn-copy-block:hover {
background: #f0f0f0;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.cc-setup-card .btn-copy-block:active {
transform: scale(0.98);
}
.cc-setup-card .btn-copy-block.copied {
background: #34a853;
color: white;
}
/* CLI hint in setup card */
.cc-setup-card .cli-hint {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.cc-setup-card .cli-hint summary {
cursor: pointer;
color: rgba(255, 255, 255, 0.85);
font-size: 0.8125rem;
font-weight: 500;
padding: 4px 0;
}
.cc-setup-card .cli-hint summary:hover {
color: white;
}
.cc-setup-card .cli-hint .cli-content {
margin-top: 10px;
background: rgba(255, 255, 255, 0.1);
padding: 10px 12px;
border-radius: 6px;
}
.cc-setup-card .cli-hint .cli-content code {
display: block;
background: rgba(0, 0, 0, 0.2);
color: white;
padding: 8px 10px;
border-radius: 4px;
font-size: 0.75rem;
margin-bottom: 6px;
}
.cc-setup-card .cli-hint .cli-desc {
color: rgba(255, 255, 255, 0.7);
font-size: 0.75rem;
}
/* Support info banner */
.support-info {
text-align: center;
color: var(--text-light);
font-size: 0.8125rem;
padding: 12px 16px;
background: #f8fafc;
border-radius: 6px;
margin-bottom: 20px;
border: 1px solid var(--border);
}
.support-info strong {
color: var(--text);
}
/* Auth tabs */
.auth-tabs {
display: flex;
gap: 0;
margin-bottom: 24px;
border-bottom: 2px solid var(--border);
}
.auth-tab {
flex: 1;
padding: 12px 16px;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
font-size: 0.9375rem;
font-weight: 500;
color: var(--text-light);
cursor: pointer;
transition: all 0.2s ease;
}
.auth-tab:hover {
color: var(--text);
}
.auth-tab.active {
color: var(--primary);
border-bottom-color: var(--primary);
}
.auth-tab-content {
display: none;
}
.auth-tab-content.active {
display: block;
}
.tab-description {
color: var(--text-light);
font-size: 0.875rem;
margin-bottom: 20px;
text-align: center;
}
.signup-note {
color: var(--text-light);
font-size: 0.8125rem;
text-align: center;
margin-top: 16px;
}
/* Login form styles */
.login-form {
text-align: left;
margin: 24px 0;
}
.login-form .form-group {
margin-bottom: 16px;
}
.login-form .form-group label {
display: block;
font-weight: 500;
font-size: 0.875rem;
margin-bottom: 6px;
color: var(--text);
}
.login-form .form-group input {
width: 100%;
padding: 12px 14px;
font-size: 0.9375rem;
border: 1px solid var(--border);
border-radius: var(--radius);
background-color: var(--card-bg);
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.login-form .form-group input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.15);
}
.login-form .form-group input::placeholder {
color: #9aa0a6;
}
.login-form .form-hint {
display: block;
font-size: 0.75rem;
color: var(--text-light);
margin-top: 4px;
}
.btn-block {
width: 100%;
}
.btn-link {
background: transparent;
color: var(--primary);
border: none;
padding: 8px 4px;
font-size: 0.875rem;
cursor: pointer;
text-decoration: none;
}
.btn-link:hover {
color: var(--primary-dark);
text-decoration: underline;
}
/* Login page links */
.login-links {
display: flex;
justify-content: center;
gap: 16px;
margin: 16px 0;
flex-wrap: wrap;
}
.login-links form {
margin: 0;
}
/* Request access form */
.request-access-form,
.reset-form {
display: inline;
}
/* Divider */
.divider {
display: flex;
align-items: center;
margin: 24px 0;
color: var(--text-light);
font-size: 0.875rem;
}
.divider::before,
.divider::after {
content: "";
flex: 1;
height: 1px;
background-color: var(--border);
}
.divider span {
padding: 0 16px;
}
/* Account email display */
.account-email {
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 12px 16px;
margin-bottom: 20px;
text-align: center;
font-size: 0.9375rem;
}
/* Responsive */
@media (max-width: 600px) {
.container {
padding: 15px;
}
header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.info-item {
flex-direction: column;
gap: 4px;
}
.info-item .label {
min-width: auto;
}
.username-display {
flex-direction: column;
align-items: stretch;
}
.username-display code {
text-align: center;
}
.login-links {
flex-direction: column;
gap: 8px;
}
}
/* Container */
.container-v2 {
max-width: 1400px;
margin: 0 auto;
padding: var(--space-6) var(--space-5);
}
/* Header */
.header-v2 {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-4) 0;
margin-bottom: var(--space-6);
border-bottom: 1px solid var(--border);
}
.logo-v2 h1 {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin: 0;
}
.logo-v2 .subtitle {
font-size: var(--text-sm);
color: var(--text-secondary);
margin-top: 2px;
}
.nav-v2 {
display: flex;
align-items: center;
gap: var(--space-3);
}
.user-info-v2 {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.avatar-v2 {
width: 32px;
height: 32px;
border-radius: var(--radius-full);
}
/* Buttons */
.btn-v2 {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: 0 var(--space-3);
height: 36px;
font-size: var(--text-base);
font-weight: var(--font-medium);
text-decoration: none;
border-radius: var(--radius-md);
border: none;
cursor: pointer;
transition: all 0.15s ease;
}
.btn-primary-v2 {
background-color: var(--primary);
color: white;
}
.btn-primary-v2:hover {
background-color: var(--primary-dark);
}
.btn-secondary-v2 {
background-color: var(--background);
color: var(--text-primary);
border: 1px solid var(--border);
}
.btn-secondary-v2:hover {
background-color: var(--border);
}
.btn-ghost-v2 {
background-color: transparent;
color: var(--text-secondary);
}
.btn-ghost-v2:hover {
background-color: rgba(243, 244, 246, 0.5);
}
/* Cards */
.card-v2 {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: var(--space-5);
margin-bottom: var(--space-5);
box-shadow: var(--shadow-sm);
}
.card-v2 h3 {
font-size: var(--text-md);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-4);
}
.card-v2 p {
color: var(--text-secondary);
margin-bottom: var(--space-3);
}
/* Welcome section */
.welcome-v2 {
margin-bottom: var(--space-6);
}
.welcome-v2 h2 {
font-size: var(--text-xl);
font-weight: var(--font-extrabold);
color: var(--text-primary);
margin-bottom: var(--space-2);
}
/* AI Setup Card - Primary CTA */
.ai-setup-card {
background: linear-gradient(135deg, var(--primary) 0%, #0056A3 100%);
border: none;
color: white;
padding: var(--space-6);
margin-bottom: var(--space-5);
}
.ai-setup-card h3 {
color: white;
font-size: var(--text-lg);
margin-bottom: var(--space-3);
}
.ai-setup-card .setup-description {
color: rgba(255, 255, 255, 0.9);
font-size: var(--text-base);
line-height: 1.6;
margin-bottom: var(--space-4);
}
.ai-setup-card .btn-copy-v2 {
background: white;
color: var(--primary);
font-weight: var(--font-semibold);
padding: var(--space-3) var(--space-6);
height: 44px;
width: 100%;
border-radius: var(--radius-md);
border: none;
cursor: pointer;
font-size: var(--text-base);
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.2s ease;
}
.ai-setup-card .btn-copy-v2:hover {
background: var(--border-light);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.ai-setup-card .btn-copy-v2.copied {
background: var(--success);
color: white;
}
.ai-setup-card .helper-text {
color: rgba(255, 255, 255, 0.9);
font-size: var(--text-sm);
margin-top: var(--space-3);
margin-bottom: 0;
line-height: 1.5;
}
/* CLI Hint */
.cli-hint-v2 {
margin-top: var(--space-4);
padding-top: var(--space-3);
border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.cli-hint-v2 summary {
cursor: pointer;
color: rgba(255, 255, 255, 0.8);
font-size: var(--text-sm);
font-weight: var(--font-medium);
padding: var(--space-1) 0;
list-style: none;
}
.cli-hint-v2 summary::-webkit-details-marker {
display: none;
}
.cli-hint-v2 summary::before {
content: '+ ';
}
.cli-hint-v2[open] summary::before {
content: '- ';
}
.cli-hint-v2 summary:hover {
color: white;
}
.cli-hint-v2 .cli-content {
margin-top: var(--space-3);
background: rgba(0, 0, 0, 0.15);
padding: var(--space-3);
border-radius: var(--radius-md);
}
.cli-hint-v2 code {
display: block;
background: rgba(0, 0, 0, 0.2);
color: white;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
font-family: var(--font-mono);
font-size: var(--text-sm);
margin-bottom: var(--space-2);
}
.cli-hint-v2 .cli-desc {
color: rgba(255, 255, 255, 0.7);
font-size: var(--text-xs);
}
/* Support banner */
.support-banner {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
margin-bottom: var(--space-5);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.support-banner strong {
color: var(--text-primary);
}
.support-banner .slack-badge {
display: inline-flex;
align-items: center;
gap: var(--space-1);
background: var(--primary-light);
color: var(--primary);
padding: 2px var(--space-2);
border-radius: var(--radius-full);
font-weight: var(--font-medium);
}
/* Status cards */
.status-card {
border-left: 3px solid var(--success);
}
.status-card.error {
border-left-color: var(--error);
}
.status-card.warning {
border-left-color: var(--warning);
}
/* Username box */
.username-box-v2 {
background: var(--primary-light);
border: 1px solid var(--primary);
border-radius: var(--radius-lg);
padding: var(--space-4);
margin: var(--space-4) 0;
}
.username-box-v2 label {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-secondary);
display: block;
margin-bottom: var(--space-2);
}
.username-display-v2 {
display: flex;
align-items: center;
gap: var(--space-3);
}
.username-display-v2 code {
font-family: var(--font-mono);
font-size: var(--text-xl);
font-weight: var(--font-bold);
color: var(--primary);
background: white;
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-md);
flex-grow: 1;
}
.username-display-v2 .btn-copy-sm {
background: var(--primary);
color: white;
border: none;
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-md);
cursor: pointer;
font-size: var(--text-sm);
font-weight: var(--font-semibold);
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.15s ease;
}
.username-display-v2 .btn-copy-sm:hover {
background: var(--primary-dark);
}
.username-display-v2 .btn-copy-sm.copied {
background: var(--success);
}
/* Info grid */
.info-grid-v2 {
display: grid;
gap: var(--space-3);
}
.info-item-v2 {
display: flex;
align-items: center;
gap: var(--space-3);
}
.info-item-v2 .label {
font-weight: var(--font-medium);
color: var(--text-secondary);
min-width: 120px;
font-size: var(--text-sm);
}
.info-item-v2 .value {
color: var(--text-primary);
}
.info-item-v2 code {
font-family: var(--font-mono);
font-size: var(--text-sm);
background: var(--background);
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
}
/* Badges */
.badge-v2 {
display: inline-flex;
align-items: center;
padding: 2px var(--space-2);
font-size: var(--text-xs);
font-weight: var(--font-medium);
border-radius: var(--radius-full);
background: var(--background);
color: var(--text-secondary);
margin-right: var(--space-1);
}
.badge-analyst-v2 {
background: rgba(16, 183, 127, 0.1);
color: var(--success);
}
.badge-privileged-v2 {
background: rgba(245, 159, 10, 0.1);
color: var(--warning);
}
.badge-admin-v2 {
background: rgba(0, 115, 209, 0.1);
color: var(--primary);
}
/* Form */
.form-v2 {
margin-top: var(--space-4);
}
.form-group-v2 {
margin-bottom: var(--space-4);
}
.form-group-v2 label {
display: block;
font-weight: var(--font-medium);
color: var(--text-primary);
margin-bottom: var(--space-2);
font-size: var(--text-sm);
}
.form-group-v2 textarea {
width: 100%;
padding: var(--space-3);
font-family: var(--font-mono);
font-size: var(--text-sm);
line-height: 1.5;
border: 1px solid var(--border);
border-radius: var(--radius-md);
resize: vertical;
min-height: 80px;
background: var(--surface);
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-group-v2 textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px var(--primary-light);
}
.form-group-v2 textarea::placeholder {
color: var(--text-secondary);
}
.form-row-v2 {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-4);
}
.form-info-v2 {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.form-info-v2 code {
font-family: var(--font-mono);
font-weight: var(--font-semibold);
color: var(--text-primary);
}
/* Help text */
.help-text-v2 {
font-size: var(--text-sm);
color: var(--text-secondary);
margin-top: var(--space-2);
}
.help-text-v2 strong {
color: var(--text-primary);
}
/* Info box */
.info-box-v2 {
background: var(--background);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--space-3);
margin-bottom: var(--space-4);
display: flex;
align-items: center;
gap: var(--space-2);
}
.info-box-v2 .username-preview {
font-family: var(--font-mono);
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--primary);
}
/* Alert */
.alert-v2 {
display: flex;
align-items: flex-start;
gap: var(--space-3);
padding: var(--space-4);
border-radius: var(--radius-lg);
margin-bottom: var(--space-4);
}
.alert-success-v2 {
background: rgba(16, 183, 127, 0.1);
border-left: 3px solid var(--success);
}
.alert-error-v2 {
background: rgba(234, 88, 12, 0.1);
border-left: 3px solid var(--error);
}
.alert-v2 .alert-icon {
font-size: var(--text-lg);
}
.alert-v2 h4 {
font-size: var(--text-base);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-1);
}
.alert-v2 p {
font-size: var(--text-sm);
color: var(--text-secondary);
margin: 0;
}
/* Flash messages */
.flash-messages-v2 {
margin-bottom: var(--space-5);
}
.flash-v2 {
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-lg);
margin-bottom: var(--space-3);
font-size: var(--text-sm);
font-weight: var(--font-medium);
}
.flash-success-v2 {
background: rgba(16, 183, 127, 0.1);
color: #0d9668;
border: 1px solid rgba(16, 183, 127, 0.3);
}
.flash-error-v2 {
background: rgba(234, 88, 12, 0.1);
color: #c2410c;
border: 1px solid rgba(234, 88, 12, 0.3);
}
.flash-info-v2 {
background: var(--primary-light);
color: var(--primary);
border: 1px solid rgba(0, 115, 209, 0.3);
}
/* Footer */
.footer-v2 {
margin-top: var(--space-8);
padding-top: var(--space-5);
border-top: 1px solid var(--border);
text-align: center;
color: var(--text-secondary);
font-size: var(--text-sm);
}
/* Next step card */
.next-step-card {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
border: 1px solid var(--primary);
border-left: 3px solid var(--primary);
}
.next-step-card h3 {
color: var(--primary);
}
/* Data Link Card */
.data-link-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-4) var(--space-5);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
text-decoration: none;
transition: all 0.15s ease;
margin-bottom: var(--space-5);
}
.data-link-card:hover {
border-color: var(--primary);
box-shadow: 0 0 0 1px var(--primary);
}
.data-link-content {
display: flex;
align-items: center;
gap: var(--space-4);
}
.data-link-icon {
width: 44px;
height: 44px;
background: var(--primary-light);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.data-link-text h3 {
font-size: var(--text-md);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: 2px;
}
.data-link-text p {
font-size: var(--text-sm);
color: var(--text-secondary);
margin: 0;
}
.data-link-stats {
display: flex;
gap: var(--space-4);
padding: var(--space-3) var(--space-4);
background: var(--background);
border-radius: var(--radius-md);
}
.data-stat {
text-align: center;
padding: 0 var(--space-2);
}
.data-stat .value {
font-size: var(--text-md);
font-weight: var(--font-semibold);
color: var(--text-primary);
}
.data-stat .label {
font-size: 10px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.3px;
}
.data-stat-divider {
width: 1px;
background: var(--border);
}
.data-link-arrow {
width: 32px;
height: 32px;
background: var(--primary-light);
border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
color: var(--primary);
transition: all 0.15s ease;
flex-shrink: 0;
}
.data-link-card:hover .data-link-arrow {
background: var(--primary);
color: white;
transform: translateX(2px);
}
.data-categories {
display: flex;
gap: var(--space-2);
margin-top: var(--space-2);
}
.category-tag {
font-size: var(--text-xs);
padding: 2px 8px;
border-radius: var(--radius-full);
font-weight: var(--font-medium);
}
.category-tag.finance { background: rgba(16, 183, 127, 0.1); color: #0d9668; }
.category-tag.hr { background: rgba(139, 92, 246, 0.1); color: #7c3aed; }
.category-tag.sales { background: rgba(0, 115, 209, 0.1); color: #0073D1; }
.category-tag.telemetry { background: rgba(245, 159, 10, 0.1); color: #b45309; }
.category-tag.support { background: rgba(234, 88, 12, 0.1); color: #EA580C; }
.category-tag.revenue { background: rgba(0, 115, 209, 0.1); color: #0073D1; }
.category-tag.customers { background: rgba(139, 92, 246, 0.1); color: #7c3aed; }
.category-tag.marketing { background: rgba(245, 159, 10, 0.1); color: #b45309; }
.data-highlights {
display: flex;
gap: var(--space-4);
margin-top: var(--space-3);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.data-highlight {
display: flex;
align-items: center;
gap: 4px;
}
.data-highlight strong {
color: var(--text-primary);
font-weight: var(--font-semibold);
}
/* Slack badge as link */
a.slack-badge {
text-decoration: none;
transition: all 0.15s ease;
}
a.slack-badge:hover {
background: var(--primary);
color: white;
}
/* Dashboard 2-column grid */
.dashboard-grid {
display: grid;
grid-template-columns: 3fr 2fr;
gap: var(--space-5);
align-items: start;
}
.col-left {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.col-right {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.full-width {
grid-column: 1 / -1;
}
/* Remove bottom margin for cards inside grid (grid gap handles spacing) */
.dashboard-grid .card-v2 {
margin-bottom: 0;
}
.dashboard-grid .data-link-card {
margin-bottom: 0;
}
.dashboard-grid .support-banner {
margin-bottom: 0;
}
.support-banner .heart {
color: #e74c3c;
}
/* Responsive */
@media (max-width: 1024px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 640px) {
.container-v2 {
padding: var(--space-4);
}
.header-v2 {
flex-direction: column;
gap: var(--space-3);
text-align: center;
}
.username-display-v2 {
flex-direction: column;
}
.username-display-v2 code {
width: 100%;
text-align: center;
}
.form-row-v2 {
flex-direction: column;
align-items: stretch;
}
.info-item-v2 {
flex-direction: column;
align-items: flex-start;
gap: var(--space-1);
}
.info-item-v2 .label {
min-width: auto;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
}
/* Setup steps styling */
.setup-steps {
margin: 24px 0;
display: flex;
flex-direction: column;
gap: 20px;
}
.step-item {
display: flex;
flex-direction: column;
gap: 8px;
}
.step-header {
display: flex;
align-items: center;
gap: 12px;
}
.step-number {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: white;
color: #0073D1;
border-radius: 50%;
font-weight: 700;
font-size: 16px;
flex-shrink: 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.step-header strong {
color: white;
font-size: 16px;
font-weight: 600;
}
.step-body {
margin-left: 44px;
}
.step-body p {
margin: 0 0 8px 0;
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
line-height: 1.5;
}
/* Code block styling */
.code-block-wrapper {
position: relative;
display: flex;
align-items: center;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
padding: 12px 48px 12px 12px;
margin-top: 8px;
}
.code-block {
flex: 1;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 13px;
color: #fff;
background: transparent;
border: none;
white-space: pre;
overflow-x: auto;
}
.btn-copy-code {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 4px;
padding: 6px 8px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.btn-copy-code:hover {
background: rgba(255, 255, 255, 0.25);
border-color: rgba(255, 255, 255, 0.4);
}
.btn-copy-code.copied {
background: rgba(76, 175, 80, 0.3);
border-color: rgba(76, 175, 80, 0.5);
}
.btn-copy-code svg {
width: 16px;
height: 16px;
}
/* Compact setup banner (existing users) */
.setup-compact {
background: linear-gradient(135deg, var(--primary) 0%, #0056A3 100%);
border: none;
color: white;
padding: var(--space-4) var(--space-5);
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-4);
}
.setup-compact h3 {
color: white;
margin-bottom: 0;
font-size: var(--text-base);
}
.setup-compact p {
color: rgba(255, 255, 255, 0.85);
margin: 4px 0 0 0;
font-size: var(--text-sm);
}
.setup-compact .btn-copy-v2 {
background: white;
color: var(--primary);
font-weight: var(--font-semibold);
padding: var(--space-2) var(--space-5);
height: 40px;
border-radius: var(--radius-md);
border: none;
cursor: pointer;
font-size: var(--text-sm);
white-space: nowrap;
transition: all 0.15s ease;
flex-shrink: 0;
}
.setup-compact .btn-copy-v2:hover {
background: var(--border-light);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.setup-compact .btn-copy-v2.copied {
background: var(--success);
color: white;
}
/* Unified Notifications card */
.notifications-card {
padding: 0;
}
.notifications-card h3 {
padding: var(--space-5) var(--space-5) 0;
margin-bottom: var(--space-3);
}
.notif-channels {
display: flex;
flex-direction: column;
}
.notif-channel {
padding: var(--space-4) var(--space-5);
border-top: 1px solid var(--border);
display: flex;
align-items: flex-start;
gap: var(--space-3);
}
.notif-channel-icon {
width: 36px;
height: 36px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.notif-channel-icon.telegram {
background: #e3f2fd;
}
.notif-channel-icon.desktop {
background: #f3e8ff;
}
.notif-channel-body {
flex: 1;
min-width: 0;
}
.notif-channel-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
margin-bottom: var(--space-1);
}
.notif-channel-name {
font-weight: var(--font-semibold);
font-size: var(--text-base);
color: var(--text-primary);
}
.notif-status-badge {
font-size: var(--text-xs);
font-weight: var(--font-medium);
padding: 2px 8px;
border-radius: var(--radius-full);
}
.notif-status-badge.linked {
background: rgba(16, 183, 127, 0.1);
color: var(--success);
}
.notif-status-badge.not-linked {
background: var(--background);
color: var(--text-secondary);
}
.notif-channel-desc {
font-size: var(--text-sm);
color: var(--text-secondary);
margin-bottom: var(--space-3);
}
.notif-channel-action {
display: flex;
align-items: center;
gap: var(--space-2);
}
.notif-channel-action input[type="text"] {
width: 120px;
padding: 6px 10px;
border: 1px solid var(--border);
border-radius: var(--radius-md);
font-family: var(--font-mono);
font-size: var(--text-sm);
text-align: center;
}
.btn-sm-v2 {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-1);
padding: 0 var(--space-3);
height: 32px;
font-size: var(--text-sm);
font-weight: var(--font-medium);
text-decoration: none;
border-radius: var(--radius-md);
border: none;
cursor: pointer;
transition: all 0.15s ease;
}
.btn-sm-primary {
background-color: var(--primary);
color: white;
}
.btn-sm-primary:hover {
background-color: var(--primary-dark);
}
.btn-sm-secondary {
background-color: var(--background);
color: var(--text-primary);
border: 1px solid var(--border);
}
.btn-sm-secondary:hover {
background-color: var(--border);
}
.download-link {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--primary);
text-decoration: none;
}
.download-link:hover {
text-decoration: underline;
}
.notif-install-steps {
margin: 4px 0 0;
padding-left: 20px;
font-size: var(--text-sm);
color: var(--text-secondary);
line-height: 1.7;
}
.notif-install-steps li {
padding-left: 4px;
}
.notif-install-steps .download-link {
font-weight: var(--font-normal);
}
/* Beta warning styles */
.beta-warning {
background: rgba(245, 159, 10, 0.1);
border: 1px solid rgba(245, 159, 10, 0.3);
border-radius: var(--radius-md);
padding: var(--space-2) var(--space-3);
margin-bottom: var(--space-3);
font-size: var(--text-sm);
color: #b45309;
}
.beta-warning strong {
color: #92400e;
}
.beta-tag {
display: inline-flex;
align-items: center;
background: rgba(245, 159, 10, 0.15);
color: #b45309;
font-size: var(--text-xs);
font-weight: var(--font-medium);
padding: 1px 6px;
border-radius: var(--radius-full);
margin-left: var(--space-1);
}
@media (max-width: 640px) {
.setup-compact {
flex-direction: column;
align-items: stretch;
text-align: center;
}
.setup-compact .btn-copy-v2 {
width: 100%;
}
}
/* Data Settings Card */
.settings-card {
border-left: 3px solid var(--primary);
}
.settings-card h3 {
margin-bottom: var(--space-3);
}
.dataset-toggles {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.dataset-toggle {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: var(--space-3);
background: var(--background);
border-radius: var(--radius-md);
gap: var(--space-3);
}
.dataset-toggle.disabled {
opacity: 0.5;
pointer-events: none;
}
.dataset-info {
flex: 1;
min-width: 0;
}
.dataset-label {
font-weight: var(--font-medium);
font-size: var(--text-base);
color: var(--text-primary);
display: flex;
align-items: center;
gap: var(--space-2);
}
.dataset-size {
font-size: var(--text-xs);
color: var(--text-secondary);
font-weight: var(--font-normal);
}
.dataset-desc {
font-size: var(--text-sm);
color: var(--text-secondary);
margin-top: 2px;
}
/* Toggle Switch */
.toggle-switch {
position: relative;
width: 44px;
height: 24px;
flex-shrink: 0;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--border);
transition: 0.2s ease;
border-radius: 24px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.2s ease;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.toggle-switch input:checked + .toggle-slider {
background-color: var(--primary);
}
.toggle-switch input:checked + .toggle-slider:before {
transform: translateX(20px);
}
.toggle-switch input:disabled + .toggle-slider {
cursor: not-allowed;
opacity: 0.6;
}
.settings-hint {
font-size: var(--text-sm);
color: var(--text-secondary);
margin-top: var(--space-3);
margin-bottom: 0;
padding-top: var(--space-3);
border-top: 1px solid var(--border);
}
.settings-saving {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.settings-error {
font-size: var(--text-sm);
color: var(--error);
margin-top: var(--space-2);
}
/* ========== KPI Card (Your Data Widget) ========== */
.kpi-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
}
.kpi-header {
background: linear-gradient(135deg, #1e3a5f 0%, #0f172a 100%);
color: white;
padding: var(--space-5);
display: flex;
justify-content: space-between;
align-items: center;
}
.kpi-header h3 {
font-size: var(--text-md);
font-weight: var(--font-semibold);
color: white;
margin: 0;
}
.kpi-header-stats {
display: flex;
gap: var(--space-5);
}
.kpi-header-stat {
text-align: center;
}
.kpi-header-stat .value {
font-size: var(--text-xl);
font-weight: var(--font-semibold);
}
.kpi-header-stat .label {
font-size: var(--text-xs);
opacity: 0.7;
text-transform: uppercase;
}
.kpi-header-stat-secondary {
opacity: 0.7;
}
.kpi-body {
padding: var(--space-5);
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-4);
}
.kpi-section {
padding: var(--space-4);
border-radius: var(--radius-md);
border: 1px solid var(--border);
}
.kpi-section-disabled {
opacity: 0.5;
}
.kpi-section-header {
display: flex;
align-items: center;
gap: var(--space-2);
margin-bottom: var(--space-3);
}
.kpi-icon {
width: 24px;
height: 24px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
}
.kpi-icon.batch {
background: var(--primary-light);
}
.kpi-icon.live {
background: rgba(16, 183, 127, 0.1);
}
.kpi-icon.disabled {
background: var(--background);
}
.kpi-section-title {
font-size: var(--text-sm);
font-weight: var(--font-medium);
}
.kpi-badge {
margin-left: auto;
font-size: 9px;
font-weight: var(--font-semibold);
padding: 2px 6px;
border-radius: var(--radius-full);
text-transform: uppercase;
display: flex;
align-items: center;
gap: 4px;
}
.kpi-badge.live {
background: rgba(16, 183, 127, 0.1);
color: var(--success);
}
.kpi-section-time {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-bottom: var(--space-2);
}
.kpi-section-time.live {
color: var(--success);
}
.kpi-section-datasets {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.kpi-footer {
padding: var(--space-3) var(--space-5);
background: var(--background);
display: flex;
justify-content: space-between;
align-items: center;
}
.kpi-highlights {
display: flex;
gap: var(--space-4);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.kpi-highlights strong {
color: var(--text-primary);
}
.kpi-link {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--primary);
text-decoration: none;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.15s ease;
}
.kpi-link:hover {
text-decoration: underline;
}
/* Live dot animation */
.live-dot {
width: 6px;
height: 6px;
background: var(--success);
border-radius: 50%;
animation: pulse 2s infinite;
display: inline-block;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* Disabled category tag */
.category-tag.disabled {
background: var(--background);
color: var(--text-secondary);
}
/* Responsive KPI card */
@media (max-width: 640px) {
.kpi-header {
flex-direction: column;
gap: var(--space-3);
text-align: center;
}
.kpi-body {
grid-template-columns: 1fr;
}
.kpi-footer {
flex-direction: column;
gap: var(--space-3);
text-align: center;
}
.kpi-highlights {
flex-wrap: wrap;
justify-content: center;
}
}
/* ========== Login Page Split Layout ========== */
.login-page {
min-height: 100vh;
background: var(--background);
}
.login-split {
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 100vh;
}
/* Left side: Features */
.login-features {
background: var(--primary);
color: white;
padding: var(--space-8);
display: flex;
align-items: center;
justify-content: center;
}
.features-content {
max-width: 480px;
}
.features-title {
font-size: 28px;
font-weight: var(--font-semibold);
line-height: 1.3;
margin-bottom: var(--space-3);
}
.features-subtitle {
font-size: var(--text-md);
line-height: 1.6;
opacity: 0.9;
margin-bottom: var(--space-6);
}
/* Feature cards */
.feature-cards {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.feature-card {
display: flex;
gap: var(--space-3);
padding: var(--space-4);
background: rgba(255, 255, 255, 0.1);
border-radius: var(--radius-lg);
border: 1px solid rgba(255, 255, 255, 0.15);
}
.feature-icon {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: rgba(255, 255, 255, 0.15);
color: white;
}
.feature-icon-data,
.feature-icon-memory,
.feature-icon-auto,
.feature-icon-notif {
background: rgba(255, 255, 255, 0.15);
color: white;
}
.feature-text h3 {
font-size: var(--text-base);
font-weight: var(--font-semibold);
margin-bottom: 2px;
color: white;
}
.feature-text p {
font-size: var(--text-sm);
line-height: 1.5;
color: rgba(255, 255, 255, 0.8);
margin: 0;
}
/* Right side: Login card */
.login-card-wrapper {
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-6);
background: var(--background);
}
.login-page .login-card {
background: var(--surface);
border-radius: var(--radius-xl);
padding: 48px 40px;
text-align: center;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
max-width: 420px;
width: 100%;
border: 1px solid var(--border);
}
.login-page .login-card h2 {
margin-bottom: var(--space-2);
font-size: var(--text-xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
}
.login-page .login-description {
color: var(--text-secondary);
margin-bottom: var(--space-6);
font-size: var(--text-base);
line-height: 1.5;
}
.login-page .login-note {
font-size: var(--text-sm);
color: var(--text-secondary);
margin-top: var(--space-3);
}
.login-page .login-note strong {
color: var(--text-primary);
}
.login-page .divider {
display: flex;
align-items: center;
margin: var(--space-5) 0;
color: var(--text-secondary);
font-size: var(--text-sm);
}
.login-page .divider::before,
.login-page .divider::after {
content: "";
flex: 1;
height: 1px;
background-color: var(--border);
}
.login-page .divider span {
padding: 0 var(--space-4);
}
/* Google button styling */
.login-page .btn-google {
background-color: white;
color: var(--text-primary);
border: 1px solid var(--border);
padding: 14px 28px;
font-size: var(--text-base);
font-weight: var(--font-medium);
width: 100%;
max-width: 280px;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
border-radius: var(--radius-md);
text-decoration: none;
cursor: pointer;
}
.login-page .btn-google:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
border-color: var(--border-dark);
background-color: var(--background);
}
.login-page .btn-google:active {
transform: scale(0.98);
}
/* Email magic link button styling */
.login-page .btn-email {
background-color: #4361ee;
color: white;
border: 1px solid #3a56d4;
padding: 14px 28px;
font-size: var(--text-base);
font-weight: var(--font-medium);
border-radius: var(--radius-md);
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 12px;
transition: all 0.2s ease;
cursor: pointer;
}
.login-page .btn-email:hover {
background-color: #3a56d4;
box-shadow: 0 2px 8px rgba(67, 97, 238, 0.3);
}
.login-page .btn-email:active {
transform: scale(0.98);
}
.login-page .btn-secondary {
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
padding: 14px 28px;
font-size: var(--text-base);
font-weight: var(--font-medium);
border-radius: var(--radius-md);
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
cursor: pointer;
}
.login-page .btn-secondary:hover {
background-color: var(--background);
border-color: var(--text-secondary);
}
/* Responsive: Tablet */
@media (max-width: 1024px) {
.login-split {
grid-template-columns: 1fr;
}
.login-features {
padding: var(--space-6);
min-height: auto;
}
.features-content {
max-width: 600px;
}
.features-title {
font-size: 28px;
}
.features-subtitle {
margin-bottom: var(--space-6);
}
.feature-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.feature-card {
padding: var(--space-3);
}
.feature-icon {
width: 36px;
height: 36px;
}
.login-card-wrapper {
padding: var(--space-6);
}
}
/* Responsive: Mobile */
@media (max-width: 640px) {
.login-features {
padding: var(--space-5);
}
.features-content {
max-width: 100%;
}
.features-title {
font-size: 24px;
}
.features-subtitle {
font-size: var(--text-base);
margin-bottom: var(--space-5);
}
.feature-cards {
grid-template-columns: 1fr;
gap: var(--space-3);
}
.feature-card {
padding: var(--space-3);
gap: var(--space-3);
}
.feature-text h3 {
font-size: var(--text-base);
}
.feature-text p {
font-size: var(--text-xs);
}
.login-card-wrapper {
padding: var(--space-4);
}
.login-page .login-card {
padding: var(--space-6);
}
}
/* ========== Corporate Memory Widget (Design System - Warning Color) ========== */
.memory-widget {
border-left: 3px solid var(--warning);
}
.memory-widget .card-header-custom {
background: rgba(245, 159, 10, 0.08);
padding: var(--space-4) var(--space-5);
margin: calc(-1 * var(--space-5));
margin-bottom: var(--space-4);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
gap: var(--space-2);
}
.memory-icon {
width: 28px;
height: 28px;
background: var(--warning);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.memory-widget h3 {
margin-bottom: 0;
display: flex;
align-items: center;
gap: var(--space-2);
}
.memory-stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
margin-bottom: var(--space-4);
}
.memory-stat {
text-align: center;
padding: var(--space-4);
background: var(--background);
border-radius: var(--radius-lg);
}
.memory-stat .value {
font-size: var(--text-2xl);
font-weight: var(--font-bold);
color: var(--text-primary);
line-height: 1;
}
.memory-stat .label {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-top: var(--space-1);
}
.memory-stat.highlight {
background: var(--warning);
}
.memory-stat.highlight .value,
.memory-stat.highlight .label {
color: white;
}
.memory-description {
font-size: var(--text-sm);
color: var(--text-secondary);
line-height: 1.6;
padding: var(--space-3) var(--space-4);
background: var(--background);
border-radius: var(--radius-md);
border-left: 3px solid rgba(245, 159, 10, 0.3);
margin-bottom: var(--space-4);
}
.memory-description strong {
color: var(--text-primary);
}
.memory-description code {
font-family: var(--font-mono);
font-size: var(--text-xs);
background: rgba(0, 0, 0, 0.05);
padding: 1px 4px;
border-radius: var(--radius-sm);
}
.memory-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: var(--space-3);
border-top: 1px solid var(--border);
margin-top: var(--space-4);
}
.sync-status {
display: flex;
align-items: center;
gap: 6px;
font-size: var(--text-sm);
color: var(--success);
}
.memory-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: var(--space-2) var(--space-4);
background: var(--warning);
color: white;
text-decoration: none;
font-size: var(--text-sm);
font-weight: var(--font-medium);
border-radius: var(--radius-md);
transition: all 0.15s ease;
}
.memory-link:hover {
background: #d97706;
transform: translateY(-1px);
}
/* ─── Shared modern header (used by base.html + future pages) ─── */
/* Mirrors the inline header styles in dashboard.html so all pages share chrome. */
.app-header {
background: var(--surface, #fff);
border-bottom: 1px solid var(--border, #e5e7eb);
padding: 0 32px;
/* Cancel the generic `header { margin-bottom: 24px }` from style.css,
which would otherwise leave a 24px stripe of pale grey under the
app-header. Invisible on white-on-white pages (dashboard) but very
visible on /home where the blue install-hero sits directly below. */
margin-bottom: 0;
height: 72px;
/* Override the legacy generic `header { margin-bottom: 24px }` rule from
style.css — the .app-header sits flush against the page container so
all base.html pages match the dashboard's header-to-content spacing. */
margin-bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 100;
}
.app-header-left {
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
}
.app-header-logo {
display: inline-flex;
align-items: center;
text-decoration: none;
color: inherit;
font-weight: 600;
font-size: 16px;
}
.app-header-logo svg {
display: block;
max-height: 40px;
width: auto;
}
a.app-header-logo:focus-visible {
outline: 2px solid var(--primary, #6366f1);
outline-offset: 2px;
border-radius: 4px;
}
.app-header-subtitle {
font-size: 11px;
font-weight: 500;
color: var(--text-secondary, #6b7280);
letter-spacing: 0.4px;
text-transform: uppercase;
margin-top: 2px;
}
.app-header-right {
display: flex;
align-items: center;
gap: 16px;
}
.app-header-email {
font-size: 13px;
color: var(--text-secondary, #6b7280);
font-weight: 500;
}
.app-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--primary-light, #eef2ff);
color: var(--primary, #6366f1);
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
letter-spacing: 0.3px;
}
.app-avatar-img {
width: 36px;
height: 36px;
border-radius: 50%;
border: 2px solid var(--border, #e5e7eb);
}
/* Top-nav entries. Both <a class="app-nav-link"> and
<button class="app-nav-link app-nav-menu-trigger"> share this rule
so the Admin dropdown trigger renders identical to its siblings —
same font, color, padding, hover, active state. The button-element
resets (background/border/font-family) flow from here, so
.app-nav-menu-trigger no longer needs to strip <button> chrome
separately. */
.app-nav-link {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
color: var(--text-secondary, #6b7280);
text-decoration: none;
padding: 6px 12px;
border-radius: 8px;
border: 0;
background: transparent;
cursor: pointer;
line-height: 1.2;
transition: all 0.15s ease;
}
.app-nav-link:hover {
color: var(--text-primary, #111827);
background: var(--border-light, #f3f4f6);
}
.app-nav-link.is-active,
.app-nav-link[aria-expanded="true"] {
color: var(--primary, #0073D1);
background: var(--primary-light, rgba(0, 115, 209, 0.1));
}
.app-nav-link:focus { outline: none; }
.app-nav-link:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(0, 115, 209, 0.25);
}
.app-btn-logout {
font-size: 13px;
font-weight: 500;
color: var(--text-secondary, #6b7280);
background: none;
border: 1px solid var(--border, #e5e7eb);
border-radius: 8px;
padding: 6px 14px;
cursor: pointer;
transition: all 0.15s ease;
text-decoration: none;
display: inline-block;
}
.app-btn-logout:hover {
color: var(--text-primary, #111827);
border-color: #d1d5db;
background: var(--border-light, #f3f4f6);
}
/* ── User menu (dropdown) ── */
.app-user-menu { position: relative; display: inline-flex; align-items: center; }
.app-user-menu-trigger {
display: inline-flex; align-items: center; gap: 6px;
background: none; border: 1px solid transparent;
border-radius: 999px; padding: 4px 10px 4px 4px;
cursor: pointer; transition: all 0.15s ease;
}
.app-user-menu-trigger:hover { background: var(--border-light, #f3f4f6); border-color: var(--border, #e5e7eb); }
.app-user-menu-trigger[aria-expanded="true"] { background: var(--border-light, #f3f4f6); border-color: var(--border, #e5e7eb); }
.app-user-menu-chevron { color: var(--text-secondary, #6b7280); transition: transform 0.15s ease; }
.app-user-menu-trigger[aria-expanded="true"] .app-user-menu-chevron { transform: rotate(180deg); }
.app-user-menu-panel {
position: absolute; top: calc(100% + 8px); right: 0;
min-width: 220px;
background: var(--surface, #fff);
border: 1px solid var(--border, #e5e7eb);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
padding: 6px;
z-index: 50;
}
.app-user-menu-panel[hidden] { display: none; }
.app-user-menu-header {
padding: 10px 12px 8px;
border-bottom: 1px solid var(--border-light, #f3f4f6);
margin-bottom: 4px;
}
.app-user-menu-email { font-size: 13px; font-weight: 500; color: var(--text-primary, #111827); word-break: break-all; }
.app-user-menu-role { font-size: 11px; color: var(--text-secondary, #6b7280); margin-top: 2px; text-transform: uppercase; letter-spacing: 0.3px; }
.app-user-menu-item {
display: block; padding: 8px 12px;
font-size: 13px; color: var(--text-primary, #111827);
text-decoration: none; border-radius: 6px;
}
.app-user-menu-item:hover { background: var(--border-light, #f3f4f6); }
.app-user-menu-item.is-active { background: rgba(0, 115, 209, 0.08); color: var(--primary, #0073D1); font-weight: 500; }
/* ── Admin nav dropdown — same vocabulary as user menu, scoped under nav links ── */
.app-nav-menu { position: relative; display: inline-flex; }
/* .app-nav-menu-trigger styling flows entirely from .app-nav-link above.
The trigger class is kept in markup as a hook for app.js dropdown
wiring and for targeting the chevron rotation below. */
.app-nav-menu-chevron { color: currentColor; transition: transform 0.15s ease; }
.app-nav-menu-trigger[aria-expanded="true"] .app-nav-menu-chevron { transform: rotate(180deg); }
.app-nav-menu-panel {
position: absolute; top: calc(100% + 8px); right: 0;
min-width: 200px;
background: var(--surface, #fff);
border: 1px solid var(--border, #e5e7eb);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
padding: 6px;
z-index: 50;
}
.app-nav-menu-panel[hidden] { display: none; }
.app-nav-menu-item {
display: block; padding: 8px 12px;
font-size: 13px; color: var(--text-primary, #111827);
text-decoration: none; border-radius: 6px;
}
.app-nav-menu-item:hover { background: var(--border-light, #f3f4f6); }
.app-nav-menu-item.is-active { background: rgba(0, 115, 209, 0.08); color: var(--primary, #0073D1); font-weight: 500; }
.admin-menu-separator { margin: 4px 8px; border: 0; border-top: 1px solid var(--border, #e5e7eb); }
/* Section header inside the admin dropdown — non-clickable, small-caps,
sitting in a light gray band so each named group (Activity Center /
Users & Access / Data / Agent Experience / Server) reads as a
visually contained cluster. The negative horizontal margins extend
the band edge-to-edge inside the panel (panel has 6px padding;
matching -6px here). Rounded top corners on the first header line
up with the panel's own border-radius so the first band tucks
into the top of the panel cleanly. */
.app-nav-menu-section {
background: var(--border-light, #f3f4f6);
margin: 8px -6px 4px;
padding: 6px 12px;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-secondary, #6b7280);
font-weight: 700;
}
.app-nav-menu-section:first-child {
margin-top: -6px;
border-radius: 10px 10px 0 0;
}
.app-nav-menu-section + .app-nav-menu-item { margin-top: 4px; }
/* <details data-section=...> group wrapper — visually identical to the
pre-collapse render, but each section can fold up. <summary> reuses
the .app-nav-menu-section style and gets a chevron via :before that
rotates 90deg when [open] (no JS for the visual). */
.app-nav-menu-group { margin: 0; }
.app-nav-menu-group > summary.app-nav-menu-section {
cursor: pointer;
user-select: none;
list-style: none; /* hide native disclosure triangle (most browsers) */
display: flex;
align-items: center;
gap: 6px;
}
.app-nav-menu-group > summary.app-nav-menu-section::-webkit-details-marker {
display: none; /* WebKit fallback */
}
.app-nav-menu-group > summary.app-nav-menu-section::before {
content: "";
font-size: 12px;
line-height: 1;
color: var(--text-secondary, #6b7280);
transition: transform 0.12s ease;
}
.app-nav-menu-group[open] > summary.app-nav-menu-section::before {
transform: rotate(90deg);
}
/* First-section rounded corners stayed on the original first-child rule,
but the wrapper <details> is now the first child — re-apply on the
nested <summary>. */
.app-nav-menu-group:first-of-type > summary.app-nav-menu-section {
margin-top: -6px;
border-radius: 10px 10px 0 0;
}
@media (max-width: 720px) {
.app-header { padding: 0 16px; gap: 8px; }
.app-header-email { display: none; }
.app-nav-link { padding: 6px 8px; }
}
/* ── Standard page shell ─────────────────────────────────────────────
Shared layout container for pages that extend base.html. Matches the
dashboard's `.main` dimensions so /dashboard, /marketplace, and the
marketplace detail pages all align. Add `page-shell` to a page's root
wrapper to opt in. */
.container:has(.page-shell) {
max-width: 1280px;
margin: 0 auto;
padding: 28px 32px 48px;
}
.container:has(.page-shell) > main { margin: 0; padding: 0; }
/* ==============================================================
News content vocabulary (shared)
--------------------------------------------------------------
Used by: /news (full content), /home (perex slot via .home-news-body),
/admin/news preview pane.
Author classes documented in docs/operator/news-content-guide.md:
.news-hero, .callout, .callout-{info,warn,success,danger},
.video-embed, .news-section, .news-grid-2, .news-grid-3, .news-cta.
Selectors are scope-free where the class itself is the marker
(.callout, .news-hero, .video-embed, .news-grid-*, .news-cta) so
authors can drop them anywhere — /home perex, /news body,
/admin/news preview, future admin pages.
The table / pre / code overrides ARE scoped to .news-content because
`<table>` / `<pre>` / inline `<code>` exist on plenty of other pages
that should not inherit this styling.
============================================================== */
/* Callouts — boxed inline notice with colored left border. */
.callout {
border-left: 4px solid #0073D1;
background: #E6F3FC;
color: #111827;
padding: 12px 16px;
border-radius: 6px;
margin: 12px 0;
}
.callout-info { border-left-color: #0073D1; background: #E6F3FC; }
.callout-warn { border-left-color: #C2410C; background: #FED7AA; }
.callout-success { border-left-color: #047857; background: #D1FAE5; }
.callout-danger { border-left-color: #B91C1C; background: #FEE2E2; }
.callout > :first-child { margin-top: 0; }
.callout > :last-child { margin-bottom: 0; }
/* 16:9 wrapper for YouTube/Vimeo/Loom iframes. */
.video-embed {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
border-radius: 8px;
margin: 14px 0;
background: black;
}
.video-embed iframe,
.video-embed video,
.video-embed a {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
border: 0;
}
.video-embed a {
display: flex;
align-items: center;
justify-content: center;
color: #fff;
text-decoration: underline;
}
/* Section divider — subtle top margin to separate topics. */
.news-section { margin-top: 28px; }
.news-section:first-child { margin-top: 0; }
/* 2- / 3-column responsive grids. */
.news-grid-2,
.news-grid-3 {
display: grid;
gap: 16px;
margin: 12px 0;
}
.news-grid-2 { grid-template-columns: repeat(2, 1fr); }
.news-grid-3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 720px) {
.news-grid-2,
.news-grid-3 { grid-template-columns: 1fr; }
}
/* Anchor styled as primary button. */
a.news-cta {
display: inline-block;
background: #0073D1;
color: white !important; /* override link colors on /home */
text-decoration: none !important;
padding: 9px 18px;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
margin: 8px 0;
transition: background 120ms ease;
}
a.news-cta:hover { background: #0056A3; }
/* --- .news-content scope: tables, code, pre, blockquote, headings ---
Anything inside the long-form body needs these refinements; they
would over-style chrome elsewhere on the site so we scope them. */
.news-content { font-size: 15px; line-height: 1.6; color: #111827; }
.news-content h1 { font-size: 24px; margin: 24px 0 12px; line-height: 1.25; }
.news-content h2 { font-size: 20px; margin: 22px 0 10px; line-height: 1.3; }
.news-content h3 { font-size: 17px; margin: 18px 0 8px; }
.news-content p { margin: 0 0 12px; }
.news-content ul,
.news-content ol { margin: 0 0 12px 24px; }
.news-content li { margin: 4px 0; }
.news-content blockquote {
border-left: 4px solid #E5E7EB;
padding: 6px 16px;
margin: 14px 0;
color: #4B5563;
font-style: italic;
background: #F9FAFB;
border-radius: 0 6px 6px 0;
}
/* Code: inline = light gray pill; block = dark navy with amber text.
The :not(pre code) split keeps inline code from overriding the
pre>code dark scheme — earlier versions had pre>code inheriting
the light background from the inline rule, producing the
"yellow-on-silver" unreadable combo. */
.news-content code {
background: #F3F4F6;
color: #0F172A;
padding: 1px 6px;
border-radius: 4px;
font-family: var(--font-mono);
font-size: 13px;
}
.news-content pre {
background: #0F172A;
color: #FBBF24;
padding: 14px 18px;
border-radius: 8px;
overflow-x: auto;
margin: 14px 0;
line-height: 1.55;
}
.news-content pre code {
background: transparent;
color: inherit;
padding: 0;
font-size: 13px;
border-radius: 0;
}
/* Tables: border-collapse, header band, zebra rows on hover, compact
padding. Authors get a presentable default without writing custom
classes; .news-grid-2 / .news-grid-3 covers the layout-grid use case. */
.news-content table {
width: 100%;
border-collapse: collapse;
margin: 14px 0;
font-size: 14px;
background: white;
border: 1px solid #E5E7EB;
border-radius: 8px;
overflow: hidden;
}
.news-content thead { background: #F9FAFB; }
.news-content th {
text-align: left;
padding: 10px 14px;
font-weight: 600;
color: #374151;
border-bottom: 1px solid #E5E7EB;
}
.news-content td {
padding: 10px 14px;
border-bottom: 1px solid #F3F4F6;
vertical-align: top;
}
.news-content tbody tr:last-child td { border-bottom: none; }
.news-content tbody tr:hover { background: #F9FAFB; }
.news-content table code {
/* Inline code inside table cells reads better with a slightly
lighter background than the page default. */
background: #EEF2F7;
}
/* Reusable hero block — blue gradient, eyebrow + title + lead.
Defined AFTER the .news-content overrides so its `color: white` on
nested headings wins source-order ties (both selectors share
specificity 0,1,1 since they're "one class + one type"). Authors
drop `<section class="news-hero">` anywhere — /home perex, /news
body, /admin/news preview. */
.news-hero {
background: linear-gradient(135deg, #0073D1 0%, #0056A3 100%);
color: white;
border-radius: 16px;
padding: 28px 32px;
margin: 18px 0;
box-shadow: 0 8px 24px rgba(0, 86, 163, 0.18);
}
.news-hero .eyebrow {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
opacity: 0.85;
margin-bottom: 10px;
color: white;
}
.news-hero h1,
.news-hero h2,
.news-hero h3 {
color: white;
margin: 0 0 8px;
font-weight: 600;
line-height: 1.25;
}
.news-hero h1 { font-size: 26px; letter-spacing: -0.4px; }
.news-hero h2 { font-size: 20px; }
.news-hero h3 { font-size: 17px; }
.news-hero .lead,
.news-hero p {
font-size: 15px;
opacity: 0.94;
line-height: 1.55;
margin: 0;
color: white;
}
.news-hero a {
color: #FBBF24;
text-decoration: underline;
}
.news-hero code {
background: rgba(15, 23, 42, 0.55);
color: #FBBF24;
border: 1px solid rgba(255, 255, 255, 0.18);
padding: 1px 6px;
border-radius: 4px;
font-size: 12.5px;
}
/* /home perex slot: the same callout / video-embed / hero classes
work inside .home-news-body without selector duplication, since the
rules above don't depend on a parent. The home-specific chrome
(margin, header strip) lives in home_not_onboarded.html. */
/* ==============================================================
Marketplace format guide page (/marketplace/format-guide).
Renders curator-facing markdown via markdown-it-py; the rules
below scope the document chrome to .format-guide so we don't
override <h1>/<pre>/<table> styling on other pages. Dark <pre>
matches the JSON snippets the guide includes.
============================================================== */
.container:has(.format-guide) { max-width: none; padding: 24px 16px; }
.format-guide {
max-width: 880px;
margin: 0 auto;
padding: 0;
line-height: 1.55;
}
.format-guide h1 {
font-size: 28px; font-weight: 700;
margin: 0 0 8px;
color: var(--text-primary, #111827);
}
.format-guide .lead {
font-size: 15px; color: var(--text-secondary, #6b7280);
margin-bottom: 32px; padding-bottom: 16px;
border-bottom: 1px solid var(--border, #e5e7eb);
}
.format-guide h2 {
font-size: 20px; font-weight: 600;
margin: 32px 0 12px;
color: var(--text-primary, #111827);
}
.format-guide h3 {
font-size: 16px; font-weight: 600;
margin: 24px 0 8px;
color: var(--text-primary, #111827);
}
.format-guide p {
margin: 0 0 12px;
color: var(--text-primary, #111827);
}
.format-guide a { color: var(--primary, #6366f1); text-decoration: none; }
.format-guide a:hover { text-decoration: underline; }
.format-guide code {
background: var(--surface-alt, #f3f4f6);
padding: 2px 6px; border-radius: 4px;
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 13px;
}
.format-guide pre {
background: #0f172a; color: #e2e8f0;
padding: 16px; border-radius: 8px; overflow-x: auto;
font-size: 13px;
font-family: var(--font-mono, ui-monospace, monospace);
line-height: 1.5;
}
.format-guide pre code { background: none; color: inherit; padding: 0; }
.format-guide table {
width: 100%; border-collapse: collapse;
margin: 12px 0 20px;
font-size: 13.5px;
}
.format-guide th, .format-guide td {
text-align: left; padding: 8px 12px;
border-bottom: 1px solid var(--border-light, #f3f4f6);
}
.format-guide thead th {
background: var(--surface-alt, #f9fafb);
font-weight: 600; font-size: 12px;
text-transform: uppercase; letter-spacing: 0.4px;
color: var(--text-secondary, #6b7280);
}
.format-guide ul, .format-guide ol {
margin: 0 0 12px; padding-left: 24px;
}
.format-guide li { margin-bottom: 6px; }
.format-guide blockquote {
border-left: 3px solid var(--primary, #6366f1);
margin: 12px 0; padding: 4px 12px;
background: var(--surface-alt, #f9fafb);
border-radius: 0 8px 8px 0;
}
.format-guide .back-link {
display: inline-flex; align-items: center; gap: 4px;
color: var(--text-secondary, #6b7280);
font-size: 13px;
margin-bottom: 16px;
}
.format-guide .back-link:hover { color: var(--primary, #6366f1); }
/* =====================================================
Canonical button family (Task 4)
.btn + .btn-primary | .btn-secondary | .btn-ghost | .btn-danger
+ .btn-sm | .btn-lg
Base .btn, .btn-primary, .btn-secondary, .btn-sm come from the
absorbed style.css block above (no change needed — they already use
the new tokens via the legacy-alias layer). This section appends
selector-list aliases for legacy class names ( .btn-secondary-v2, .btn-ghost-v2, .modal-btn) and defines the new
variants (.btn-ghost, .btn-danger, .btn-lg) that the legacy stylesheet
didn't ship.
Aliases removed in Task 16 once templates migrate to canonical names.
Cascade-order note: this section is the LAST in the file, so it wins
over both the absorbed legacy rules AND any earlier v2-suffixed rules
(e.g. .btn-primary-v2 hover color in the absorbed block). Adding
per-page <style> overrides will still beat this on equal-specificity
selectors due to source order — Tasks 814 strip those.
===================================================== */
.btn-primary {
background-color: var(--primary);
color: #fff;
border: 1px solid var(--primary);
transition: all 0.15s ease;
}
.btn-primary:hover,
.btn-primary-v2:hover,
.modal-btn.primary:hover {
background-color: var(--primary-dark);
border-color: var(--primary-dark);
box-shadow: 0 2px 8px rgba(0, 115, 209, 0.3);
}
.btn-secondary {
background-color: var(--surface);
color: var(--text-primary);
border: 1px solid var(--border);
}
.btn-secondary:hover,
.btn-secondary-v2:hover,
.modal-btn:not(.primary):not(.danger):hover {
background-color: var(--border-light);
}
.btn-ghost {
background-color: transparent;
color: var(--text-secondary);
border: 1px solid transparent;
}
.btn-ghost:hover,
.btn-ghost-v2:hover {
background-color: var(--border-light);
color: var(--text-primary);
}
.btn-danger {
background-color: #fff;
color: var(--error);
border: 1px solid var(--error);
}
.btn-danger:hover,
.modal-btn.danger:hover {
background-color: var(--error);
color: #fff;
}
.btn-warning {
background: #f59e0b;
color: #fff;
border: 1px solid #d97706;
}
.btn-warning:hover { filter: brightness(1.06); }
.btn-lg { padding: 12px 20px; font-size: var(--text-md); }
.btn:disabled,
.btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.btn:focus { outline: none; }
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* =====================================================
Canonical form controls (Task 5)
.search-input | .filter-bar | .filter-pill | .form-input
Selector-list aliases for legacy per-page classes ( .marketplaces-search, .kb-search, .filters-card, .pill) so existing
markup keeps rendering until migration tasks swap them out.
===================================================== */
.search-input,
.filter-bar input[type="search"],
.filter-bar input[type="text"],
.filter-bar select {
height: 36px;
padding: 0 var(--space-3);
font-family: var(--font-primary);
font-size: var(--text-sm);
color: var(--text-primary);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.search-input::placeholder,
.users-search::placeholder,
.marketplaces-search::placeholder,
.kb-search::placeholder,
.filter-bar input::placeholder { color: var(--text-muted); }
.search-input:focus,
.users-search:focus,
.marketplaces-search:focus,
.kb-search:focus,
.filter-bar input:focus,
.filter-bar select:focus {
outline: none;
border-color: var(--primary);
box-shadow: var(--focus-ring);
}
.filter-bar {
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
padding: var(--space-3) var(--space-4);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
margin-bottom: var(--space-4);
}
.filter-bar > .search-input,
.filters-card > .search-input { flex: 1 1 240px; min-width: 200px; }
.filter-pill {
display: inline-flex;
align-items: center;
height: 28px;
padding: 0 var(--space-3);
font-family: var(--font-primary);
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-secondary);
background: transparent;
border: 1px solid var(--border);
border-radius: var(--radius-full);
cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
text-decoration: none;
}
.filter-pill:hover {
background: var(--border-light);
color: var(--text-primary);
}
.filter-pill.is-active,
.filter-pill[aria-pressed="true"] {
background: var(--primary-light);
color: var(--primary);
border-color: var(--primary);
}
/* Form input — sibling of .search-input for forms (multi-line capable). */
.form-input,
input.form-input,
select.form-input,
textarea.form-input {
width: 100%;
min-height: 36px;
padding: var(--space-2) var(--space-3);
font-family: var(--font-primary);
font-size: var(--text-sm);
color: var(--text-primary);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
textarea.form-input {
min-height: 96px;
resize: vertical;
font-family: var(--font-mono);
line-height: 1.5;
}
textarea.form-textarea {
width: 100%;
min-height: 80px;
padding: var(--space-2) var(--space-3);
font-family: var(--font-primary);
font-size: var(--text-sm);
color: var(--text-primary);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
resize: vertical;
line-height: normal;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
textarea.form-textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: var(--focus-ring);
}
textarea.form-textarea::placeholder { color: var(--text-muted); }
.form-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: var(--focus-ring);
}
.form-input::placeholder { color: var(--text-muted); }
/* chip-input widget — set font on the container so the inner input's
`font: inherit` inline style picks up the right value */
.chip-input {
font-family: var(--font-primary);
font-size: var(--text-sm);
color: var(--text-primary);
}
/* =====================================================
Page-header primitive (Task 6)
.page-header + __main / __title / __subtitle / __actions / __eyebrow
+ variants: .page-header--hero | .page-header--compact
+ .tab-strip (paired tab row for marketplace tabs etc.)
===================================================== */
.page-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
margin: var(--space-6) 0 var(--space-5);
flex-wrap: wrap;
}
.page-header__main { min-width: 0; flex: 1 1 auto; }
.page-header__title {
margin: 0;
font-size: 22px;
font-weight: var(--font-semibold);
color: var(--text-primary);
line-height: 1.2;
}
.page-header__subtitle {
margin: var(--space-2) 0 0;
font-size: var(--text-sm);
color: var(--text-secondary);
line-height: 1.4;
}
.page-header__eyebrow {
margin: 0 0 var(--space-2);
font-size: var(--text-xs);
font-weight: var(--font-semibold);
text-transform: uppercase;
letter-spacing: 0.8px;
color: var(--text-secondary);
}
.page-header__actions {
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
}
/* Hero variant — gradient background, larger title, light eyebrow.
Tuned to match the look of admin_tokens.html's per-page .tokens-hero
(which analysts already liked): tighter padding, 14px radius, soft
blue glow shadow. Applied across all primary-content pages so the
admin section reads as one product instead of a patchwork. */
.page-header--hero {
padding: 28px 32px 24px;
margin: var(--space-5) auto;
/* Self-constrain to the canonical app width so the gradient stays
visually balanced even on standalone pages (catalog, corporate-memory,
install, dashboard) that don't wrap content in `.container`. On
templates that DO use `.container` (admin pages), the parent's
max-width is the same or tighter, so this is a no-op there. */
max-width: var(--width-app);
box-sizing: border-box;
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
color: #fff;
border-radius: 14px;
box-shadow: 0 4px 16px rgba(0, 115, 209, 0.2);
position: relative;
}
.page-header--hero .page-header__main { color: inherit; }
.page-header--hero .page-header__title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.01em;
color: #fff;
margin: 0 0 6px;
}
.page-header--hero .page-header__subtitle {
color: rgba(255, 255, 255, 0.85);
font-size: 13.5px;
margin: 6px 0 0;
max-width: 720px;
}
.page-header--hero .page-header__eyebrow {
color: rgba(255, 255, 255, 0.75);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
margin: 0 0 6px;
}
.page-header--hero .page-header__actions a,
.page-header--hero .page-header__actions button {
color: #fff;
}
@media (max-width: 640px) {
.page-header--hero { padding: 24px 20px 20px; border-radius: 12px; }
.page-header--hero .page-header__title { font-size: 22px; }
}
/* Compact variant — for dense admin index pages that have many controls
already and don't need vertical generosity. Smaller title, tighter
spacing, no border. */
.page-header--compact {
margin: var(--space-4) 0 var(--space-3);
}
.page-header--compact .page-header__title { font-size: 18px; }
/* Tab strip — the secondary row pattern used by /marketplace?tab=… and
similar tabbed pages. Sits below the .page-header (or inside it on
hero-variant pages). */
.tab-strip {
display: flex;
gap: var(--space-1);
align-items: center;
border-bottom: 1px solid var(--border);
margin-bottom: var(--space-5);
}
.tab-strip__item {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-2) var(--space-3);
font-family: var(--font-primary);
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-secondary);
text-decoration: none;
background: transparent;
border: 0;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
cursor: pointer;
transition: color var(--transition-fast), border-color var(--transition-fast);
}
.tab-strip__item:hover { color: var(--text-primary); }
.tab-strip__item.is-active,
.tab-strip__item[aria-selected="true"] {
color: var(--primary);
border-bottom-color: var(--primary);
}
/* =====================================================
Data display + feedback primitives (Task 7)
.data-table (+ --compact) | .empty-state | .toast | .stat-card
Selector-list aliases for legacy per-page tables ( .gp-table, .marketplaces-table, .audit-table) so existing markup
keeps rendering until migration tasks swap them out.
===================================================== */
.data-table,
.ad-table,
.ea-table,
.md-table,
.members-table,
.obs-table,
.overview-stats-table,
.registry-table,
.sample-table,
.sched-table,
.sess-table,
.sub-table,
.subs-table,
.ud-table {
width: 100%;
border-collapse: collapse;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
font-variant-numeric: tabular-nums;
}
.data-table thead,
.ad-table thead,
.ea-table thead,
.md-table thead,
.members-table thead,
.obs-table thead,
.overview-stats-table thead,
.registry-table thead,
.sample-table thead,
.sched-table thead,
.sess-table thead,
.sub-table thead,
.subs-table thead,
.ud-table thead {
background: var(--border-light);
}
.data-table th,
.ad-table th,
.ea-table th,
.md-table th,
.members-table th,
.obs-table th,
.overview-stats-table th,
.registry-table th,
.sample-table th,
.sched-table th,
.sess-table th,
.sub-table th,
.subs-table th,
.ud-table th {
padding: var(--space-3) var(--space-4);
text-align: left;
font-size: var(--text-xs);
font-weight: var(--font-semibold);
text-transform: uppercase;
letter-spacing: 0.4px;
color: var(--text-secondary);
border-bottom: 1px solid var(--border);
}
.data-table td,
.ad-table td,
.ea-table td,
.md-table td,
.members-table td,
.obs-table td,
.overview-stats-table td,
.registry-table td,
.sample-table td,
.sched-table td,
.sess-table td,
.sub-table td,
.subs-table td,
.ud-table td {
padding: var(--space-3) var(--space-4);
font-size: var(--text-sm);
color: var(--text-primary);
border-bottom: 1px solid var(--border-light);
vertical-align: middle;
}
.data-table tbody tr:last-child td,
.ad-table tbody tr:last-child td,
.ea-table tbody tr:last-child td,
.md-table tbody tr:last-child td,
.members-table tbody tr:last-child td,
.obs-table tbody tr:last-child td,
.overview-stats-table tbody tr:last-child td,
.registry-table tbody tr:last-child td,
.sample-table tbody tr:last-child td,
.sched-table tbody tr:last-child td,
.sess-table tbody tr:last-child td,
.sub-table tbody tr:last-child td,
.subs-table tbody tr:last-child td,
.ud-table tbody tr:last-child td {
border-bottom: none;
}
.data-table tbody tr:hover,
.ad-table tbody tr:hover,
.ea-table tbody tr:hover,
.md-table tbody tr:hover,
.members-table tbody tr:hover,
.obs-table tbody tr:hover,
.overview-stats-table tbody tr:hover,
.registry-table tbody tr:hover,
.sample-table tbody tr:hover,
.sched-table tbody tr:hover,
.sess-table tbody tr:hover,
.sub-table tbody tr:hover,
.subs-table tbody tr:hover,
.ud-table tbody tr:hover {
background: var(--border-light);
}
/* Compact modifier — for dense lists like /admin/activity (audit log). */
.data-table--compact th,
.data-table--compact td {
padding: var(--space-2) var(--space-3);
font-size: var(--text-xs);
}
/* =====================================================
.empty-state — for "no records" / "no results" panels
===================================================== */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
padding: var(--space-8) var(--space-6);
text-align: center;
background: var(--surface);
border: 1px dashed var(--border);
border-radius: var(--radius-lg);
color: var(--text-secondary);
}
.empty-state__icon {
font-size: 32px;
opacity: 0.5;
line-height: 1;
}
.empty-state__title {
margin: 0;
font-size: var(--text-md);
font-weight: var(--font-medium);
color: var(--text-primary);
}
.empty-state__description {
margin: 0;
max-width: 480px;
font-size: var(--text-sm);
line-height: 1.5;
}
.empty-state__actions {
margin-top: var(--space-3);
display: flex;
gap: var(--space-2);
flex-wrap: wrap;
justify-content: center;
}
/* =====================================================
.toast — global notification surface
Paired with window.appToast({kind, msg, timeout}) in app.js
===================================================== */
.toast-container {
position: fixed;
right: var(--space-5);
bottom: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-2);
z-index: 9999;
pointer-events: none;
}
.toast {
pointer-events: auto;
min-width: 240px;
max-width: 360px;
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-md);
background: var(--surface);
border: 1px solid var(--border);
box-shadow: var(--shadow-elevated);
font-size: var(--text-sm);
color: var(--text-primary);
cursor: pointer;
animation: toast-in 200ms ease;
}
.toast.is-success { border-left: 3px solid var(--success); }
.toast.is-warning { border-left: 3px solid var(--warning); }
.toast.is-error { border-left: 3px solid var(--error); }
.toast.is-info { border-left: 3px solid var(--primary); }
@keyframes toast-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: none; }
}
/* =====================================================
.stat-card — for dashboard metric tiles
===================================================== */
.stat-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-1);
min-width: 0;
}
.stat-card__label {
font-size: var(--text-xs);
font-weight: var(--font-semibold);
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-secondary);
}
.stat-card__value {
font-size: 28px;
font-weight: var(--font-semibold);
color: var(--text-primary);
font-variant-numeric: tabular-nums;
line-height: 1.1;
}
.stat-card__hint {
font-size: var(--text-xs);
color: var(--text-muted);
}
.stat-card__icon {
font-size: 18px;
opacity: 0.6;
}
/* Variant: with primary accent. */
.stat-card--accent {
border-color: var(--primary);
background: var(--primary-light);
}
.stat-card--accent .stat-card__value { color: var(--primary); }
/* Stat row container — gap-spaced grid for the dashboard tile row. */
.stat-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: var(--space-3);
margin-bottom: var(--space-5);
}