stem-bio-ai / stem_ai /render_html_styles.py
Flamehaven Initiative
docs: sync hf packet surfaces to 5p and 7p
e9858b7
Raw
History Blame
16.2 kB
"""CSS and JS template strings for STEM BIO-AI interactive HTML report."""
from __future__ import annotations
from .render_html_components import _C
def build_css(tc: str) -> str:
n, t = _C["navy"], _C["teal"]
g, a, r = _C["green"], _C["amber"], _C["red"]
p, lg, mg, dg, w = _C["purple"], _C["lgray"], _C["mgray"], _C["dgray"], _C["white"]
return f"""
:root{{
--navy:{n};
--teal:{t};
--green:{g};
--amber:{a};
--red:{r};
--purple:{p};
--slate:{_C["slate"]};
--bg:{lg};
--panel:{w};
--line:{mg};
--muted:{dg};
--tier:{tc};
--shadow-soft:0 10px 28px rgba(13,31,60,.08);
--shadow-strong:0 20px 48px rgba(13,31,60,.16);
--radius:18px;
}}
*,*::before,*::after{{box-sizing:border-box;margin:0;padding:0}}
html{{scroll-behavior:smooth}}
body{{
font-family:"Segoe UI Variable Text","Segoe UI",ui-sans-serif,sans-serif;
background:
radial-gradient(circle at top left, rgba(26,111,168,.09), transparent 28%),
linear-gradient(180deg, #f5f8fc 0%, {lg} 42%, #eef3f9 100%);
color:{n};
font-size:14px;
line-height:1.6;
}}
h1,h2,h3,.stage-name,.metric-value,.tier{{
font-family:"Iowan Old Style","Palatino Linotype","Book Antiqua",Georgia,serif;
}}
.nav{{
position:sticky;top:0;z-index:60;
background:rgba(255,255,255,.86);
backdrop-filter:blur(14px);
display:flex;justify-content:space-between;align-items:center;gap:16px;
border-bottom:1px solid rgba(13,31,60,.08);
box-shadow:0 8px 24px rgba(13,31,60,.06);
padding-right:16px;
}}
.nav-links{{display:flex;gap:4px;overflow-x:auto;flex:1;min-width:0}}
.nav-brand{{display:grid;justify-items:end;flex-shrink:0;white-space:nowrap;padding-left:12px;border-left:1px solid rgba(13,31,60,.08)}}
.nav-brand strong{{font-size:11px;letter-spacing:.08em;color:{n}}}
.nav-brand span{{font-size:10px;color:{dg};font-weight:700}}
.nav-link{{
padding:14px 18px;color:{dg};font-size:12px;font-weight:700;
text-decoration:none;transition:color .2s,background .2s,border-color .2s;
border-bottom:3px solid transparent;white-space:nowrap;
}}
.nav-link:hover{{color:{n};background:rgba(13,31,60,.04);transform:translateY(-1px)}}
.nav-link.active{{color:{n};border-bottom-color:{t}}}
.hero{{
background:
radial-gradient(circle at top right, rgba(255,255,255,.35), transparent 28%),
radial-gradient(circle at top left, rgba(255,255,255,.26), transparent 26%),
linear-gradient(135deg, #2B547B 0%, #3C6C96 46%, #6E9FBE 100%);
color:#fff;
padding:42px 48px 46px;
display:grid;
grid-template-columns:minmax(180px,220px) 1fr;
gap:30px;
align-items:center;
}}
.hero-left{{display:flex;justify-content:center}}
.hero-right{{display:flex;flex-direction:column;gap:10px}}
.hero h1{{font-size:34px;font-weight:700;line-height:1.1;letter-spacing:-.02em}}
.hero-link{{color:#fff;text-decoration:none;border-bottom:1px solid transparent}}
.hero-link:hover{{border-bottom-color:rgba(255,255,255,.72)}}
.eyebrow{{font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:rgba(13,31,60,.62)}}
.hero .eyebrow{{color:rgba(255,255,255,.82)}}
.hero-gauge-card{{
background:rgba(255,255,255,.96);
border:1px solid rgba(255,255,255,.45);
border-radius:22px;
padding:18px 20px 14px;
box-shadow:0 18px 42px rgba(8,22,38,.18);
}}
.hero-meta{{display:flex;gap:10px;flex-wrap:wrap;align-items:center}}
.tier{{
display:inline-block;background:{tc};color:#fff;font-size:13px;font-weight:700;
padding:6px 16px;border-radius:999px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.12);
}}
.hero-chip{{
display:inline-flex;align-items:center;padding:6px 12px;border-radius:999px;
border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.08);
color:rgba(255,255,255,.84);font-size:11px;font-weight:700;
}}
.lede{{font-size:14px;color:rgba(255,255,255,.82);max-width:720px}}
.content{{max-width:1180px;margin:0 auto;padding:34px 24px 54px}}
section{{margin-bottom:34px;scroll-margin-top:72px}}
.s-title{{font-size:24px;font-weight:700;color:{n};margin-bottom:16px;display:flex;gap:8px;align-items:center;flex-wrap:wrap}}
.subhead{{font-size:22px;font-weight:700;color:{n};margin:2px 0 10px}}
.panel{{
background:{w};
border:1px solid rgba(13,31,60,.07);
border-radius:var(--radius);
padding:24px;
box-shadow:var(--shadow-soft);
}}
.panel:hover,
.metric-card:hover,
.memo-card:hover,
.stage-card:hover{{
box-shadow:var(--shadow-strong);
transform:translateY(-2px);
}}
.memo-grid{{display:grid;grid-template-columns:repeat(12,1fr);gap:18px}}
.integrity-stack,.airi-stack{{display:grid;grid-template-columns:1fr;gap:18px}}
.memo-card,.stage-card{{
background:{w};
border:1px solid rgba(13,31,60,.08);
border-radius:var(--radius);
padding:20px 20px 18px;
box-shadow:var(--shadow-soft);
}}
.memo-primary{{background:linear-gradient(180deg,#ffffff 0%,#f8fbff 100%)}}
.memo-card:nth-child(1){{grid-column:span 6}}
.memo-card:nth-child(2),.memo-card:nth-child(3),.memo-card:nth-child(4){{grid-column:span 6}}
.memo-text{{font-size:13px;color:{n}}}
.memo-note,.muted-note,.muted{{font-size:11px;color:{dg};line-height:1.5}}
.memo-list{{padding-left:18px;display:grid;gap:8px;font-size:12px;color:{n}}}
.pill-row{{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}}
.pill{{display:inline-flex;align-items:center;padding:5px 11px;border-radius:999px;background:{lg};border:1px solid {mg};font-size:11px;font-weight:700;color:{n}}}
.metric-grid{{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:14px;margin-bottom:18px}}
.metric-card{{
background:{w};border:1px solid rgba(13,31,60,.08);border-radius:16px;
padding:18px 16px;box-shadow:var(--shadow-soft);text-align:center
}}
.metric-value{{font-size:32px;font-weight:700;line-height:1;margin-bottom:6px}}
.metric-label{{font-size:11px;color:{dg};font-weight:800;text-transform:uppercase;letter-spacing:.08em}}
.alert-t0{{background:{r};color:#fff;padding:15px 18px;border-radius:14px;margin-bottom:18px;font-weight:700;box-shadow:var(--shadow-soft)}}
.decision-panel{{padding:18px}}
.formula-banner{{
margin-bottom:16px;padding:12px 14px;border-radius:14px;background:#eef5fb;
border:1px solid #d7e5f4;font-size:12px;color:{n};font-family:"Consolas","SFMono-Regular",monospace
}}
.config-pattern{{
display:grid;
grid-template-columns:minmax(0,1.2fr) minmax(0,1.8fr);
gap:18px;
margin-bottom:18px;
padding:18px;
border-radius:16px;
background:linear-gradient(180deg,#fbfdff 0%,#f2f7fc 100%);
border:1px solid #dbe6f1;
}}
.config-copy{{display:grid;gap:10px;align-content:start}}
.config-grid{{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}}
.config-card{{
background:{w};
border:1px solid rgba(13,31,60,.08);
border-radius:14px;
padding:12px;
box-shadow:var(--shadow-soft);
}}
.config-label{{
font-size:10px;
font-weight:800;
letter-spacing:.12em;
text-transform:uppercase;
color:{dg};
margin-bottom:8px;
}}
.config-code{{
font-family:"Consolas","SFMono-Regular",monospace;
font-size:11px;
line-height:1.55;
white-space:pre-wrap;
color:{n};
background:#f7fafc;
border:1px solid #e3ebf3;
border-radius:10px;
padding:10px 11px;
overflow:auto;
}}
.stage-deck{{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}}
.stage-card-top{{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;margin-bottom:10px}}
.stage-name{{font-size:18px;font-weight:700;color:{n};line-height:1.2}}
.stage-value{{font-size:26px;font-weight:700;line-height:1}}
.stage-summary{{font-size:12px;color:{dg};margin:12px 0 10px}}
.focus-list{{list-style:none;display:grid;gap:10px}}
.focus-line{{padding-top:10px;border-top:1px solid rgba(13,31,60,.08)}}
.focus-top{{display:flex;justify-content:space-between;gap:12px;align-items:baseline;margin-bottom:4px}}
.focus-key{{font-size:12px;font-weight:700;color:{n}}}
.focus-score{{font-size:12px;font-weight:800;color:{n}}}
.focus-detail{{font-size:11px;color:{dg};line-height:1.55}}
.focus-detector{{display:inline-flex;margin-right:8px;padding:2px 8px;border-radius:999px;background:#f2f6fa;border:1px solid {mg};font-family:"Consolas","SFMono-Regular",monospace;color:{p}}}
.card-grid{{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px}}
.card{{background:{w};border-radius:14px;box-shadow:var(--shadow-soft);cursor:pointer;transition:box-shadow .2s,transform .15s;overflow:hidden;outline:none;border:1px solid rgba(13,31,60,.06)}}
.card:hover{{box-shadow:var(--shadow-strong);transform:translateY(-2px)}}
.card:focus{{box-shadow:0 0 0 3px rgba(26,111,168,.2)}}
.card.expanded{{box-shadow:var(--shadow-strong)}}
.card.expanded .caret{{transform:rotate(180deg)}}
.caret{{display:block;transition:transform .2s}}
.faq-block{{display:grid;gap:10px;margin-top:16px}}
.faq-item{{border-top:1px solid rgba(13,31,60,.08);padding-top:10px}}
.faq-item summary{{cursor:pointer;font-size:12px;font-weight:800;color:{n};list-style:none}}
.faq-item summary::-webkit-details-marker{{display:none}}
.faq-item p{{margin-top:8px;font-size:12px;color:{dg}}}
.tip-icon{{display:inline-flex;align-items:center;justify-content:center;width:17px;height:17px;border-radius:50%;background:{mg};color:{dg};font-size:10px;font-weight:800;cursor:help;position:relative;flex-shrink:0;vertical-align:middle}}
[data-tooltip]{{position:relative}}
[data-tooltip]::after{{
content:attr(data-tooltip);position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);
background:{n};color:#fff;padding:9px 12px;border-radius:10px;font-size:11px;line-height:1.45;white-space:normal;
max-width:280px;width:max-content;opacity:0;pointer-events:none;transition:opacity .2s;z-index:200;text-align:left;
font-weight:500;box-shadow:0 10px 28px rgba(0,0,0,.25)
}}
[data-tooltip]:hover::after,[data-tooltip]:focus::after{{opacity:1}}
.toggle-row{{display:flex;align-items:center;gap:12px;flex-wrap:wrap}}
.toggle-group{{display:flex;border:1px solid {mg};border-radius:999px;overflow:hidden;width:fit-content;background:{w}}}
.toggle-btn{{padding:8px 18px;font-size:12px;font-weight:800;color:{dg};background:{w};border:none;cursor:pointer;transition:all .2s}}
.toggle-btn:hover{{background:{lg}}}
.toggle-btn.active{{background:{n};color:#fff}}
.risk-item{{padding:12px 15px;border-left:4px solid {a};margin-bottom:10px;background:{w};border-radius:0 10px 10px 0;font-size:12px;color:{n};line-height:1.5;transition:border-color .2s,box-shadow .2s}}
.risk-item:hover{{border-left-color:{r};box-shadow:0 2px 8px rgba(0,0,0,.08)}}
.domain-grid{{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;margin-bottom:18px}}
.domain-card{{display:flex;align-items:center;gap:10px;padding:11px 14px;background:{w};border:1.5px solid {mg};border-radius:12px;transition:border-color .15s,box-shadow .15s;user-select:none}}
.domain-card:hover{{border-color:{t};box-shadow:var(--shadow-strong);transform:translateY(-2px)}}
.domain-card.domain-active{{border-color:var(--d-color,{t});box-shadow:0 0 0 3px color-mix(in srgb,var(--d-color,{t}) 18%,transparent)}}
.domain-num{{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:6px;color:#fff;font-size:11px;font-weight:800;flex-shrink:0}}
.domain-label{{font-size:11px;color:{n};font-weight:700;flex:1;line-height:1.3}}
.domain-cnt{{font-size:12px;font-weight:800;flex-shrink:0}}
.airi-tag{{font-size:12px;color:{dg};font-weight:500;margin-left:4px}}
.airi-kpi{{display:flex;gap:18px;align-items:center;flex-wrap:wrap;margin-top:14px}}
.airi-copy{{display:grid;gap:8px;min-width:240px}}
.metric-inline{{font-size:12px;color:{n}}}
.airi-table{{width:100%;border-collapse:collapse}}
.airi-table th{{padding:10px 8px;font-size:11px;text-align:left;color:{dg};background:{lg};font-weight:800;text-transform:uppercase;letter-spacing:.05em}}
.airi-table tr:hover{{background:{lg}}}
.ev-row{{transition:background .15s;cursor:default}}
.ev-row:hover{{background:{lg}}}
.filter-chip{{display:inline-flex;align-items:center;padding:5px 12px;border-radius:999px;font-size:11px;font-weight:800;cursor:pointer;border:1px solid {mg};margin:0 6px 8px 0;transition:all .15s;background:{w};user-select:none}}
.filter-chip.active{{background:{n};color:#fff;border-color:{n}}}
.filter-chip:hover:not(.active){{background:{lg}}}
.footer{{font-size:11px;color:{dg};text-align:center;padding:24px 0;border-top:1px solid rgba(13,31,60,.08);margin-top:40px}}
@media(max-width:1000px){{
.metric-grid{{grid-template-columns:repeat(3,minmax(0,1fr))}}
.stage-deck{{grid-template-columns:1fr}}
.memo-card,.memo-card:nth-child(1),.memo-card:nth-child(2),.memo-card:nth-child(3),.memo-card:nth-child(4){{grid-column:span 12}}
.config-pattern{{grid-template-columns:1fr}}
.config-grid{{grid-template-columns:1fr}}
}}
@media(max-width:760px){{
.hero{{padding:28px 22px;grid-template-columns:1fr}}
.content{{padding:24px 14px 42px}}
.nav{{padding-right:10px}}
.nav-brand{{display:none}}
.nav-link{{padding:12px 12px;font-size:11px}}
.metric-grid{{grid-template-columns:repeat(2,minmax(0,1fr))}}
}}
@media(max-width:520px){{
.metric-grid{{grid-template-columns:1fr}}
.s-title{{font-size:21px}}
.hero h1{{font-size:28px}}
}}
"""
JS = r"""
function toggleCard(card) {
var d = card.querySelector('.card-detail');
var exp = card.classList.toggle('expanded');
card.setAttribute('aria-expanded', String(exp));
if (d) d.style.display = exp ? 'block' : 'none';
}
var _airiView = 'covered';
var _airiDomain = 0;
function _updateAiriToggleLabels() {
var covered = 0;
var gaps = 0;
var card = document.querySelector('.domain-card[data-domain="' + _airiDomain + '"]');
if (card) {
covered = parseInt(card.dataset.covered || '0', 10);
gaps = parseInt(card.dataset.gaps || '0', 10);
} else {
var allCard = document.querySelector('.domain-card[data-domain="0"]');
covered = parseInt(allCard?.dataset.covered || '0', 10);
gaps = parseInt(allCard?.dataset.gaps || '0', 10);
}
document.querySelectorAll('.toggle-btn[data-view]').forEach(function(b) {
if (b.dataset.view === 'covered') b.textContent = 'Covered (' + covered + ')';
if (b.dataset.view === 'gaps') b.textContent = 'Gaps (' + gaps + ')';
});
}
function _applyAiriFilter() {
document.querySelectorAll('.airi-covered,.airi-gaps').forEach(function(r) {
var viewOk = r.classList.contains('airi-' + _airiView);
var domainOk = (_airiDomain === 0 || r.dataset.domain == _airiDomain);
r.style.display = (viewOk && domainOk) ? '' : 'none';
});
_updateAiriToggleLabels();
}
function airiToggle(view) {
_airiView = view;
_applyAiriFilter();
document.querySelectorAll('.toggle-btn[data-view]').forEach(function(b) {
b.classList.toggle('active', b.dataset.view === view);
});
}
function filterDomain(num) {
_airiDomain = (_airiDomain === num) ? 0 : num;
document.querySelectorAll('.domain-card').forEach(function(c) {
var active = (c.dataset.domain == _airiDomain);
c.classList.toggle('domain-active', active);
c.style.setProperty('--d-color', active ? c.querySelector('.domain-num').style.background : '');
});
_applyAiriFilter();
}
function filterEv(sev) {
document.querySelectorAll('.ev-row').forEach(function(r) {
r.style.display = (sev === 'all' || r.classList.contains('ev-' + sev)) ? '' : 'none';
});
document.querySelectorAll('.filter-chip').forEach(function(c) {
c.classList.toggle('active', c.dataset.sev === sev);
});
}
function updateNav() {
var cur = '';
document.querySelectorAll('section[id]').forEach(function(s) {
if (window.scrollY >= s.offsetTop - 88) cur = s.id;
});
document.querySelectorAll('.nav-link').forEach(function(l) {
l.classList.toggle('active', l.getAttribute('href') === '#' + cur);
});
}
window.addEventListener('scroll', updateNav, { passive: true });
document.addEventListener('DOMContentLoaded', function() {
updateNav();
airiToggle('covered');
filterEv('all');
document.querySelectorAll('.card').forEach(function(c) {
c.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleCard(c); }
});
});
});
"""