sqlinsight-pro/src/SQLInsight.Web/wwwroot/app.css
Søren Eggert Lundsteen Olsen 244153e9e4 Rules the version does not support are "not applicable", not "not measurable"
On SQL Server 2005 twenty rules stood as "not measurable" (Query Store,
system_health, memory columns …) between the real gaps, and counted in the
overview's "regler ikke målbare". They are not gaps: the version simply
lacks what they look at.

- New RuleOutcome.NotApplicable (last in the enum: outcomes are stored as
  numbers, so existing rows keep their meaning).
- A rule or collector missing only capabilities inherent to the instance
  (version-gated, plan cache memory columns, or Azure-managed) is not
  applicable, with the version each needs: "Ikke relevant for SQL Server
  2005: kræver QueryStore (fra SQL Server 2016)". Missing settings or rights
  (HADR not enabled …), collector failures and permission errors stay
  "not measurable". CollectedData.MarkNotApplicable carries this from
  collectors to the rules that use their data; community rules below their
  minimumVersion are not applicable too.
- Counts are unchanged in shape: the overview, KPI, JSON API and Prometheus
  count outcomes 2 and 3 only. The instance page lists the rest in a
  collapsed "Ikke relevant for SQL Server 2005 (n)" (details/summary, no
  JS) after the real gaps. Report controls whose core rule does not apply
  count neither as met nor as not measured.

Tests: engine (version vs. setting vs. collected data), inherent
capabilities, the existing version tests now expect not applicable, report
control status, and the web page and overview count.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 16:05:39 +02:00

195 lines
14 KiB
CSS

