/*
 * SPDX-License-Identifier: Apache-2.0
 * SPDX-FileCopyrightText: 2026 ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG, Cologne
 * SPDX-FileCopyrightText: Author: Pierre Gronau <Pierre.Gronau@ndaal.eu>
 */

/* vl-web's own styles. Served as a file because the Content-Security-Policy
   allows no inline style element (style-src 'self'). */

@font-face {
    font-family: Roboto;
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/roboto/roboto-regular.ttf') format('truetype');
}

@font-face {
    font-family: Roboto;
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/static/fonts/roboto/roboto-bold.ttf') format('truetype');
}

@font-face {
    font-family: 'Roboto Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/roboto-mono/roboto-mono-regular.ttf') format('truetype');
}

@font-face {
    font-family: 'Roboto Mono';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/static/fonts/roboto-mono/roboto-mono-bold.ttf') format('truetype');
}

:root {
    --vl-accent: #0d6efd;
    --vl-critical: #dc3545;
    --vl-high: #fd7e14;
    --vl-medium: #ffc107;
    --vl-low: #198754;
    --vl-info: #0dcaf0;
}

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    font-family: Roboto, 'Segoe UI', system-ui, -apple-system, sans-serif;
}

main {
    flex: 1;
}

.vuln-id {
    font-family: 'Roboto Mono', 'Courier New', Courier, monospace;
    font-weight: 600;
}

pre.json-block {
    background-color: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: .375rem;
    padding: 1rem;
    overflow-x: auto;
    max-height: 60vh;
    font-size: 0.85rem;
}

.navbar-brand {
    font-size: 1.25rem;
    letter-spacing: -0.02em;
}

.brand-icon {
    color: var(--vl-accent);
}

.severity-critical {
    background-color: var(--vl-critical) !important;
    color: #ffffff !important;
}

.severity-high {
    background-color: var(--vl-high) !important;
    color: #000000 !important;
}

.severity-medium {
    background-color: var(--vl-medium) !important;
    color: #000000 !important;
}

.severity-low {
    background-color: var(--vl-low) !important;
    color: #ffffff !important;
}

.cvss-badge {
    font-family: 'Roboto Mono', 'Courier New', Courier, monospace;
    font-size: 0.8rem;
    padding: 0.25em 0.55em;
    border-radius: 0.375rem;
    font-weight: 700;
}

/* Shared fixed-width table column helpers — used by the
   dashboard + CNA + Debian tables to replace inline styles
   flagged by htmlhint's `inline-style-disabled` rule. */
.col-w-4 { width: 4rem; }
.col-w-6 { width: 6rem; }
.col-w-9 { width: 9rem; }
.col-w-11 { width: 11rem; }

.stat-card {
    border: none;
    border-radius: 0.75rem;
    transition: transform 0.15s ease;
}

.stat-card:hover {
    transform: translateY(-2px);
}

.stat-card .stat-icon {
    font-size: 2rem;
    opacity: 60%;
}

.stat-card .stat-value {
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.1;
}

.stat-card .stat-label {
    font-size: 0.85rem;
    opacity: 80%;
}

.table th {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 70%;
    border-bottom-width: 2px;
}

.search-hero {
    padding: 2rem 0;
}

.search-hero .form-control {
    font-size: 1.1rem;
    padding: 0.75rem 1rem;
}

.navbar-blur {
    background-color: rgb(var(--bs-body-bg-rgb), 0.95);
    backdrop-filter: blur(10px);
}
.search-form-container { max-width: 600px; }
.col-id { width: 200px; }
.col-severity { width: 110px; }
.col-cvss { width: 80px; }
.col-source { width: 100px; }
.col-updated { width: 120px; }
.cell-truncate { max-width: 350px; }
.pre-wrap { white-space: pre-wrap; }
.chart-container { max-height: 500px; }

.changelog-scroll {
    max-height: 70vh;
    overflow-y: auto;
}

.cve-icu-year-table-scroll {
    max-height: 420px;
    overflow-y: auto;
}
.cpe-dictionary-filter-width { max-width: 22rem; }

footer {
    font-size: 0.8rem;
}

#page-loader {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 3px;
    z-index: 9999;
    background: transparent;
}

#page-loader.loading {
    background: linear-gradient(90deg, var(--vl-accent), #6610f2, var(--vl-accent));
    background-size: 200% 100%;
    animation: loading-bar 1.5s ease infinite;
}

