ktg-plugin-marketplace/shared/playground-design-system/components-tier3-supplement.css
Kjell Tore Guttormsen f1fecf39b8 feat(shared): Tier 3 wave 2 (12 components) + self-hosted fonts
Two changes in one commit because they were prepared together and the
component demos depend on the new self-hosted fonts.css.

Tier 3 wave 2 — 12 new components
---------------------------------
Adds components-tier3-supplement.css (886 lines) and 12 isolated demo
HTML pages under shared/playground-examples/components/:
toxic-flow chain, fleet-overview, kanban Keep/Review/Remove,
maturity-ladder, classify-and-transform, cycle-ribbon,
persistent-antipattern, suppressed-signals, ExpansionCard, ReadMore,
FormProgress, Aspirational-vs-Committed.

Reuses existing tokens — no new CSS custom properties. Honors the
Phase 1 feedback rules: no large pink areas for body text, severity-red
distinct from failure-red, dark mode via existing [data-theme="dark"].

Provenance: components-tier3-supplement.css and the 12 demo bodies were
authored by claude.ai/design (separate Anthropic instance) on 2026-05-03.
This commit only integrates them — path rewrites, font swap, generic
name substitution in fleet-overview demo data, README updates.
base.css from the export was deliberately NOT taken in because it
reverted the inline-message contrast fix from v0.1.

Self-hosted fonts (Inter, JetBrains Mono, Source Serif 4)
---------------------------------------------------------
Replaces all fonts.googleapis.com / fonts.gstatic.com requests with
.woff2 files bundled at shared/playground-design-system/fonts/.

Why:
- No data leaked to Google about end-user IPs and User-Agents.
- GDPR-safe for Norwegian public-sector deployments.
- Works offline / behind air-gapped firewalls.
- Forkers downloading the marketplace get a complete bundle.

All three families are SIL Open Font License 1.1 — license texts
included alongside the woff2 files. Source Serif 4 woff2 generated
locally from the upstream OTF release using
fonttools ttLib.woff2 compress; Inter and JetBrains Mono are
unmodified upstream webfont releases.

Total bundle: 9 woff2 files, ~940 KB. New fonts.css declares all
@font-face rules with font-display: swap. All 6 example HTMLs and 12
new component demos load it via a single relative path.

