File size: 3,250 Bytes
6bef416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from __future__ import annotations

from collections.abc import Iterable

import streamlit as st

CSS = """
<style>
:root {
  --app-bg: #0b1020;
  --panel: #111827;
  --border: rgba(148, 163, 184, 0.28);
  --muted: #94a3b8;
}

.stApp {
  background: linear-gradient(180deg, #08111f 0%, #0b1020 45%, #0f172a 100%);
}

.block-container {
  max-width: 1480px;
  padding-top: 1.25rem;
  padding-bottom: 3rem;
}

[data-testid="stSidebar"] {
  background: #0b1220;
  border-right: 1px solid var(--border);
}

h1, h2, h3 {
  letter-spacing: -0.02em;
}

[data-testid="stMetric"] {
  background: rgba(15, 23, 42, 0.72);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px 14px 12px 14px;
}

[data-testid="stDataFrame"] {
  border-radius: 12px;
}

.small-note {
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.5;
}
</style>
"""


def inject_css() -> None:
    """Inject static CSS only; do not interpolate user or dataset values into this markup."""
    st.markdown(CSS, unsafe_allow_html=True)


def badge(text: str, kind: str = "info") -> str:
    """Return a plain-text badge label for native Streamlit rendering."""
    prefix = {
        "good": "✅",
        "warn": "⚠️",
        "bad": "🚨",
        "info": "ℹ️",
    }.get(kind, "•")
    return f"{prefix} {text}"


def hero(title: str, subtitle: str, badges: Iterable[str] | None = None) -> None:
    st.title(title)
    st.markdown(subtitle)
    if badges:
        st.caption("  ·  ".join(str(item) for item in badges))
    st.divider()


def decision_strip(items: list[tuple[str, str, str]]) -> None:
    """Render the decision brief with native Streamlit elements.

    This keeps the decision brief stable across Streamlit versions.
    """
    st.subheader("Decision brief")
    cols = st.columns(len(items))
    for col, (label, value, hint) in zip(cols, items, strict=False):
        with col:
            with st.container(border=True):
                st.caption(label)
                st.markdown(f"**{value}**")
                st.caption(hint)


def metric_card(label: str, value: str, caption: str, status: str = "info") -> None:
    st.metric(label=label, value=value)
    st.caption(f"{status.title()} · {caption}")


def status_kind(status: str) -> str:
    s = status.lower()
    if any(x in s for x in ["stable", "good", "healthy", "pass"]):
        return "good"
    if any(x in s for x in ["risk", "high", "bad", "fail"]):
        return "bad"
    if any(x in s for x in ["watch", "review", "warn"]):
        return "warn"
    return "info"


def section_title(title: str, subtitle: str = "", chip: str = "") -> None:
    label = f" — {chip}" if chip else ""
    st.markdown(f"## {title}{label}")
    if subtitle:
        st.caption(subtitle)


def callout(kind: str, title: str, body: str) -> None:
    text = f"**{title}**\n\n{body}"
    if kind in {"good", "success", "pass"}:
        st.success(text)
    elif kind in {"warn", "warning", "review"}:
        st.warning(text)
    elif kind in {"bad", "error", "risk", "fail"}:
        st.error(text)
    else:
        st.info(text)


def trace_box(title: str, body: str) -> None:
    with st.container(border=True):
        st.markdown(f"**{title}**")
        st.write(body)