@keyframes loading-bar {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* Dashboards dropdown — 24 entries arranged in a responsive
   multi-column grid so the list stays scanable on every
   screen size.  Stacking-context machinery kept: promoting
   `nav.navbar` to its own stacking context
   (`position: relative; z-index: 10050`, Bootstrap modal is
   1055 so we stay under that) prevents the dropdown from
   rendering behind page-content cards with their own
   non-zero z-indexes.  `max-height` + overflow-y is kept as
   a safety belt for very short viewports. */
nav.navbar {
    position: relative;
    z-index: 10050;
}

.dropdown-menu.dashboards-dropdown {
    /* v0.1.47 (rev4, 2026-05-21): viewport-pinned, clamp-width
       mega-panel.  VERIFIED LIVE with Chromium + CDP against the
       real navbar (overflowRight measured in px):

         attempt                         1920    1440    2560
         clamp + Popper anchor (rev3)    +529❌  +497❌   —
         fixed full-width (rev2)         −16✓   −16✓   (3840px wide)
         fixed + clamp, centered (rev4)  −16✓   −16✓   capped@1920✓

       Two facts the live test nailed down:
       1. An explicit WIDTH is required or `column-width` collapses
          to ONE column (an absolutely-positioned dropdown has no
          intrinsic width).  `clamp(13rem, calc(100vw - 2rem),
          120rem)` supplies it (operator's insight).
       2. Popper will NOT shift a near-viewport-wide menu, so a
          toggle-anchored dropdown runs ~500px off the RIGHT
          ("missing parts").  The toggle therefore carries
          `data-bs-display="static"` (no Popper) and the panel is
          `position: fixed`, pinned below the navbar and
          horizontally centred (`left: 50%` + `translateX(-50%)`).
          Centring + the 120rem clamp cap means it spans the
          viewport up to 1920px then caps & centres on ultra-wide.
       `max-height: calc(100vh - 4.5rem)` + `overflow-y: auto`
       give the WHOLE panel one scrollbar if still too tall — no
       per-column clip (the v0.1.43 trap: max-height + columns +
       overflow-y:hidden clips into a single column).
       Test: test_dashboards_dropdown_renders_all_categories. */
    position: fixed !important;
    top: 3.5rem !important;
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%) !important;
    width: clamp(13rem, calc(100vw - 2rem), 120rem) !important;
    min-width: 13rem;
    max-width: calc(100vw - 2rem);
    max-height: calc(100vh - 4.5rem);

    /* overflow (so overflow-x too) MUST stay `auto`, never clipped.  This is a CSS
       multicol container: content that does not fit flows into extra
       columns in the INLINE direction, so clipping that axis hides them
       with no scrollbar.  See test_dashboards_navbar.rs for the measured
       numbers. */
    overflow: auto;

    /* 14rem.  Longest label "Debian Rust Security" (20 chars) fits:
       224px - 2x0.6rem padding - 24px icon leaves ~180px; measured
       wrap-count 0 at 1280/1440/1512/1920px.  Columns OCCUPIED equals
       contentHeight/panelHeight, independent of this value -- so the
       row-height trim below is what makes the content fit, not this.
       Pinned by test_dashboards_navbar.rs. */
    column-width: 14rem;
    column-gap: 0.75rem;
    column-fill: balance;
    z-index: 10060 !important;
}

/* v0.1.36: multi-column grid for the dashboard list.  Uses
   CSS `columns` so items flow automatically — no HTML
   restructure needed, and the <hr> divider after "Overview"
   naturally stays in column 1.  `break-inside: avoid` on
   each <li> prevents Bootstrap dropdown items from being
   split across column boundaries. */

/* Row height dominates: columns-occupied = contentHeight / panelHeight,
   so trimming each of the 166 rows is what makes all 10 categories fit
   on a laptop.  Measured: 10/10 headers visible at >= 1440px wide. */
.dashboards-dropdown .dropdown-item {
    padding: 0.12rem 0.6rem;
    line-height: 1.4;
}

.dashboards-dropdown .dropdown-header {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
}

.dashboards-dropdown .dropdown-divider {
    margin: 0.25rem 0;
}

.dashboards-dropdown > li {
    break-inside: avoid;
}

/* One-off chart / filter dimensions moved out of per-page
   inline style attributes, which the CSP blocks (style-src 'self'). */
.vendor-filter-input {
    max-width: 16rem;
}

.epss-histogram {
    height: 12em;
    gap: 0.4em;
}

.eh-chart {
    height: 220px;
}

.eh-decile {
    height: 200px;
}

.eh-decile-bars {
    height: 170px;
}

.nuclei-severity-bars {
    height: 12em;
    gap: 0.6em;
}

/* Nested dropdown submenus — Bootstrap 5 ships no built-in
   support.  Used by Attacks ▸ "MITRE ATT&CK — <domain>" to reveal
   the per-version choices to the right on hover / keyboard focus. */
.dropdown-submenu {
    position: relative;
}

.dropdown-submenu > .dropdown-menu {
    top: 0;
    left: 100%;
    margin-top: -0.4rem;
    margin-left: 0.1rem;
}

.dropdown-submenu > .dropdown-toggle::after {
    transform: rotate(-90deg);
    margin-left: 0.4em;
}

.dropdown-submenu:hover > .dropdown-menu,
.dropdown-submenu:focus-within > .dropdown-menu {
    display: block;
}

/* htmx loading indicators. htmx would inject these rules as an inline
   style element, which the CSP blocks; base.html sets includeIndicatorStyles
   to false and they live here instead (htmx 2 defaults). */
.htmx-indicator {
    opacity: 0%;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
    opacity: 100%;
    transition: opacity 200ms ease-in;
}

/* pages/emb3d_dashboard.html — histogram bar height, formerly an inline
   style attribute set by the page script. */
.emb3d-bar-track {
    block-size: 0.75rem;
}

/* pages/epss_dashboard.html — decile histogram bar minimum height,
   formerly part of an inline style attribute built by the page script
   (the computed height is set through element.style). */
.epss-decile-bar {
    min-block-size: 1px;
}

/* pages/epss_comparison_dashboard.html — old/new decile bar minimum
   height, formerly part of an inline style attribute built by the page
   script (the computed height is set through element.style). */
.ehc-decile-bar {
    min-height: 1px;
}

/* pages/nuclei_dashboard.html — severity bar minimum height, formerly
   part of an inline style attribute built by the page script (the
   computed height is set through element.style). */
.nuclei-severity-bar {
    min-height: 2px;
}

/* partials/nuclei_recent_rows.html — caps the template-path column
   before text-truncate clips it, formerly an inline style attribute. */
.nuclei-path-cell {
    max-inline-size: 36em;
}