Verified
--------
- Privacy grep returns empty across plugins/ and shared/
- Google Fonts grep returns empty across shared/*.html
- Smoke test via python -m http.server: HTML + 7 stylesheets +
  Inter-Regular.woff2 all return 200

Doc updates
-----------
- shared/playground-design-system/README.md: file tree updated,
  Quick start snippet shows fonts.css link, "Self-hosted fonts"
  section added
- shared/playground-design-system/fonts/LICENSES.md: combined attribution
- README.md (root): Tier 3 wave 1+2 component list, Privacy-first bullet
- CLAUDE.md (root): tree entry expanded for new components + fonts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 05:08:07 +02:00

886 lines
34 KiB
CSS

/* =============================================================================
components-tier3-supplement.css
Tier 3 supplement — 12 components added after Tier 3 main set.
Pinned rules:
- No big pink fills for text. Use surface bg + colored border + dark body text.
- severity-critical (#A40E26) ≠ state-failed (#7D1A1A). Don't conflate.
- Light + dark theme via existing tokens only.
============================================================================= */
/* =========================================================================
1. Sankey / Toxic-Flow Chain (.tfa-flow)
3-step: Input → Access → Exfil with mitigation shields breaking the chain.
========================================================================= */
.tfa-flow {
display: grid;
grid-template-columns: 1fr auto 1fr auto 1fr;
gap: 0;
align-items: stretch;
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-lg);
padding: var(--space-5);
position: relative;
}
.tfa-flow__verdict {
position: absolute;
top: -12px; right: var(--space-5);
padding: 4px 10px;
font-size: 11px;
font-weight: var(--font-weight-bold);
letter-spacing: 0.06em;
border-radius: var(--radius-pill);
background: var(--color-severity-critical);
color: #fff;
}
.tfa-flow__verdict[data-verdict="ALLOW"] { background: var(--color-state-success); }
.tfa-flow__verdict[data-verdict="WARN"] { background: var(--color-severity-medium); color: #fff; }
.tfa-flow__verdict[data-verdict="BLOCK"] { background: var(--color-severity-critical); }
.tfa-leg {
display: flex; flex-direction: column; gap: 6px;
padding: var(--space-3);
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-left-width: 4px;
border-radius: var(--radius-md);
cursor: pointer;
transition: background var(--duration-fast) var(--ease-default);
text-align: left;
}
.tfa-leg:hover { background: var(--color-bg-soft); }
.tfa-leg:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.tfa-leg[data-severity="medium"] { border-left-color: var(--color-severity-medium); }
.tfa-leg[data-severity="high"] { border-left-color: var(--color-severity-high); }
.tfa-leg[data-severity="critical"] { border-left-color: var(--color-severity-critical); }
.tfa-leg__label {
font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold);
}
.tfa-leg__name { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); }
.tfa-leg__source { font-family: var(--font-family-mono); font-size: 12px; color: var(--color-text-secondary); }
.tfa-leg__status {
margin-top: auto;
font-size: 11px;
font-weight: var(--font-weight-medium);
display: inline-flex; align-items: center; gap: 4px;
}
.tfa-leg__status[data-mit="unmitigated"] { color: var(--color-severity-critical); }
.tfa-leg__status[data-mit="partially_mitigated"] { color: var(--color-severity-medium); }
.tfa-leg__status[data-mit="mitigated"] { color: var(--color-state-success); }
/* Arrow connectors. Width grows with severity */
.tfa-arrow {
display: flex; align-items: center; justify-content: center;
position: relative;
min-width: 56px;
padding: 0 4px;
}
.tfa-arrow__line {
height: 4px;
width: 100%;
background: var(--color-border-moderate);
position: relative;
}
.tfa-arrow[data-severity="medium"] .tfa-arrow__line { background: var(--color-severity-medium); height: 6px; }
.tfa-arrow[data-severity="high"] .tfa-arrow__line { background: var(--color-severity-high); height: 8px; }
.tfa-arrow[data-severity="critical"] .tfa-arrow__line { background: var(--color-severity-critical); height: 10px; }
.tfa-arrow__line::after {
content: ""; position: absolute; right: -1px; top: 50%;
width: 0; height: 0;
border-left: 10px solid currentColor;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
transform: translateY(-50%);
color: inherit;
}
.tfa-arrow[data-severity="medium"] .tfa-arrow__line { color: var(--color-severity-medium); }
.tfa-arrow[data-severity="high"] .tfa-arrow__line { color: var(--color-severity-high); }
.tfa-arrow[data-severity="critical"] .tfa-arrow__line { color: var(--color-severity-critical); }
.tfa-arrow__shield {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 32px; height: 32px;
background: var(--color-state-success);
color: #fff;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
border: 3px solid var(--color-surface);
font-size: 16px;
}
.tfa-arrow--mitigated .tfa-arrow__line {
background: repeating-linear-gradient(90deg, var(--color-state-success) 0 4px, transparent 4px 8px);
}
@media (max-width: 720px) {
.tfa-flow {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto auto auto;
}
.tfa-arrow { min-height: 48px; min-width: auto; }
.tfa-arrow__line { width: 4px; height: 100%; }
.tfa-arrow[data-severity="medium"] .tfa-arrow__line { width: 6px; height: 100%; }
.tfa-arrow[data-severity="high"] .tfa-arrow__line { width: 8px; height: 100%; }
.tfa-arrow[data-severity="critical"] .tfa-arrow__line { width: 10px; height: 100%; }
.tfa-arrow__line::after {
right: 50%; top: auto; bottom: -1px; transform: translateX(50%);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 10px solid currentColor;
border-bottom: none;
}
}
/* =========================================================================
2. Fleet-Overview (.fleet-grid, .fleet-tile)
========================================================================= */
.fleet-toolbar {
display: flex; gap: var(--space-3); flex-wrap: wrap;
align-items: center;
padding: var(--space-3) var(--space-4);
background: var(--color-bg-soft);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
margin-bottom: var(--space-3);
}
.fleet-toolbar__label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold); }
.fleet-toolbar__spacer { flex: 1; }
.fleet-toolbar__count { font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.fleet-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-3);
}
@media (max-width: 980px) { .fleet-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 540px) { .fleet-grid { grid-template-columns: 1fr; } }
.fleet-tile {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-3);
display: grid;
grid-template-rows: auto auto auto auto;
gap: 6px;
cursor: pointer;
transition: border-color var(--duration-fast), transform var(--duration-fast);
}
.fleet-tile:hover { border-color: var(--color-primary-300); transform: translateY(-1px); }
.fleet-tile:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fleet-tile__row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.fleet-tile__name {
font-family: var(--font-family-mono);
font-size: 12px;
color: var(--color-text-primary);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
flex: 1;
}
.fleet-tile__grade {
width: 28px; height: 28px;
display: flex; align-items: center; justify-content: center;
font-weight: var(--font-weight-bold);
font-size: 13px;
border-radius: var(--radius-sm);
color: #fff;
flex-shrink: 0;
}
.fleet-tile__grade[data-grade="A"] { background: var(--color-state-success); }
.fleet-tile__grade[data-grade="B"] { background: #4D8E2F; }
.fleet-tile__grade[data-grade="C"] { background: var(--color-severity-medium); }
.fleet-tile__grade[data-grade="D"] { background: var(--color-severity-high); }
.fleet-tile__grade[data-grade="E"] { background: var(--color-severity-critical); }
.fleet-tile__grade[data-grade="F"] { background: var(--color-severity-extreme); }
.fleet-tile__meter {
height: 6px; border-radius: 3px;
background: var(--color-bg-soft);
overflow: hidden;
position: relative;
}
.fleet-tile__meter-fill { height: 100%; border-radius: 3px; }
.fleet-tile__meter-fill[data-band="1"] { background: var(--color-state-success); }
.fleet-tile__meter-fill[data-band="2"] { background: var(--color-severity-medium); }
.fleet-tile__meter-fill[data-band="3"] { background: var(--color-severity-high); }
.fleet-tile__meter-fill[data-band="4"] { background: var(--color-severity-critical); }
.fleet-tile__chip {
display: inline-flex; align-items: center;
font-size: 11px;
padding: 2px 8px;
border-radius: var(--radius-pill);
background: var(--color-bg-soft);
color: var(--color-text-secondary);
border: 1px solid var(--color-border-subtle);
width: fit-content;
}
.fleet-tile__meta {
display: flex; justify-content: space-between;
font-size: 11px; color: var(--color-text-tertiary);
font-family: var(--font-family-mono);
}
.fleet-tile__trend--better { color: var(--color-state-success); }
.fleet-tile__trend--worse { color: var(--color-severity-critical); }
.fleet-tile__trend--stable { color: var(--color-text-tertiary); }
/* =========================================================================
3. Kanban Keep / Review / Remove (.kanban-board)
========================================================================= */
.kanban-board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
}
@media (max-width: 820px) { .kanban-board { grid-template-columns: 1fr; } }
.kanban-col {
background: var(--color-bg-soft);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-3);
display: flex; flex-direction: column; gap: var(--space-3);
min-height: 320px;
}
.kanban-col__head {
display: flex; align-items: center; justify-content: space-between;
padding-bottom: var(--space-2);
border-bottom: 2px solid var(--color-border-subtle);
}
.kanban-col[data-bucket="keep"] .kanban-col__head { border-bottom-color: var(--color-state-success); }
.kanban-col[data-bucket="review"] .kanban-col__head { border-bottom-color: var(--color-state-warning); }
.kanban-col[data-bucket="remove"] .kanban-col__head { border-bottom-color: var(--color-severity-critical); }
.kanban-col__title { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); }
.kanban-col__count {
font-family: var(--font-family-mono);
font-size: 12px;
background: var(--color-surface);
padding: 2px 8px;
border-radius: var(--radius-pill);
color: var(--color-text-secondary);
border: 1px solid var(--color-border-subtle);
}
.kanban-card {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-3);
cursor: grab;
display: flex; flex-direction: column; gap: 6px;
transition: box-shadow var(--duration-fast);
}
.kanban-card:hover { box-shadow: var(--shadow-md); }
.kanban-card[data-verdict="BLOCK"] { border-color: var(--color-severity-critical); border-left-width: 4px; }
.kanban-card[data-verdict="trusted"] { border-left: 4px solid var(--color-state-success); }
.kanban-card[data-verdict="unknown"] { border-left: 4px solid var(--color-state-warning); }
.kanban-card__name { font-family: var(--font-family-mono); font-size: 13px; color: var(--color-text-primary); word-break: break-all; }
.kanban-card__meta { font-size: 11px; color: var(--color-text-tertiary); }
.kanban-card__reason { font-size: 12px; color: var(--color-text-secondary); }
.kanban-col__empty {
margin: auto;
text-align: center;
color: var(--color-text-tertiary);
font-size: var(--font-size-sm);
padding: var(--space-4);
}
.kanban-col__empty button { margin-top: var(--space-2); }
.kanban-actions { display: flex; gap: 4px; margin-top: 4px; }
.kanban-actions button {
flex: 1; font-size: 11px; padding: 4px 6px;
background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-sm); color: var(--color-text-secondary);
cursor: pointer; font-family: inherit;
}
.kanban-actions button:hover { background: var(--color-surface-sunken); color: var(--color-text-primary); }
/* =========================================================================
4. Maturity-Ladder (.mat-ladder)
========================================================================= */
.mat-ladder {
display: flex; flex-direction: column;
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-4);
gap: 0;
}
.mat-step {
display: grid;
grid-template-columns: 56px 1fr;
gap: var(--space-4);
padding: var(--space-3) 0;
position: relative;
}
.mat-step + .mat-step { border-top: 1px dashed var(--color-border-subtle); }
.mat-step__icon {
width: 44px; height: 44px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
background: var(--color-surface);
border: 2px solid var(--color-border-moderate);
color: var(--color-text-tertiary);
font-weight: var(--font-weight-semibold);
font-size: 15px;
position: relative;
z-index: 1;
}
.mat-step[data-state="completed"] .mat-step__icon {
background: var(--color-state-success);
border-color: var(--color-state-success);
color: #fff;
}
.mat-step[data-state="current"] .mat-step__icon {
border-color: var(--color-primary-500);
color: var(--color-primary-700);
background: var(--color-surface);
}
/* progress ring around current step */
.mat-step__ring {
position: absolute;
inset: -4px;
border-radius: 50%;
pointer-events: none;
}
.mat-step__ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.mat-step__ring circle { fill: none; stroke-width: 3; }
.mat-step__ring .ring-bg { stroke: var(--color-border-subtle); }
.mat-step__ring .ring-fill { stroke: var(--color-primary-500); }
.mat-step__name {
font-size: var(--font-size-md);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
display: flex; align-items: center; gap: 8px;
}
.mat-step[data-state="completed"] .mat-step__name { color: var(--color-text-secondary); }
.mat-step[data-state="future"] .mat-step__name { color: var(--color-text-tertiary); }
.mat-step__pill {
font-size: 11px; padding: 2px 8px; border-radius: var(--radius-pill);
text-transform: uppercase; letter-spacing: 0.06em; font-weight: var(--font-weight-semibold);
}
.mat-step__pill--current { background: var(--color-primary-100); color: var(--color-primary-700); }
.mat-step__pill--complete { background: transparent; color: var(--color-state-success); border: 1px solid currentColor; }
.mat-step__desc {
font-size: var(--font-size-sm);
color: var(--color-text-secondary);
margin-top: 2px;
max-width: 60ch;
}
.mat-step__progress {
margin-top: 6px;
display: flex; align-items: center; gap: 8px;
font-size: 12px; color: var(--color-text-tertiary);
}
.mat-step__progress-bar {
flex: 1; height: 4px;
background: var(--color-bg-soft);
border-radius: 2px;
overflow: hidden;
max-width: 200px;
}
.mat-step__progress-fill { height: 100%; background: var(--color-primary-500); border-radius: 2px; }
/* =========================================================================
5. Classify-and-Transform / 5-Bucket-Sorter (.cls-sorter)
========================================================================= */
.cls-sorter { display: flex; flex-direction: column; gap: var(--space-4); }
.cls-input {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-3);
}
.cls-input textarea {
width: 100%; min-height: 100px;
font-family: var(--font-family-sans);
font-size: var(--font-size-sm);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-sm);
padding: var(--space-2) var(--space-3);
background: var(--color-bg);
color: var(--color-text-primary);
resize: vertical;
}
.cls-input textarea:focus { outline: none; box-shadow: var(--shadow-focus); border-color: var(--color-border-focus); }
.cls-buckets {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: var(--space-3);
}
@media (max-width: 1100px) { .cls-buckets { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 720px) { .cls-buckets { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .cls-buckets { grid-template-columns: 1fr; } }
.cls-bucket {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-top-width: 4px;
border-radius: var(--radius-md);
padding: var(--space-3);
display: flex; flex-direction: column; gap: var(--space-2);
min-height: 200px;
}
.cls-bucket[data-egnethet="lav"] { border-top-color: var(--color-text-tertiary); }
.cls-bucket[data-egnethet="medium"] { border-top-color: var(--color-state-info); }
.cls-bucket[data-egnethet="hoy"] { border-top-color: var(--color-state-success); }
.cls-bucket__head {
display: flex; flex-direction: column; gap: 2px;
padding-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border-subtle);
}
.cls-bucket__title { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); }
.cls-bucket__egnethet {
font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold);
}
.cls-bucket[data-egnethet="lav"] .cls-bucket__egnethet { color: var(--color-text-tertiary); }
.cls-bucket[data-egnethet="medium"] .cls-bucket__egnethet { color: var(--color-state-info); }
.cls-bucket[data-egnethet="hoy"] .cls-bucket__egnethet { color: var(--color-state-success); }
.cls-item {
background: var(--color-bg-soft);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-sm);
padding: 6px 8px;
font-size: 12px;
color: var(--color-text-primary);
cursor: grab;
display: flex; flex-direction: column; gap: 2px;
}
.cls-item__action {
font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
color: var(--color-text-tertiary); font-weight: var(--font-weight-medium);
}
.cls-bucket__action {
margin-top: auto;
padding-top: var(--space-2);
border-top: 1px dashed var(--color-border-subtle);
}
.cls-bucket__empty {
font-size: 12px; color: var(--color-text-tertiary);
font-style: italic;
text-align: center;
padding: var(--space-3);
}
/* =========================================================================
6. Cycle Position Ribbon (.cycle-ribbon)
========================================================================= */
.cycle-ribbon {
position: relative;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border-subtle);
padding: 8px var(--space-5);
display: flex; align-items: center; gap: var(--space-4);
font-size: 13px;
cursor: pointer;
overflow: hidden;
}
.cycle-ribbon::before {
content: ""; position: absolute; inset: 0;
background: var(--color-state-info);
opacity: 0.06;
width: var(--cycle-progress, 0%);
transition: width var(--duration-normal);
}
.cycle-ribbon[data-phase="planning"] { border-bottom-color: var(--color-state-info); }
.cycle-ribbon[data-phase="planning"]::before { background: var(--color-state-info); }
.cycle-ribbon[data-phase="execution"] { border-bottom-color: var(--color-state-success); }
.cycle-ribbon[data-phase="execution"]::before { background: var(--color-state-success); }
.cycle-ribbon[data-phase="retrospective_prep"] { border-bottom-color: var(--color-severity-medium); }
.cycle-ribbon[data-phase="retrospective_prep"]::before { background: var(--color-severity-medium); }
.cycle-ribbon > * { position: relative; z-index: 1; }
.cycle-ribbon__id { font-family: var(--font-family-mono); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); white-space: nowrap; flex-shrink: 0; }
.cycle-ribbon__week { color: var(--color-text-secondary); font-family: var(--font-family-mono); white-space: nowrap; flex-shrink: 0; }
.cycle-ribbon__phase {
font-size: 11px; padding: 2px 8px;
border-radius: var(--radius-pill);
text-transform: uppercase; letter-spacing: 0.06em;
font-weight: var(--font-weight-semibold);
white-space: nowrap; flex-shrink: 0;
}
.cycle-ribbon[data-phase="planning"] .cycle-ribbon__phase { background: var(--color-primary-100); color: var(--color-primary-700); }
.cycle-ribbon[data-phase="execution"] .cycle-ribbon__phase { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
.cycle-ribbon[data-phase="retrospective_prep"] .cycle-ribbon__phase { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
.cycle-ribbon__msg { color: var(--color-text-secondary); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cycle-ribbon__chev { color: var(--color-text-tertiary); transition: transform var(--duration-fast); }
.cycle-ribbon[aria-expanded="true"] .cycle-ribbon__chev { transform: rotate(180deg); }
.cycle-ribbon__panel {
background: var(--color-bg-soft);
border-bottom: 1px solid var(--color-border-subtle);
padding: var(--space-4) var(--space-5);
display: none;
font-size: var(--font-size-sm);
}
.cycle-ribbon__panel[data-open="true"] { display: block; }
@media (max-width: 720px) {
.cycle-ribbon__msg { display: none; }
}
/* =========================================================================
7. Persistent-Antipattern Badge (.pap-badge)
========================================================================= */
.pap-badge {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 10px;
background: var(--color-surface);
border: 1px solid var(--color-severity-critical);
border-radius: var(--radius-pill);
font-size: 12px;
font-weight: var(--font-weight-medium);
color: var(--color-severity-critical);
cursor: pointer;
position: relative;
}
.pap-badge::before {
content: "";
width: 8px; height: 8px;
border-radius: 50%;
background: var(--color-severity-critical);
animation: pap-pulse 2.4s var(--ease-default) infinite;
}
@keyframes pap-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.45; transform: scale(0.7); }
}
@media (prefers-reduced-motion: reduce) {
.pap-badge::before { animation: none; opacity: 1; }
}
.pap-badge__count { font-family: var(--font-family-mono); font-weight: var(--font-weight-semibold); }
.pap-detail {
margin-top: var(--space-3);
background: var(--color-surface);
border: 1px solid var(--color-severity-critical);
border-left-width: 4px;
border-radius: var(--radius-md);
padding: var(--space-4);
display: none;
}
.pap-detail[data-open="true"] { display: block; }
.pap-detail h4 { margin: 0 0 4px; color: var(--color-severity-critical); font-size: var(--font-size-md); }
.pap-detail__cycles { display: flex; gap: 4px; flex-wrap: wrap; margin: var(--space-2) 0; }
.pap-detail__cycle {
font-family: var(--font-family-mono);
font-size: 11px;
padding: 2px 6px;
background: var(--color-bg-soft);
border-radius: var(--radius-sm);
color: var(--color-text-secondary);
}
.pap-detail__rec {
background: var(--color-bg-soft);
border-radius: var(--radius-sm);
padding: var(--space-2) var(--space-3);
margin-top: var(--space-2);
font-size: var(--font-size-sm);
color: var(--color-text-primary);
}
/* one-shot variant */
.pap-badge--oneshot {
border-style: dashed;
border-color: var(--color-severity-medium);
color: var(--color-severity-medium);
}
.pap-badge--oneshot::before { display: none; }
/* =========================================================================
8. Suppressed-Signals Panel (.suppressed)
========================================================================= */
.suppressed {
background: var(--color-bg-soft);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
overflow: hidden;
}
.suppressed__head {
width: 100%;
display: flex; align-items: center; gap: var(--space-3);
padding: var(--space-3) var(--space-4);
background: transparent;
border: 0;
cursor: pointer;
font-family: inherit;
text-align: left;
color: var(--color-text-secondary);
}
.suppressed__head:hover { background: var(--color-surface-sunken); color: var(--color-text-primary); }
.suppressed__head:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.suppressed__chev { color: var(--color-text-tertiary); transition: transform var(--duration-fast); }
.suppressed[aria-expanded="true"] .suppressed__chev { transform: rotate(90deg); }
.suppressed__label { font-size: var(--font-size-sm); }
.suppressed__count {
font-family: var(--font-family-mono);
font-size: 12px;
background: var(--color-surface);
padding: 2px 8px;
border-radius: var(--radius-pill);
color: var(--color-text-secondary);
border: 1px solid var(--color-border-subtle);
margin-left: auto;
}
.suppressed__body {
display: none;
padding: 0 var(--space-4) var(--space-4);
}
.suppressed[aria-expanded="true"] .suppressed__body { display: block; }
.suppressed-group {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-sm);
padding: var(--space-3);
}
.suppressed-group + .suppressed-group { margin-top: var(--space-2); }
.suppressed-group__head {
display: flex; justify-content: space-between; align-items: center; gap: 8px;
margin-bottom: 4px;
}
.suppressed-group__reason { font-family: var(--font-family-mono); font-size: 12px; color: var(--color-text-tertiary); }
.suppressed-group__count { font-size: 11px; color: var(--color-text-tertiary); }
.suppressed-group__desc { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 0 0 6px; }
.suppressed-group__examples {
display: flex; gap: 4px; flex-wrap: wrap;
}
.suppressed-group__example {
font-family: var(--font-family-mono);
font-size: 11px;
background: var(--color-bg-soft);
padding: 2px 6px;
border-radius: var(--radius-sm);
color: var(--color-text-secondary);
}
/* =========================================================================
9. ExpansionCard (Aksel) (.expansion)
========================================================================= */
.expansion {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
overflow: hidden;
}
.expansion + .expansion { margin-top: var(--space-2); }
.expansion__head {
width: 100%;
display: flex; align-items: flex-start; gap: var(--space-3);
padding: var(--space-3) var(--space-4);
background: transparent;
border: 0;
cursor: pointer;
font-family: inherit;
text-align: left;
}
.expansion__head:hover { background: var(--color-bg-soft); }
.expansion__head:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.expansion__title { flex: 1; }
.expansion__title-main { font-size: var(--font-size-md); color: var(--color-text-primary); font-weight: var(--font-weight-medium); }
.expansion__title-sub { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin-top: 2px; }
.expansion__chev {
color: var(--color-text-tertiary);
transition: transform var(--duration-normal) var(--ease-default);
flex-shrink: 0;
margin-top: 2px;
}
.expansion[aria-expanded="true"] .expansion__chev { transform: rotate(180deg); }
.expansion__body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows var(--duration-normal) var(--ease-default);
}
.expansion[aria-expanded="true"] .expansion__body { grid-template-rows: 1fr; }
.expansion__body-inner { overflow: hidden; }
.expansion__body-inner > div {
padding: 0 var(--space-4) var(--space-4);
border-top: 1px solid var(--color-border-subtle);
padding-top: var(--space-3);
margin-top: -1px;
}
@media (prefers-reduced-motion: reduce) {
.expansion__body { transition: none; }
}
/* =========================================================================
10. ReadMore (Aksel) (.read-more)
========================================================================= */
.read-more {
display: inline;
}
.read-more__trigger {
display: inline-flex; align-items: center; gap: 4px;
background: transparent;
border: 0;
color: var(--color-text-link);
font-family: inherit;
font-size: inherit;
font-weight: var(--font-weight-medium);
cursor: pointer;
padding: 0;
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 3px;
}
.read-more__trigger:hover { color: var(--color-text-link-hover); }
.read-more__trigger:focus-visible { outline: none; box-shadow: var(--shadow-focus); border-radius: 2px; }
.read-more__chev { transition: transform var(--duration-fast); }
.read-more[aria-expanded="true"] .read-more__chev { transform: rotate(180deg); }
.read-more__body { display: none; margin-top: var(--space-2); }
.read-more[aria-expanded="true"] .read-more__body { display: block; }
/* =========================================================================
11. FormProgress (Aksel multi-step skjema) (.form-progress)
========================================================================= */
.form-progress {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-4);
display: flex; flex-direction: column; gap: var(--space-3);
width: 280px;
position: sticky;
top: var(--space-4);
}
.form-progress__autosave {
display: flex; align-items: center; gap: 6px;
font-size: 12px;
color: var(--color-text-tertiary);
padding-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border-subtle);
}
.form-progress__autosave-dot {
width: 6px; height: 6px;
border-radius: 50%;
background: var(--color-state-success);
}
.form-progress__steps { display: flex; flex-direction: column; gap: 2px; }
.fp-step {
display: grid;
grid-template-columns: 28px 1fr;
gap: var(--space-2);
align-items: start;
padding: 8px;
border-radius: var(--radius-sm);
text-align: left;
background: transparent;
border: 0;
cursor: pointer;
font-family: inherit;
position: relative;
}
.fp-step:hover { background: var(--color-bg-soft); }
.fp-step:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fp-step[disabled] { cursor: not-allowed; opacity: 0.5; }
.fp-step__num {
width: 22px; height: 22px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
background: var(--color-surface);
border: 1.5px solid var(--color-border-moderate);
color: var(--color-text-tertiary);
font-size: 11px;
font-weight: var(--font-weight-semibold);
}
.fp-step[data-state="done"] .fp-step__num {
background: var(--color-state-success);
border-color: var(--color-state-success);
color: #fff;
}
.fp-step[data-state="in-progress"] .fp-step__num {
border-color: var(--color-primary-500);
color: var(--color-primary-700);
font-weight: var(--font-weight-bold);
}
.fp-step__name { font-size: var(--font-size-sm); color: var(--color-text-primary); font-weight: var(--font-weight-medium); }
.fp-step[data-state="done"] .fp-step__name { color: var(--color-text-secondary); font-weight: var(--font-weight-regular); }
.fp-step[data-state="in-progress"] .fp-step__name { color: var(--color-primary-700); font-weight: var(--font-weight-semibold); }
.fp-step__progress {
margin-top: 4px;
font-size: 11px;
color: var(--color-text-tertiary);
display: flex; align-items: center; gap: 6px;
}
.fp-step__bar {
flex: 1; height: 3px;
background: var(--color-bg-soft);
border-radius: 2px; overflow: hidden;
max-width: 80px;
}
.fp-step__bar-fill { height: 100%; background: var(--color-primary-500); }
.form-progress__remaining {
padding-top: var(--space-2);
border-top: 1px solid var(--color-border-subtle);
font-size: 12px; color: var(--color-text-tertiary);
display: flex; justify-content: space-between;
}
/* =========================================================================
12. Aspirational vs Committed Visual (.okr-mode)
Modifier added to OKR Objective cards
========================================================================= */
.okr-mode {
position: relative;
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-4);
}
.okr-mode__gauge {
position: relative;
width: 88px; height: 88px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.okr-mode__gauge svg { position: absolute; inset: 0; transform: rotate(-90deg); width: 100%; height: 100%; }
.okr-mode__gauge circle.gauge-bg { fill: none; stroke: var(--color-border-subtle); stroke-width: 6; }
.okr-mode__gauge circle.gauge-fill { fill: none; stroke: var(--color-state-success); stroke-width: 6; stroke-linecap: round; }
.okr-mode__gauge .gauge-value { font-family: var(--font-family-mono); font-size: 22px; font-weight: var(--font-weight-bold); color: var(--color-text-primary); position: relative; z-index: 1; }
/* aspirational variant — dashed stroke */
.okr-mode[data-mode="aspirational"] .okr-mode__gauge circle.gauge-fill {
stroke: var(--color-scope-okr);
stroke-dasharray: 6 4;
}
.okr-mode__badge {
position: absolute;
top: var(--space-2); right: var(--space-2);
font-size: 10px; font-weight: var(--font-weight-bold); letter-spacing: 0.08em;
padding: 2px 8px;
border-radius: var(--radius-sm);
}
.okr-mode[data-mode="aspirational"] .okr-mode__badge {
background: transparent;
color: var(--color-scope-okr);
border: 1px dashed var(--color-scope-okr);
}
.okr-mode[data-mode="committed"] .okr-mode__badge {
background: var(--color-primary-700);
color: #fff;
}
.okr-mode__row { display: flex; gap: var(--space-4); align-items: center; }
.okr-mode__objective { font-size: var(--font-size-md); color: var(--color-text-primary); flex: 1; }
.okr-mode__hint { font-size: 12px; color: var(--color-text-tertiary); margin-top: 4px; }