summaryrefslogtreecommitdiff
path: root/frontend/src/app.css
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-17 14:32:37 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-17 14:32:37 +0800
commit5c0ba37eda80d39e6ceca59bb1d5f4942f858995 (patch)
tree948723f9cedf7ccb0707fa6ee516bd30fe20fd10 /frontend/src/app.css
downloadstrategy-lab-5c0ba37eda80d39e6ceca59bb1d5f4942f858995.tar.gz
chore: establish Strategy Lab source baseline (development, not release)
Diffstat (limited to 'frontend/src/app.css')
-rw-r--r--frontend/src/app.css249
1 files changed, 249 insertions, 0 deletions
diff --git a/frontend/src/app.css b/frontend/src/app.css
new file mode 100644
index 0000000..c9ed4be
--- /dev/null
+++ b/frontend/src/app.css
@@ -0,0 +1,249 @@
+:root {
+ --bg: #f7fafb;
+ --surface: #ffffff;
+ --surface-2: #f1f5f6;
+ --border: #e2e8ea;
+ --border-strong: #cbd5d8;
+ --text: #1e2a30;
+ --text-2: #4b5a62;
+ --text-3: #7b8a92;
+ --teal: #0d9488;
+ --teal-strong: #0f766e;
+ --teal-soft: #e6f4f2;
+ --ink: #12312d;
+ --danger: #b91c1c;
+ --danger-soft: #fdf2f2;
+ --warn: #92400e;
+ --warn-soft: #fff9ec;
+ --ok: #15803d;
+ --ok-soft: #ecfdf3;
+ --radius: 10px;
+ --shadow: 0 1px 2px rgba(16, 42, 49, 0.05), 0 2px 8px rgba(16, 42, 49, 0.05);
+ --mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, 'Noto Sans Mono CJK SC', monospace;
+ --font: -apple-system, 'SF Pro Text', BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
+ 'Noto Sans CJK SC', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
+}
+
+* { box-sizing: border-box; }
+
+html, body {
+ margin: 0;
+ padding: 0;
+ background: var(--bg);
+ color: var(--text);
+ font-family: var(--font);
+ font-size: 14.5px;
+ line-height: 1.55;
+ -webkit-font-smoothing: antialiased;
+}
+
+h1, h2, h3, h4 { font-weight: 600; margin: 0; }
+h1 { font-size: 1.35rem; }
+h2 { font-size: 1.1rem; }
+h3 { font-size: 1rem; }
+a { color: var(--teal-strong); text-decoration: none; }
+a:hover { text-decoration: underline; }
+p { margin: 0.4rem 0; }
+
+button, input, select, textarea { font: inherit; color: inherit; }
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 0.45rem 0.95rem;
+ border-radius: var(--radius);
+ border: 1px solid var(--border-strong);
+ background: var(--surface);
+ color: var(--text);
+ cursor: pointer;
+ font-size: 0.92rem;
+ transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
+ white-space: nowrap;
+}
+.btn:hover:not(:disabled) { background: var(--surface-2); }
+.btn:disabled { opacity: 0.5; cursor: not-allowed; }
+.btn:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, a:focus-visible {
+ outline: 2px solid var(--teal);
+ outline-offset: 1px;
+}
+.btn.primary {
+ background: var(--teal-strong);
+ border-color: var(--teal-strong);
+ color: #fff;
+}
+.btn.primary:hover:not(:disabled) { background: #115e59; }
+.btn.danger { color: var(--danger); border-color: #f2c7c7; }
+.btn.danger:hover:not(:disabled) { background: var(--danger-soft); }
+.btn.ghost { border-color: transparent; background: transparent; }
+.btn.ghost:hover:not(:disabled) { background: var(--surface-2); }
+.btn.small { padding: 0.25rem 0.6rem; font-size: 0.83rem; }
+.btn[aria-pressed='true'] { background: var(--teal-soft); border-color: var(--teal); color: var(--ink); }
+
+label { display: block; font-size: 0.84rem; color: var(--text-2); font-weight: 500; margin-bottom: 0.25rem; }
+input, select, textarea.plain {
+ width: 100%;
+ padding: 0.45rem 0.65rem;
+ border: 1px solid var(--border-strong);
+ border-radius: 8px;
+ background: var(--surface);
+ font-size: 0.92rem;
+}
+input[type='checkbox'], input[type='radio'] { width: auto; }
+
+.card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 1rem 1.15rem;
+ box-shadow: var(--shadow);
+}
+.card + .card { margin-top: 0.9rem; }
+
+.hint { color: var(--text-3); font-size: 0.83rem; }
+
+table.data {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.88rem;
+}
+table.data th {
+ text-align: left;
+ font-weight: 600;
+ color: var(--text-2);
+ border-bottom: 1px solid var(--border-strong);
+ padding: 0.45rem 0.6rem;
+ white-space: nowrap;
+}
+table.data td {
+ border-bottom: 1px solid var(--border);
+ padding: 0.42rem 0.6rem;
+ vertical-align: top;
+}
+table.data tr:hover td { background: var(--surface-2); }
+.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
+
+.badge {
+ display: inline-block;
+ padding: 0.1rem 0.5rem;
+ border-radius: 999px;
+ font-size: 0.76rem;
+ font-weight: 600;
+ border: 1px solid transparent;
+}
+.badge.teal { background: var(--teal-soft); color: var(--teal-strong); }
+.badge.grey { background: var(--surface-2); color: var(--text-2); border-color: var(--border); }
+.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: #f0ddb4; }
+.badge.danger { background: var(--danger-soft); color: var(--danger); border-color: #f2c7c7; }
+.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: #c3ecd0; }
+
+.banner {
+ border-radius: var(--radius);
+ padding: 0.65rem 0.9rem;
+ font-size: 0.9rem;
+ border: 1px solid;
+ display: flex;
+ gap: 0.6rem;
+ align-items: flex-start;
+}
+.banner.error { background: var(--danger-soft); border-color: #f2c7c7; color: var(--danger); }
+.banner.warn { background: var(--warn-soft); border-color: #f0ddb4; color: var(--warn); }
+.banner.info { background: var(--teal-soft); border-color: #c8eae6; color: var(--teal-strong); }
+
+.stack { display: flex; flex-direction: column; gap: 0.75rem; }
+.row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
+.spread { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
+.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
+.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem; }
+
+.tabs {
+ display: flex;
+ gap: 0.25rem;
+ border-bottom: 1px solid var(--border);
+}
+.tabs button {
+ padding: 0.55rem 1rem;
+ border: none;
+ background: none;
+ color: var(--text-2);
+ font-size: 0.95rem;
+ cursor: pointer;
+ border-bottom: 2px solid transparent;
+ border-radius: 6px 6px 0 0;
+}
+.tabs button:hover { background: var(--surface-2); }
+.tabs button[aria-selected='true'] {
+ color: var(--teal-strong);
+ font-weight: 600;
+ border-bottom-color: var(--teal);
+}
+
+/* metric tiles shared by the results card and other metric rows; without
+ these the six result metrics stacked vertically with large blank space */
+.metric-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 0.7rem;
+}
+.metric {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 0.6rem 0.85rem;
+ min-width: 0;
+}
+.mlabel { color: var(--text-3); font-size: 0.8rem; }
+.mvalue {
+ font-size: 1.15rem;
+ font-weight: 700;
+ color: var(--text);
+ font-variant-numeric: tabular-nums;
+ overflow-wrap: anywhere;
+}
+.cols-2 { display: grid; grid-template-columns: 1.4fr 1fr; gap: 1rem; align-items: start; }
+.table-wrap { overflow-x: auto; min-width: 0; }
+
+@media (max-width: 900px) {
+ .cols-2 { grid-template-columns: minmax(0, 1fr); }
+ .metric-grid { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
+}
+
+code, pre {
+ font-family: var(--mono);
+ font-size: 0.85rem;
+}
+pre { margin: 0; white-space: pre-wrap; word-break: break-all; }
+
+.sr-only {
+ position: absolute;
+ width: 1px; height: 1px;
+ padding: 0; margin: -1px;
+ overflow: hidden; clip: rect(0 0 0 0);
+ border: 0;
+}
+
+/* ---- narrow-viewport wrapping (no global content hiding) ----
+ Real provider error strings contain long unbreakable URLs; .spread/.stack
+ children would otherwise propagate min-content outwards and inflate the
+ document width on mobile. `overflow-wrap` keeps every word reachable while
+ wrapping; nothing is hidden — long tables get a local scroll container. */
+.card, .stack, .row, .spread, .banner { min-width: 0; }
+.banner { overflow-wrap: anywhere; }
+.banner .err-list { overflow-wrap: anywhere; }
+.card table.data { min-width: 0; }
+
+@media (max-width: 900px) {
+ /* collapse multi-column forms: the dataset wizard grid3 + label min-widths
+ measured 523px min-content at 375px viewport (mobile-overflow-probe) */
+ .grid2, .grid3 { grid-template-columns: minmax(0, 1fr); }
+ /* wrap table cells instead of nowrap inflating min-content; a truly wide
+ tabular block still gets its own local scrollbar (.scrollx), content
+ remains fully accessible without a document-level horizontal overflow */
+ table.data th, table.data td {
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ .btn { white-space: normal; }
+}
+
+