/* Darstellung der vier Reader-Ansichten (Datensaetze, Kacheln, Raster, Rohdaten). * * Vollstaendig auf die Designsprache aus tokens.css umgestellt: keine rohen * Farbwerte, keine Neomorph-Schatten, Haarlinien statt Tiefe, kleine Radien, * hohe Dichte. Der frueher noetige Sonderblock fuer den hellen Skin ist * entfallen - beide Skins entstehen jetzt allein aus den Tokenwerten. * * Farbe traegt hier ausschliesslich Bedeutung: die Severity-Hexagone und * -Plaketten nutzen die Severity-Tokens, Auswahl nutzt --state-selected, * gemerkte Eintraege nutzen --state-active. */ body { font-family: var(--font); font-size: var(--ui-body-size); line-height: 1.45; font-weight: var(--ui-body-weight); } button, input, select, textarea { font-family: inherit; } .btn, .icon-btn, .select, .input, .tab-btn, .bookmark-btn, .results-btn { font-size: var(--ui-control-size); line-height: 1.35; } .viewer { overflow: hidden; background: var(--bg); } .viewer-scroll, .table-scroll { height: 100%; min-height: 0; overflow: auto; scrollbar-width: thin; scrollbar-color: var(--line-strong) var(--panel-soft); } .viewer-scroll::-webkit-scrollbar, .table-scroll::-webkit-scrollbar { width: 10px; height: 10px; } .viewer-scroll::-webkit-scrollbar-track, .table-scroll::-webkit-scrollbar-track { background: var(--panel-soft); } .viewer-scroll::-webkit-scrollbar-thumb, .table-scroll::-webkit-scrollbar-thumb { border: 2px solid var(--panel-soft); border-radius: var(--radius-pill); background: var(--line-strong); } /* Datensaetze: Trefferliste und Detailleser. */ .records-layout { display: grid; grid-template-columns: clamp(380px, 24vw, 470px) minmax(0, 1fr); height: 100%; min-height: 0; /* Derselbe Innenabstand wie im Kachelraster. Vorher lagen die beiden Spalten randlos auf den Kanten von `.viewer`, dadurch war dessen Rundung verdeckt und die Ansicht wirkte als einzige nicht wie ein Panel. */ padding: var(--panel-inset); background: var(--bg); } .record-list { min-width: 0; min-height: 0; padding: 0; /* Weder Rahmen noch eigene Flaeche: Liste und Leser teilen sich eine durchgehende Panelflaeche, getrennt allein durch die Haarlinie. Eine Trefferliste ist eine Zone, keine Karte. */ border: 0; border-right: var(--border-hair) solid var(--border-strong); background: transparent; overflow-y: auto; scrollbar-gutter: stable; scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; } .record-list::-webkit-scrollbar { width: 6px; } .record-list::-webkit-scrollbar-track { background: transparent; margin: 4px 2px 4px 0; } .record-list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); min-height: 36px; } .record-list::-webkit-scrollbar-thumb:hover { background: var(--text-muted); } .record-card { position: relative; display: grid; grid-template-columns: 28px minmax(0, 1fr); grid-template-rows: auto auto; column-gap: var(--space-2); row-gap: var(--legacy-space-2); width: 100%; min-height: 74px; margin: 0; padding: 11px 14px; border: 0; /* Die Auswahlkante ist immer vorhanden, nur unsichtbar. Sonst versetzt die aktive Zeile ihren Inhalt gegen alle anderen. */ border-left: var(--marker-width) solid transparent; border-bottom: var(--border-hair) solid var(--line); border-radius: 0; color: var(--text); background: var(--panel-raised); box-shadow: none; font: inherit; text-align: left; cursor: pointer; transition: background .12s ease, border-color .12s ease; } .record-card-with-group:first-child > .record-card { border-top-left-radius: 0; border-top-right-radius: 0; } .record-card-with-group:last-child > .record-card { border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom: 0; } .record-card::before { content: ''; grid-column: 1; grid-row: 1 / 3; align-self: center; justify-self: center; width: 24px; height: 27px; clip-path: polygon(50% 0, 94% 24%, 94% 76%, 50% 100%, 6% 76%, 6% 24%); background: var(--muted); } /* Haken im Hexagon. Die Severity-Flaechen sind durchgehend gesaettigt, daher traegt der Haken die dunkle Tinte, die auch auf dem Aktivton verwendet wird. */ .record-card::after { content: ''; grid-column: 1; grid-row: 1 / 3; align-self: center; justify-self: center; width: 7px; height: 4px; border-left: 2px solid var(--sev-on); border-bottom: 2px solid var(--sev-on); transform: translate(-6.5px, 0.5px) rotate(-45deg); pointer-events: none; } .record-card:hover { border-color: var(--line); background: var(--panel-hover); box-shadow: none; transform: none; } .record-card.active, .record-card.active:hover { z-index: 1; border-color: var(--line); border-left-color: var(--state-selected-flat-border); color: var(--state-selected-flat-text); background: var(--state-selected-flat); box-shadow: none; transform: none; } .record-card.severity-critical::before { background: var(--critical); } .record-card.severity-high::before { background: var(--high); } .record-card.severity-medium::before { background: var(--medium); } .record-card.severity-low::before { background: var(--low); } .record-card.severity-info::before { background: var(--info); } .record-card.is-workspace { outline: 2px solid var(--state-active); outline-offset: -2px; } .record-title { grid-column: 2; grid-row: 1; align-self: end; min-width: 0; color: var(--heading); font-size: var(--type-h4-size); font-weight: var(--fw-regular); line-height: 1.28; overflow-wrap: anywhere; } .record-meta { min-width: 0; color: var(--muted); font-size: var(--ui-control-size); font-weight: 400; line-height: 1.3; } .record-meta-line { grid-column: 2; grid-row: 2; display: flex; flex-wrap: wrap; align-items: center; gap: var(--legacy-space-2); min-width: 0; } .record-diagnostic-badge { display: inline-flex; align-items: center; min-height: 20px; padding: 2px var(--legacy-space-2); border: var(--border-hair) solid var(--line-strong); border-radius: var(--legacy-radius-sm); color: var(--text); background: transparent; font-size: var(--ui-badge-size); font-weight: var(--ui-label-weight); line-height: 1.18; letter-spacing: var(--ui-label-tracking); text-transform: uppercase; } .record-card.active .record-title { color: var(--state-selected-flat-text); } .record-card.active .record-meta { color: var(--state-selected-flat-muted); } .record-card.active .record-diagnostic-badge { border-color: var(--state-selected-flat-muted); color: var(--state-selected-flat-text); } .action-pill { display: inline-flex; width: fit-content; margin-left: var(--legacy-space-1); padding: 1px var(--legacy-space-1); border: var(--border-hair) solid var(--success); border-radius: var(--legacy-radius-sm); color: var(--sev-on); background: var(--success); font-size: var(--ui-small-size); line-height: 1.4; vertical-align: middle; } .finding-reader { min-width: 0; min-height: 0; padding: var(--legacy-space-6) 0 var(--space-7); color: var(--text); background: var(--bg); overflow-y: auto; } /* Linksbuendig mit festem Abstand zur Trefferliste statt in der Spalte zentriert - der Text steht damit dort, wo der Blick von der Liste kommt. */ /* Die Begrenzung ist eine Lesebreite, keine Layoutreserve: Fliesstext wie Remediation und Likelihood of Compromise laeuft sonst ueber 1100 px und verliert beim Zeilenwechsel den Anschluss. */ .finding-reader > * { width: min(834px, calc(100% - var(--reader-inset) - var(--legacy-space-6))); margin-left: var(--reader-inset); margin-right: auto; } .reader-nav { margin-bottom: var(--legacy-space-6); color: var(--muted); font-size: var(--ui-control-size); } .reader-nav button { color: var(--text); } .reader-heading { padding-bottom: var(--legacy-space-5); border-color: var(--line); } /* Der Fundname steht als kompakter Detailtitel eine Stufe ueber dem Inhalt. */ .reader-heading h2 { min-width: 0; color: var(--heading); font-size: var(--type-detail-title-size); font-weight: var(--ui-strong-weight); line-height: 1.3; letter-spacing: 0; } .reader-heading h2 .technical-path, .reader-summary h3 .technical-path { display: block; box-sizing: border-box; width: 100%; margin-top: var(--space-1); } .reader-title-hex { width: 26px; height: 30px; } .finding-badges, .mitre-line { gap: var(--legacy-space-2); margin-top: var(--legacy-space-3); font-size: var(--ui-badge-size); } .status-badge, .severity-badge, .mitre-badge, .reader-severity { display: inline-flex; align-items: center; min-height: 22px; height: auto; padding: 3px var(--legacy-space-2); border: var(--border-hair) solid var(--line-strong); border-radius: var(--legacy-radius-sm); color: var(--text); background: transparent; font-size: var(--ui-badge-size); font-weight: var(--ui-label-weight); line-height: 1.18; letter-spacing: var(--ui-label-tracking); text-transform: uppercase; white-space: normal; } /* Die Plakette traegt exakt den Ton des Hexagons derselben Stufe und dieselbe dunkle Tinte. Vorher stand hier --text-on-accent (Weiss) auf einem eigens abgedunkelten Badgeton - zwei Rotwerte fuer dieselbe Aussage. */ .severity-badge.severity-critical, .reader-severity.severity-critical { border-color: var(--critical-badge); color: var(--severity-ink); background: var(--critical-badge); } .severity-badge.severity-high, .reader-severity.severity-high { border-color: var(--high-badge); color: var(--severity-ink); background: var(--high-badge); } .severity-badge.severity-medium, .reader-severity.severity-medium { border-color: var(--medium-badge); color: var(--severity-ink); background: var(--medium-badge); } .severity-badge.severity-low, .reader-severity.severity-low { border-color: var(--low-badge); color: var(--severity-ink); background: var(--low-badge); } .severity-badge.severity-info, .reader-severity.severity-info { border-color: var(--info-badge); color: var(--severity-ink); background: var(--info-badge); } .reader-summary, .review-grid, .all-fields { border-color: var(--line); } /* Die Ueberschrift des Finding-Abschnitts traegt die Pruefbeschreibung der Regel und ist deshalb ein Satz, keine Bereichsmarke - entsprechend gesetzt. */ .reader-summary h3 { color: var(--heading); font-size: var(--type-h3-size); font-weight: var(--ui-strong-weight); line-height: 1.3; letter-spacing: 0; } /* Der Finding-Abschnitt ist die Lesefläche des Berichts. Beschriftungen und Fliesstext stehen deshalb auf der Lesestufe, nicht auf der dichten Werkzeugtypografie der Leisten, Kacheln und Raster. */ .reader-summary h4 { color: var(--heading); font-size: var(--ui-reading-size); font-weight: var(--ui-label-weight); line-height: 1.25; letter-spacing: var(--ui-label-tracking); text-transform: uppercase; } .reader-summary p, .reader-summary .rich-text-value { color: var(--text); font-size: var(--ui-reading-size); font-weight: 400; line-height: 1.55; } .dashboard-grid label, .review-grid > label { color: var(--heading); font-size: var(--ui-control-size); font-weight: var(--ui-label-weight); letter-spacing: var(--ui-label-tracking); } .dashboard-grid input, .dashboard-grid select, .review-grid textarea { border-color: var(--line); background-color: var(--surface-inset); color: var(--text); } /* Kacheln. */ /* Kacheln sind die einzige bewusst erhabene Flaeche der Anwendung: groessere Abstaende, weiche Ecken und ein echter Schattenwurf. Alles uebrige bleibt flach mit Haarlinien. */ .tile-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(0, var(--tile-content-width))); align-items: stretch; align-content: start; justify-content: start; gap: var(--segment-gap); min-height: 100%; padding: var(--segment-inset); border: var(--border-hair) solid var(--segment-border); border-radius: var(--segment-radius); background: var(--segment-surface); } .data-tile { position: relative; display: grid; grid-template-columns: 32px minmax(0, 1fr) 30px; align-items: start; gap: var(--legacy-space-3); min-width: 0; min-height: var(--tile-content-min-height); padding: var(--legacy-space-4); border: var(--border-hair) solid var(--tile-border); border-radius: var(--tile-radius); color: var(--text); background: var(--tile-surface); box-shadow: var(--tile-card-shadow); cursor: pointer; overflow: hidden; text-align: left; transition: background .12s ease, border-color .12s ease; } .data-tile:hover { border-color: var(--tile-border); border-radius: var(--tile-radius); background: var(--tile-surface-hover); box-shadow: var(--tile-card-shadow); outline: 0; } .data-tile:focus-visible { border-color: var(--tile-border); border-radius: var(--tile-radius); background: var(--tile-surface-hover); box-shadow: var(--tile-card-shadow); outline: var(--bw-emphasis) solid var(--focus-ring); outline-offset: calc(-1 * var(--bw-emphasis)); } .data-tile-hex { position: relative; width: 26px; height: 30px; margin-top: 1px; clip-path: polygon(50% 0, 94% 24%, 94% 76%, 50% 100%, 6% 76%, 6% 24%); background: var(--muted); } .data-tile-hex::after { content: ''; position: absolute; left: 2px; top: 12px; width: 7px; height: 4px; border-left: 2px solid var(--sev-on); border-bottom: 2px solid var(--sev-on); transform: rotate(-45deg); pointer-events: none; } .data-tile.severity-critical .data-tile-hex { background: var(--critical); } .data-tile.severity-high .data-tile-hex { background: var(--high); } .data-tile.severity-medium .data-tile-hex { background: var(--medium); } .data-tile.severity-low .data-tile-hex { background: var(--low); } .data-tile.severity-info .data-tile-hex { background: var(--info); } .data-tile-content { min-width: 0; } /* Matrix- und Listentitel teilen die kompakte Inhaltsstufe, damit lange Scannerbezeichnungen nicht die Kachelstruktur verdraengen. */ .data-tile-content h3 { margin: 0; color: var(--heading); font-size: var(--type-h4-size); font-weight: var(--ui-strong-weight); line-height: 1.35; overflow-wrap: normal; word-break: normal; hyphens: manual; } /* Registry- und Policy-Pfade enthalten oft keine Leerzeichen. Nur GPO-Titel duerfen deshalb innerhalb eines langen Pfadsegments umbrechen; normale Findingtitel behalten ihre wortbasierte Typografie. */ .data-tile[data-tool="group-policy"] .data-tile-content h3 { max-width: 100%; white-space: normal; overflow-wrap: anywhere; word-break: normal; } /* Die Groesse gilt dem Outcome-Text ("IOE Found"), der sonst die 14 px des Rumpfes erbte und damit groesser stand als dieselbe Aussage in der Trefferliste. Die Plaketten daneben tragen ihre eigene Groesse. */ .data-tile-badges { display: flex; flex-wrap: wrap; gap: var(--legacy-space-2); margin-top: var(--legacy-space-4); font-size: var(--ui-control-size); } /* Die regulaere Work-Kachel folgt einer festen semantischen Badge-Matrix: Status, Kategorien, Severity/Weight und Score. Graph-Micro-Cards behalten ihre eigene kompakte Flex-Anordnung. */ .tile-grid > .data-tile .data-tile-badges { display: grid; grid-template-columns: max-content max-content; align-items: start; justify-content: start; } .tile-grid > .data-tile .data-tile-badges > * { max-width: 100%; overflow-wrap: normal; word-break: normal; } .tile-grid > .data-tile [data-badge-role="status"], .tile-grid > .data-tile [data-badge-role="category"], .tile-grid > .data-tile [data-badge-role="score"] { grid-column: 1 / -1; justify-self: start; } .data-tile-actions { display: grid; gap: var(--legacy-space-2); align-content: start; } .tile-bookmark-btn, .tile-results-btn { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: var(--border-hair) solid var(--line); border-radius: var(--legacy-radius-sm); color: var(--text); background: var(--panel-raised); font-size: var(--ui-control-size); font-weight: var(--ui-label-weight); cursor: pointer; } /* Merken und geoeffnete Ergebnisse sind Werkzeugzustaende und tragen den Aktivton, nicht den Markenakzent. */ .tile-bookmark-btn[aria-pressed="true"] { border-color: var(--state-active); color: var(--state-active-text); background: var(--state-active); } .tile-results-btn, .tile-results-btn:hover { border-color: var(--border-results-action); color: var(--text-results-action); background: var(--surface-results-action); } .tile-results-btn.is-open, .tile-results-btn.is-open:hover { border-color: var(--result-color); color: var(--button-accent-text); background: var(--result-color); } .data-tile.is-bookmarked { outline: 2px solid var(--state-active); outline-offset: -2px; } /* Raster. `.table-scroll` ist hier zugleich Scrollflaeche und Panelcontainer und traegt deshalb selbst den Innenabstand - anders als `.viewer-scroll`, das seinen Container (`.tile-grid`, `.raw-view`) darin fuehrt. */ .table-scroll { padding: var(--panel-inset); } /* Die Tabelle nimmt die Breite ihres Inhalts an und scrollt waagerecht in `.table-scroll`, statt sich in die Panelbreite zu quetschen. Zuvor erzwang `table { width: 100% }` aus base.css bei rund dreissig Spalten Spalten von wenigen Pixeln. */ .view-table table { width: max-content; min-width: 100%; border-color: var(--line); background: var(--panel); color: var(--text); font-size: var(--type-table-body-size); } .view-table th, .view-table td { min-width: var(--table-col-min-w); max-width: min(440px, 46vw); padding: var(--legacy-space-1) var(--legacy-space-2); border-color: var(--line); white-space: normal; /* `overflow-wrap: anywhere` geht in die Mindestbreite einer Spalte ein und erlaubt damit den Umbruch nach jedem einzelnen Zeichen. `break-word` bricht ueberlange Werte genauso, bestimmt die Spaltenbreite aber nicht. */ overflow-wrap: break-word; } .view-table th { color: var(--muted); background: var(--panel-soft); font-size: var(--type-table-head-size); font-weight: var(--ui-label-weight); line-height: 1.2; letter-spacing: var(--ui-label-tracking); text-transform: uppercase; } .view-table td { color: var(--text); background: var(--panel); font-size: var(--type-table-body-size); font-weight: 400; line-height: 1.35; } /* Zeilenwechsel als ruhige Lesehilfe, in beiden Skins aus denselben Tokens. */ .view-table tbody tr:nth-child(even) td { background: var(--panel-soft); } .view-table tbody tr:hover td { color: var(--heading); background: var(--panel-hover); } .view-table .row-index { width: 54px; min-width: 54px; font-variant-numeric: tabular-nums; } /* Rohdaten. */ .raw-view { min-height: 100%; margin: 0; padding: var(--panel-inset); color: var(--text); background: var(--panel); font-family: var(--font-ui); font-size: var(--type-table-body-size); font-weight: var(--fw-regular); line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; } /* Auf schmalen Fenstern waere der feste Leseabstand verschenkte Breite. */ @media (max-width: 1050px) { :root { --reader-inset: var(--legacy-space-6); } .records-layout { grid-template-columns: minmax(300px, 42%) minmax(0, 1fr); } } @media (max-width: 760px) { :root { --reader-inset: var(--legacy-space-4); } .records-layout { grid-template-columns: 1fr; } .record-list { max-height: 320px; border-right: 0; border-bottom: var(--border-hair) solid var(--line); } .tile-grid { grid-template-columns: 1fr; padding: var(--panel-inset); } } @media (prefers-reduced-motion: reduce) { .data-tile, .record-card { transition: none; } .record-card:hover { transform: none; } }