/* simple-cost-dashboard — design tokens + composants partagés (page globale et page détail tenant) */ :root { color-scheme: light; --surface-1: #fcfcfb; /* cartes */ --plane: #f9f9f7; /* fond de page */ --text-primary: #0b0b0b; --text-secondary: #52514e; --text-muted: #898781; --gridline: #e1e0d9; --axis: #c3c2b7; --border: rgba(11, 11, 11, 0.10); --delta-good: #006300; --delta-bad: #d03b3b; --status-good: #0ca30c; --status-warning: #fab219; --status-serious: #ec835a; --status-critical: #d03b3b; --provider-aws: #eb6834; /* orange — identité AWS */ --provider-azure: #2a78d6; /* bleu — identité Azure */ --series-1: #2a78d6; --series-2: #eb6834; --series-3: #1baf7a; --series-4: #eda100; --series-5: #e87ba4; --series-6: #008300; --series-7: #4a3aa7; --series-8: #e34948; --series-other: #c3c2b7; --radius-card: 12px; --radius-control: 8px; --shadow-card: 0 1px 2px rgba(11, 11, 11, 0.04), 0 1px 8px rgba(11, 11, 11, 0.04); } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; --surface-1: #1a1a19; --plane: #0d0d0d; --text-primary: #ffffff; --text-secondary: #c3c2b7; --text-muted: #898781; --gridline: #2c2c2a; --axis: #383835; --border: rgba(255, 255, 255, 0.10); --delta-good: #0ca30c; --delta-bad: #e66767; --provider-aws: #d95926; --provider-azure: #3987e5; --series-1: #3987e5; --series-2: #d95926; --series-3: #199e70; --series-4: #c98500; --series-5: #d55181; --series-6: #008300; --series-7: #9085e9; --series-8: #e66767; --series-other: #383835; --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 8px rgba(0, 0, 0, 0.2); } } * { box-sizing: border-box; } html, body { margin: 0; padding: 0; } body { background: var(--plane); color: var(--text-primary); font-family: system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 14px; line-height: 1.45; } a { color: inherit; } /* ---------------------------------------------------------------- */ /* Layout */ /* ---------------------------------------------------------------- */ .topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 32px; border-bottom: 1px solid var(--border); background: var(--surface-1); } .topbar-title { display: flex; align-items: baseline; gap: 10px; } .topbar-title h1 { font-size: 17px; font-weight: 600; margin: 0; } .topbar-title .breadcrumb { color: var(--text-muted); font-size: 13px; } .topbar-title .breadcrumb a { color: var(--text-secondary); text-decoration: none; } .topbar-title .breadcrumb a:hover { text-decoration: underline; } .container { max-width: 1240px; margin: 0 auto; padding: 28px 32px 64px; } .section-title { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin: 32px 0 12px; } .section-title:first-child { margin-top: 0; } /* ---------------------------------------------------------------- */ /* Cartes / KPI */ /* ---------------------------------------------------------------- */ .card { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); } .kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; } .kpi-card { padding: 18px 20px; } .kpi-label { font-size: 13px; color: var(--text-secondary); margin: 0 0 8px; } .kpi-value { font-size: 26px; font-weight: 600; margin: 0; font-variant-numeric: proportional-nums; } .kpi-delta { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 500; margin-top: 6px; } .kpi-delta.up { color: var(--delta-bad); } .kpi-delta.down { color: var(--delta-good); } .kpi-delta.flat { color: var(--text-muted); } .kpi-sub { font-size: 12px; color: var(--text-muted); margin-top: 6px; } .kpi-row.no-comparison .kpi-card-comparison { display: none; } /* ---------------------------------------------------------------- */ /* Grille de graphiques */ /* ---------------------------------------------------------------- */ .chart-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: 16px; align-items: stretch; } @media (max-width: 860px) { .chart-grid { grid-template-columns: 1fr; } } .chart-card { padding: 20px 20px 12px; display: flex; flex-direction: column; } .chart-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; gap: 12px; flex-wrap: wrap; } .chart-card-header h2 { font-size: 14px; font-weight: 600; margin: 0; } .chart-card-sub { font-size: 12px; color: var(--text-muted); margin: 2px 0 0; } .chart-canvas-wrap { position: relative; flex: 1; min-height: 220px; } .legend { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: var(--text-secondary); margin-top: 10px; } .legend-item { display: inline-flex; align-items: center; gap: 6px; } .legend-dot { width: 8px; height: 8px; border-radius: 2px; flex: none; } /* ---------------------------------------------------------------- */ /* Contrôles (recherche, plage 7j/30j) */ /* ---------------------------------------------------------------- */ .search-input { width: 240px; max-width: 100%; padding: 7px 10px; border-radius: var(--radius-control); border: 1px solid var(--border); background: var(--surface-1); color: var(--text-primary); font-size: 13px; } .search-input:focus { outline: 2px solid var(--series-1); outline-offset: -1px; } .range-toggle { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-control); overflow: hidden; } .range-toggle button { border: none; background: transparent; color: var(--text-secondary); font-size: 13px; padding: 6px 14px; cursor: pointer; font-family: inherit; } .range-toggle button + button { border-left: 1px solid var(--border); } .range-toggle button.active { background: var(--series-1); color: #ffffff; font-weight: 500; } .selection-note { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--text-secondary); } .selection-note button { border: 1px solid var(--border); background: transparent; color: var(--text-secondary); font-size: 12px; padding: 4px 10px; border-radius: var(--radius-control); cursor: pointer; font-family: inherit; } .selection-note button:hover { color: var(--text-primary); background: color-mix(in srgb, var(--text-primary) 6%, transparent); } /* ---------------------------------------------------------------- */ /* Tables */ /* ---------------------------------------------------------------- */ table.data-table { width: 100%; border-collapse: collapse; font-size: 13px; } .data-table thead th { text-align: left; font-weight: 500; color: var(--text-muted); padding: 8px 12px; border-bottom: 1px solid var(--gridline); white-space: nowrap; } .data-table td { padding: 10px 12px; border-bottom: 1px solid var(--gridline); vertical-align: middle; } .data-table tbody tr:last-child td { border-bottom: none; } .data-table tbody tr.clickable { cursor: pointer; } .data-table tbody tr.clickable:hover { background: color-mix(in srgb, var(--text-primary) 4%, transparent); } .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; } .name-cell { display: flex; align-items: center; gap: 8px; } .provider-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; padding: 2px 7px; border-radius: 999px; flex: none; } .provider-badge.aws { color: var(--provider-aws); background: color-mix(in srgb, var(--provider-aws) 14%, transparent); } .provider-badge.azure { color: var(--provider-azure); background: color-mix(in srgb, var(--provider-azure) 14%, transparent); } .status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; } .status-dot.ok { background: var(--status-good); } .status-dot.error { background: var(--status-critical); } .status-dot.unknown { background: var(--text-muted); } .status-label { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; } /* ---------------------------------------------------------------- */ /* Bandeaux d'état / vides */ /* ---------------------------------------------------------------- */ .banner { display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px; border-radius: var(--radius-control); font-size: 13px; margin-bottom: 20px; } .banner.error { background: color-mix(in srgb, var(--status-critical) 12%, var(--surface-1)); color: var(--text-primary); border: 1px solid color-mix(in srgb, var(--status-critical) 30%, transparent); } .banner strong { font-weight: 600; } .empty-state { padding: 40px 20px; text-align: center; color: var(--text-muted); font-size: 13px; } .skeleton { padding: 40px 20px; text-align: center; color: var(--text-muted); font-size: 13px; } /* ---------------------------------------------------------------- */ /* Panneau latéral (drawer) — détail par ressource d'un service */ /* ---------------------------------------------------------------- */ .drawer-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); opacity: 0; pointer-events: none; transition: opacity 0.15s ease; z-index: 40; } .drawer-backdrop.open { opacity: 1; pointer-events: auto; } .drawer { position: fixed; top: 0; right: 0; bottom: 0; width: 440px; max-width: 92vw; background: var(--surface-1); border-left: 1px solid var(--border); box-shadow: -8px 0 24px rgba(0, 0, 0, 0.14); transform: translateX(100%); transition: transform 0.2s ease; z-index: 41; display: flex; flex-direction: column; } .drawer.open { transform: translateX(0); } .drawer-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--border); flex: none; } .drawer-header h3 { margin: 0; font-size: 15px; font-weight: 600; word-break: break-word; } .drawer-header p { margin: 4px 0 0; font-size: 12px; color: var(--text-muted); } .drawer-close { border: none; background: transparent; color: var(--text-muted); font-size: 22px; line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: var(--radius-control); flex: none; } .drawer-close:hover { color: var(--text-primary); background: color-mix(in srgb, var(--text-primary) 6%, transparent); } .drawer-body { flex: 1; overflow-y: auto; padding: 8px 20px 20px; } .resource-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--gridline); } .resource-row:last-child { border-bottom: none; } .resource-name { font-size: 13px; overflow-wrap: anywhere; } .resource-pct { color: var(--text-muted); font-size: 12px; } .resource-amount { font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; white-space: nowrap; text-align: right; flex: none; } /* ---------------------------------------------------------------- */ /* Chart.js — surcharges de style (couleurs de police/grille) */ /* ---------------------------------------------------------------- */ .chartjs-tooltip { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }