summaryrefslogtreecommitdiff
path: root/web/style.css
diff options
context:
space:
mode:
Diffstat (limited to 'web/style.css')
-rw-r--r--web/style.css299
1 files changed, 299 insertions, 0 deletions
diff --git a/web/style.css b/web/style.css
index b11faf6..a5fefb7 100644
--- a/web/style.css
+++ b/web/style.css
@@ -16,6 +16,14 @@
--text: #e2e8f0;
--text-muted: #94a3b8;
--accent: #38bdf8;
+
+ /* Approximate solid chart surface (this app has no opaque surface token —
+ --surface/--bg are translucent over the rotating background image).
+ Used as the SVG chart background, unlabeled figure, and marker rings
+ (see marks-and-anatomy.md's "surface ring" spec) for the Phase 9b
+ budget/escalation charts; the dataviz palette validator was run against
+ this exact hex. */
+ --chart-surface: #0f172a;
}
/* Reset */
@@ -2408,3 +2416,294 @@ dialog label select:focus {
border-radius: 0;
}
}
+
+/* ── Budget & Escalation dashboard (Phase 9b) ──────────────────────────────
+ Provider identity uses a fixed categorical palette (PROVIDER_COLORS in
+ app.js), deliberately distinct from --state-* — see app.js's doc comment
+ for why and how it was validated. ── */
+
+.provider-legend {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.4rem 1rem;
+ margin-top: 0.6rem;
+ font-size: 0.75rem;
+ color: var(--text-muted);
+}
+
+.provider-legend-item {
+ display: flex;
+ align-items: center;
+ gap: 0.35rem;
+}
+
+.provider-legend-swatch {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ border-radius: 2px;
+ flex-shrink: 0;
+}
+
+/* Budget meters */
+.budget-meters {
+ display: flex;
+ flex-direction: column;
+ gap: 0.85rem;
+}
+
+.budget-meter-row {
+ display: flex;
+ flex-direction: column;
+ gap: 0.3rem;
+}
+
+.budget-meter-label {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.82rem;
+}
+
+.budget-meter-label > span:first-child {
+ font-weight: 600;
+ text-transform: capitalize;
+}
+
+.budget-meter-value {
+ color: var(--text-muted);
+ font-size: 0.78rem;
+}
+
+.budget-meter-track {
+ height: 10px;
+ border-radius: 999px;
+ background: var(--border);
+ overflow: hidden;
+}
+
+.budget-meter-fill {
+ height: 100%;
+ border-radius: 999px;
+ transition: width 0.2s;
+}
+
+/* Window selector shared by the funnel + spend-over-time charts */
+.dashboard-window-row {
+ margin: 0.5rem 0 1.5rem;
+ font-size: 0.85rem;
+ color: var(--text-muted);
+}
+
+.dashboard-window-select {
+ margin-left: 0.35rem;
+}
+
+.funnel-note {
+ margin-bottom: 0.75rem;
+ max-width: 60ch;
+}
+
+/* Escalation funnel */
+.funnel-chart {
+ display: flex;
+ flex-direction: column;
+ gap: 0.6rem;
+}
+
+.funnel-row {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
+.funnel-row-label {
+ width: 60px;
+ flex-shrink: 0;
+ font-size: 0.8rem;
+ font-weight: 600;
+ color: var(--text-muted);
+}
+
+.funnel-track-outer {
+ flex: 1;
+ min-width: 0;
+}
+
+.funnel-track {
+ display: flex;
+ height: 22px;
+ border-radius: 4px;
+ overflow: hidden;
+ min-width: 4px;
+}
+
+.funnel-seg {
+ height: 100%;
+}
+
+/* The 2px surface-color gap between stacked segments (see
+ marks-and-anatomy.md's "surface gap" spec) — border rather than margin so
+ it doesn't shrink the flex-grow proportions. */
+.funnel-seg + .funnel-seg {
+ border-left: 2px solid var(--chart-surface);
+}
+
+.funnel-count {
+ width: 90px;
+ flex-shrink: 0;
+ text-align: right;
+ font-size: 0.78rem;
+ color: var(--text-muted);
+ white-space: nowrap;
+}
+
+/* Spend-over-time line chart */
+.spend-chart-wrap {
+ margin-top: 0.5rem;
+}
+
+.spend-chart-svg {
+ background: var(--chart-surface);
+ border-radius: 8px;
+ display: block;
+}
+
+.spend-chart-axis {
+ stroke: var(--border);
+ stroke-width: 1;
+}
+
+.spend-chart-tick {
+ fill: var(--text-muted);
+ font-size: 9px;
+}
+
+.spend-chart-label {
+ fill: var(--text-muted);
+ font-size: 9px;
+}
+
+.spend-chart-dot {
+ stroke: var(--chart-surface);
+ stroke-width: 2;
+}
+
+/* Role/config management panel */
+.role-card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 1rem 1.25rem;
+ margin-bottom: 1rem;
+}
+
+.role-card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.75rem;
+ margin-bottom: 0.5rem;
+}
+
+.role-card-header h3 {
+ font-size: 1rem;
+ font-weight: 700;
+ text-transform: capitalize;
+}
+
+.role-active-badge {
+ font-size: 0.72rem;
+ font-weight: 600;
+ padding: 0.2em 0.6em;
+ border-radius: 999px;
+ background: var(--state-completed);
+ color: #0f172a;
+ white-space: nowrap;
+}
+
+.role-active-badge--none {
+ background: var(--border);
+ color: var(--text-muted);
+}
+
+.role-ladder-section {
+ margin-bottom: 0.85rem;
+}
+
+.escalation-ladder-list {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 0.35rem;
+ margin-top: 0.35rem;
+}
+
+.escalation-ladder-item {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ gap: 0.5rem;
+ font-size: 0.82rem;
+ padding: 0.35rem 0.6rem;
+ background: rgba(148, 163, 184, 0.08);
+ border-radius: 6px;
+}
+
+.escalation-ladder-tier {
+ font-weight: 700;
+ min-width: 3.5rem;
+}
+
+.escalation-ladder-candidates {
+ font-family: ui-monospace, monospace;
+ font-size: 0.78rem;
+}
+
+.escalation-ladder-meta {
+ color: var(--text-muted);
+ font-size: 0.75rem;
+ margin-left: auto;
+}
+
+.role-version-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+}
+
+.role-version-row {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ font-size: 0.82rem;
+ padding: 0.4rem 0.1rem;
+ border-top: 1px solid var(--border);
+}
+
+.role-version-num {
+ font-weight: 700;
+ min-width: 2.5rem;
+}
+
+.role-version-status {
+ font-size: 0.68rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.03em;
+ padding: 0.15em 0.5em;
+ border-radius: 999px;
+ color: #0f172a;
+}
+
+.role-version-status--active { background: var(--state-completed); }
+.role-version-status--draft { background: var(--state-queued); }
+.role-version-status--retired { background: var(--border); color: var(--text-muted); }
+
+.role-version-meta {
+ color: var(--text-muted);
+ font-size: 0.75rem;
+}
+
+.role-version-row .btn-primary.btn-sm {
+ margin-left: auto;
+}