"""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}} .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}} .reg-basis-box{{background:#f8fafc;border:1px solid #dbe6f1;border-radius:12px;padding:14px 16px;margin-bottom:20px}} .reg-basis-title{{font-size:12px;font-weight:700;color:{n};margin-bottom:4px}} .reg-basis-body{{font-size:12px;color:{dg};margin-bottom:2px}} .reg-basis-note{{font-size:11px;color:{dg};font-style:italic}} .reg-review-warn{{font-size:11px;color:#b45309;margin-top:6px;font-weight:600}} .reg-stage{{margin-bottom:22px}} .reg-stage-title{{font-size:13px;font-weight:700;color:{n};border-bottom:1px solid #dbe6f1;padding-bottom:4px;margin-bottom:10px}} .reg-item{{border-left:3px solid #dbe6f1;padding:8px 12px;margin-bottom:8px;border-radius:0 8px 8px 0;background:#fbfdff}} .reg-item-header{{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:4px}} .reg-req-label{{font-size:12px;font-weight:600;color:{n}}} .reg-badge{{font-size:10px;font-weight:700;padding:2px 7px;border-radius:6px;white-space:nowrap}} .reg-signal{{background:#fff7ed;color:#b45309;border:1px solid #fed7aa}} .reg-partial{{background:#eff6ff;color:#1e40af;border:1px solid #bfdbfe}} .reg-aligned{{background:#f0fdf4;color:#166534;border:1px solid #bbf7d0}} .reg-gap{{background:#fef2f2;color:#991b1b;border:1px solid #fecaca}} .reg-src-chip{{font-size:10px;color:{dg};background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:1px 5px;font-family:"Consolas","SFMono-Regular",monospace}} .reg-meta{{font-size:11px;color:{dg};margin-top:2px}} .reg-gap-note{{color:#9a3412}} .reg-note-text{{font-size:11px;color:{dg};margin-top:3px;font-style:italic}} .reg-summary{{font-size:12px;color:{dg};border-top:1px solid #dbe6f1;padding-top:10px;margin-top:4px}} @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); } }); }); }); """