/* SQLInsight Pro design system: light first, neutral surfaces, one accent, semantic severity colours. */
@font-face {
font-family: "Inter";
src: url("fonts/inter-latin-wght-normal.woff2") format("woff2");
font-weight: 100 900; font-style: normal; font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: "Inter";
src: url("fonts/inter-latin-ext-wght-normal.woff2") format("woff2");
font-weight: 100 900; font-style: normal; font-display: swap;
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
:root {
--bg: #f6f7f9;
--surface: #ffffff;
--surface-2: #f1f3f6;
--border: #e4e7ec;
--text: #101828;
--text-2: #475467;
--text-3: #98a2b3;
--accent: #0e7490; /* deep teal */
--accent-soft: #e0f2f6;
--accent-text: #0b5c72;
--critical: #d92d20; --critical-soft: #fee4e2;
--high: #e04f16; --high-soft: #ffead5;
--medium: #b54708; --medium-soft: #fef0c7;
--low: #1570ef; --low-soft: #d1e9ff;
--info: #667085; --info-soft: #f2f4f7;
--ok: #079455; --ok-soft: #dcfae6;
--radius: 12px;
--shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .06);
--sidebar: 256px;
font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
font-feature-settings: "cv11", "ss01";
color-scheme: light;
}
:root[data-theme="dark"] {
--bg: #0c111d; --surface: #161b26; --surface-2: #1f2533; --border: #2a3140;
--text: #f5f5f6; --text-2: #cecfd2; --text-3: #85888e;
--accent: #22b8cf; --accent-soft: #0e2d36; --accent-text: #7dd9e8;
--critical-soft: #3b1512; --high-soft: #3a1d0d; --medium-soft: #3a2a07; --low-soft: #0f2744; --info-soft: #1f2533; --ok-soft: #0b2e1d;
--shadow: none;
color-scheme: dark;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.55; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.icon { width: 18px; height: 18px; flex: none; }
/* Shell */
.shell { display: grid; grid-template-columns: var(--sidebar) 1fr; min-height: 100vh; background: linear-gradient(to right, var(--surface) calc(var(--sidebar) - 1px), var(--border) calc(var(--sidebar) - 1px), var(--border) var(--sidebar), transparent var(--sidebar)); }
.sidebar { position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; gap: 1.5rem; padding: 1.25rem 1rem; }
.brand { display: flex; align-items: center; gap: .6rem; color: var(--text); font-size: 1.05rem; padding: 0 .5rem; }
.brand:hover { text-decoration: none; }
.brand strong { color: var(--accent); }
.logo { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 9px; background: linear-gradient(135deg, var(--accent), #155e75); color: #fff; font-weight: 700; font-size: .8rem; letter-spacing: .02em; }
.nav { display: flex; flex-direction: column; gap: 2px; }
.nav-label { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--text-3); padding: .9rem .75rem .35rem; }
.nav a { display: flex; align-items: center; gap: .65rem; padding: .5rem .75rem; border-radius: 8px; color: var(--text-2); font-weight: 500; }
.nav a:hover { background: var(--surface-2); text-decoration: none; color: var(--text); }
.nav a.active { background: var(--accent-soft); color: var(--accent-text); }
.support-card { margin-top: auto; padding: 1rem; border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--border); display: flex; flex-direction: column; gap: .35rem; }
.support-card .icon { color: var(--accent); width: 22px; height: 22px; }
.support-card p { margin: 0; color: var(--text-2); font-size: .82rem; }
.support-card a { font-weight: 600; font-size: .85rem; }
.main { display: flex; flex-direction: column; min-width: 0; }
.topbar { display: flex; justify-content: space-between; align-items: center; padding: .75rem 2rem; border-bottom: 1px solid var(--border); background: color-mix(in srgb, var(--surface) 85%, transparent); backdrop-filter: blur(8px); position: sticky; top: 0; z-index: 5; }
.topbar-note { color: var(--text-3); font-size: .82rem; }
.theme-toggle { display: inline-flex; align-items: center; gap: .4rem; border: 1px solid var(--border); background: var(--surface); color: var(--text-2); border-radius: 8px; padding: .35rem .7rem; font: inherit; font-size: .82rem; cursor: pointer; }
.theme-toggle:hover { color: var(--text); background: var(--surface-2); }
.page { width: 100%; max-width: 1320px; padding: 1.75rem 2rem 2rem; }
.footer { color: var(--text-3); font-size: .8rem; padding: 1rem 2rem 2rem; }
/* Typography */
h1 { font-size: 1.6rem; line-height: 1.25; letter-spacing: -.02em; margin: .2rem 0 .35rem; display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
h2 { font-size: 1.02rem; letter-spacing: -.01em; margin: 0 0 .75rem; }
h3 { font-size: .95rem; margin: 1.1rem 0 .3rem; }
.lead { color: var(--text-2); margin: 0 0 1.5rem; }
.muted { color: var(--text-3); }
.crumbs { margin: 0; font-size: .85rem; color: var(--text-3); }
.mono, pre, code { font-family: "Cascadia Code", "JetBrains Mono", Consolas, monospace; font-size: .82rem; }
/* Cards and sections */
.card, .panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 1.25rem 1.4rem; margin-bottom: 1.25rem; }
.card-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: .75rem; }
.card-head h2 { margin: 0; }
.card > table, .panel > table { margin: 0 -1.4rem -1.25rem; width: calc(100% + 2.8rem); border: 0; border-top: 1px solid var(--border); border-radius: 0 0 var(--radius) var(--radius); box-shadow: none; }
.section { margin-top: 1.75rem; }
.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 1.25rem; }
.grid2 > .card, .grid2 > .panel { margin-bottom: 0; }
/* KPI tiles */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 1rem 1.2rem; display: flex; gap: .9rem; align-items: center; }
.kpi .kpi-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 10px; background: var(--c-soft, var(--accent-soft)); color: var(--c, var(--accent)); }
.kpi strong { display: block; font-size: 1.55rem; line-height: 1.1; letter-spacing: -.02em; }
.kpi span { color: var(--text-2); font-size: .82rem; }
/* Tables */
table { width: 100%; border-collapse: separate; border-spacing: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
th, td { text-align: left; padding: .65rem 1rem; border-bottom: 1px solid var(--border); vertical-align: top; }
th { background: var(--surface-2); font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-2); font-weight: 600; }
tbody tr:hover td { background: color-mix(in srgb, var(--surface-2) 60%, transparent); }
tr:last-child td { border-bottom: 0; }
td .muted { font-size: .82rem; }
.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.bad { color: var(--critical); font-weight: 600; }
meter { width: 64px; height: 8px; margin-right: .4rem; vertical-align: middle; }
/* Severity */
.sev-critical { --c: var(--critical); --c-soft: var(--critical-soft); }
.sev-high { --c: var(--high); --c-soft: var(--high-soft); }
.sev-medium { --c: var(--medium); --c-soft: var(--medium-soft); }
.sev-low { --c: var(--low); --c-soft: var(--low-soft); }
.sev-info { --c: var(--info); --c-soft: var(--info-soft); }
.sev-ok { --c: var(--ok); --c-soft: var(--ok-soft); }
.badge { display: inline-flex; align-items: center; gap: .35rem; padding: .12rem .6rem; border-radius: 999px; font-size: .75rem; font-weight: 600; white-space: nowrap; color: var(--c, var(--info)); background: var(--c-soft, var(--info-soft)); }
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.counts { display: flex; gap: .3rem; flex-wrap: wrap; }
/* Charts */
.chart { margin: 0; }
.chart svg { width: 100%; height: 90px; display: block; }
.chart .line { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.chart .grid { stroke: var(--border); stroke-width: 1; vector-effect: non-scaling-stroke; stroke-dasharray: 3 4; }
.chart figcaption { display: flex; justify-content: space-between; gap: 1rem; font-size: .82rem; color: var(--text-2); margin-top: .5rem; }
.chart figcaption span:first-child { font-weight: 600; color: var(--text); }
/* Alerts */
.alert { border: 1px solid var(--c, var(--critical)); background: var(--c-soft, var(--critical-soft)); border-radius: var(--radius); padding: .85rem 1rem; margin: 0 0 1.25rem; }
.alert.warn { --c: var(--medium); --c-soft: var(--medium-soft); }
.alert pre { white-space: pre-wrap; margin: .5rem 0 0; }
/* Finding detail */
.evidence { box-shadow: none; }
.evidence th { width: 38%; text-transform: none; letter-spacing: 0; font-size: .85rem; background: transparent; color: var(--text-2); font-weight: 500; }
.recommendation { border-left: 4px solid var(--ok); }
.script { position: relative; }
.script pre { background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; padding: 1rem; overflow-x: auto; margin: .5rem 0 0; }
.copy { position: absolute; top: .5rem; right: .5rem; border: 1px solid var(--border); background: var(--surface); color: var(--text); border-radius: 6px; padding: .2rem .65rem; font: inherit; font-size: .8rem; cursor: pointer; }
.empty { background: var(--surface); border: 1px dashed var(--border); border-radius: var(--radius); padding: 1.25rem; color: var(--text-2); }
@media (max-width: 900px) {
.shell { grid-template-columns: 1fr; }
.shell { background: none; }
.sidebar { position: static; height: auto; background: var(--surface); border-bottom: 1px solid var(--border); }
.support-card { display: none; }
.page, .topbar { padding-left: 1rem; padding-right: 1rem; }
.grid2 { grid-template-columns: 1fr; }
}
details { border-top: 1px solid var(--border); padding: .6rem 0; }
details summary { cursor: pointer; font-weight: 500; }
details .script pre { max-height: 32rem; }
.report-links { display: flex; gap: .6rem; flex-wrap: wrap; }
a.button { display: inline-block; background: var(--accent); color: #fff; padding: .5rem 1rem; border-radius: 8px; font-weight: 600; }
a.button:hover { filter: brightness(1.08); }
.panel.insight { border-left: 4px solid var(--accent); }
/* Support cases: the form, notices and the "Opret supportsag" context menu on findings (support-menu.js). */
.support-form { display: grid; gap: .9rem; max-width: 52rem; }
.support-form label { display: grid; gap: .3rem; font-size: .85rem; color: var(--text-2); }
.support-form input, .support-form textarea { font: inherit; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: .5rem .65rem; }
.support-form textarea { resize: vertical; }
.support-contact { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .9rem; }
.support-form button.button { justify-self: start; border: 0; cursor: pointer; font: inherit; background: var(--accent); color: #fff; padding: .5rem 1rem; border-radius: 8px; font-weight: 600; }
.notice { border-radius: var(--radius); padding: .75rem 1rem; margin: 0 0 1.25rem; border: 1px solid var(--border); background: var(--surface); }
.notice.ok { border-left: 4px solid var(--accent); }
.notice.bad { border-left: 4px solid #d92d20; }
.context-menu { position: absolute; z-index: 1000; min-width: 12rem; padding: .3rem; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; box-shadow: var(--shadow); }
.context-menu a { display: block; padding: .45rem .7rem; border-radius: 6px; color: var(--text); text-decoration: none; }
.context-menu a:hover, .context-menu a:focus { background: var(--surface-2); outline: none; }
/* Time window analysis */
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-top: 1.75rem; }
.section-head h2.section { margin-top: 0; }
.window-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .75rem 1rem; }
.window-form label { display: grid; gap: .25rem; font-size: .85rem; color: var(--text-2); }
.window-form input, .window-form select { font: inherit; padding: .4rem .5rem; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); }
.window-form button.button { border: 0; cursor: pointer; font: inherit; background: var(--accent); color: #fff; padding: .5rem 1rem; border-radius: 8px; font-weight: 600; }
.window-form .presets { display: flex; gap: .75rem; font-size: .9rem; }
.small { font-size: .8rem; }
.panel.unusual { border-left: 4px solid var(--critical); }
tr.unusual td { background: var(--critical-soft); }
pre.sql { white-space: pre-wrap; word-break: break-word; max-height: 9rem; overflow: auto; margin: .25rem 0 0; font-size: .8rem; }
/* Rules that do not apply to the instance's version: collapsed, out of the way of real gaps */
details.not-applicable { margin-top: 1rem; }
details.not-applicable summary { cursor: pointer; color: var(--text-2); font-weight: 600; }
details.not-applicable > table { margin-top: .75rem; }