299 lines
9.5 KiB
CSS
299 lines
9.5 KiB
CSS
@import "tw-animate-css";
|
|
@import "shadcn/tailwind.css";
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/*
|
|
* GSD theme — mirrors the Omics Portal design system (Manrope + Fraunces, warm
|
|
* off-white surfaces, #cb333b red). Tokens are RAW OKLCH triplets (L C H) so the
|
|
* Tailwind v3 config can wrap them as `oklch(var(--token) / <alpha-value>)` and
|
|
* opacity utilities (bg-primary/80, …) keep working. Dark tokens follow either a
|
|
* `.dark` class or the portal's `[data-bs-theme="dark"]`, ready for embedding.
|
|
*/
|
|
@layer base {
|
|
:root {
|
|
--font-sans: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Arial, sans-serif;
|
|
--font-heading: "Fraunces", "Source Serif Pro", Georgia, "Times New Roman", serif;
|
|
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
|
|
|
|
/* Layered elevation, tinted with the foreground hue so depth reads warm on
|
|
warm surfaces. Borders mark structure; shadows mark elevation. */
|
|
--shadow-tint: 0.2678 0.0097 355.6;
|
|
--shadow-xs: 0 1px 2px oklch(var(--shadow-tint) / 0.05);
|
|
--shadow-sm: 0 1px 2px oklch(var(--shadow-tint) / 0.05), 0 2px 6px -1px oklch(var(--shadow-tint) / 0.05);
|
|
--shadow-md: 0 2px 4px -2px oklch(var(--shadow-tint) / 0.06), 0 12px 32px -8px oklch(var(--shadow-tint) / 0.1);
|
|
|
|
--background: 0.9918 0.0011 17.2; /* surface */
|
|
--foreground: 0.2678 0.0097 355.6; /* text */
|
|
--card: 0.9985 0.0006 17.2;
|
|
--card-foreground: 0.2678 0.0097 355.6;
|
|
--popover: 0.9985 0.0006 17.2;
|
|
--popover-foreground: 0.2678 0.0097 355.6;
|
|
--primary: 0.5587 0.1881 23.2; /* gsd red */
|
|
--primary-foreground: 0.9918 0.0011 17.2;
|
|
--primary-hover: 0.5095 0.1812 24.1;
|
|
--secondary: 0.9709 0.0011 17.2; /* surface-alt */
|
|
--secondary-foreground: 0.2678 0.0097 355.6;
|
|
--muted: 0.9709 0.0011 17.2;
|
|
--muted-foreground: 0.5133 0.0088 345.6;
|
|
--accent: 0.9408 0.0011 17.2; /* sunken */
|
|
--accent-foreground: 0.2678 0.0097 355.6;
|
|
--destructive: 0.5587 0.1881 23.2;
|
|
--destructive-foreground: 0.9918 0.0011 17.2;
|
|
--border: 0.9093 0.0035 354.7;
|
|
--input: 0.9093 0.0035 354.7;
|
|
--ring: 0.5587 0.1881 23.2;
|
|
--success: 0.7577 0.1581 165.0;
|
|
--warning: 0.8523 0.1386 78.9;
|
|
--info: 0.7035 0.1128 221.3;
|
|
--radius: 0.5rem;
|
|
--sidebar: 0.9709 0.0011 17.2;
|
|
--sidebar-foreground: 0.2678 0.0097 355.6;
|
|
--sidebar-primary: 0.5587 0.1881 23.2;
|
|
--sidebar-primary-foreground: 0.9918 0.0011 17.2;
|
|
--sidebar-accent: 0.9408 0.0011 17.2;
|
|
--sidebar-accent-foreground: 0.2678 0.0097 355.6;
|
|
--sidebar-border: 0.9093 0.0035 354.7;
|
|
--sidebar-ring: 0.5587 0.1881 23.2;
|
|
}
|
|
|
|
.dark,
|
|
[data-bs-theme="dark"] {
|
|
--shadow-tint: 0 0 0;
|
|
--background: 0.2178 0 90;
|
|
--foreground: 0.9310 0 90;
|
|
--card: 0.2350 0 90;
|
|
--card-foreground: 0.9310 0 90;
|
|
--popover: 0.2350 0 90;
|
|
--popover-foreground: 0.9310 0 90;
|
|
--primary: 0.6023 0.1848 20.3;
|
|
--primary-foreground: 0.9310 0 90;
|
|
--primary-hover: 0.6897 0.1779 16.9;
|
|
--secondary: 0.2972 0 90;
|
|
--secondary-foreground: 0.9310 0 90;
|
|
--muted: 0.2972 0 90;
|
|
--muted-foreground: 0.7826 0 90;
|
|
--accent: 0.3485 0 90;
|
|
--accent-foreground: 0.9310 0 90;
|
|
--destructive: 0.6023 0.1848 20.3;
|
|
--destructive-foreground: 0.9310 0 90;
|
|
--border: 0.3715 0 90;
|
|
--input: 0.3715 0 90;
|
|
--ring: 0.6897 0.1779 16.9;
|
|
--sidebar: 0.2350 0 90;
|
|
--sidebar-foreground: 0.9310 0 90;
|
|
--sidebar-primary: 0.6023 0.1848 20.3;
|
|
--sidebar-primary-foreground: 0.9310 0 90;
|
|
--sidebar-accent: 0.3485 0 90;
|
|
--sidebar-accent-foreground: 0.9310 0 90;
|
|
--sidebar-border: 0.3715 0 90;
|
|
--sidebar-ring: 0.6897 0.1779 16.9;
|
|
}
|
|
|
|
* {
|
|
@apply border-border outline-ring/50;
|
|
}
|
|
|
|
/* A click should read as a physical state change, not only as a hover ending.
|
|
Keep the response short and compositor-friendly so repeated actions stay fast. */
|
|
button:not(:disabled) {
|
|
transform-origin: center;
|
|
transition-property: color, background-color, border-color, box-shadow, filter, transform;
|
|
transition-duration: 140ms;
|
|
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
|
|
button:not(:disabled):active {
|
|
transform: scale(0.97);
|
|
filter: brightness(0.94);
|
|
box-shadow: none;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
button:not(:disabled) {
|
|
transition-duration: 0.01ms;
|
|
}
|
|
|
|
button:not(:disabled):active {
|
|
transform: none;
|
|
filter: brightness(0.9);
|
|
box-shadow: none;
|
|
}
|
|
}
|
|
|
|
html {
|
|
@apply font-sans;
|
|
}
|
|
|
|
body {
|
|
@apply bg-background text-foreground antialiased;
|
|
font-feature-settings: "ss01", "cv11";
|
|
}
|
|
|
|
h1, h2, h3, h4 {
|
|
font-family: var(--font-heading);
|
|
font-optical-sizing: auto;
|
|
letter-spacing: -0.015em;
|
|
}
|
|
|
|
code, pre, kbd, samp {
|
|
font-family: var(--font-mono);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
::selection {
|
|
background: oklch(var(--primary) / 0.16);
|
|
}
|
|
|
|
/* Thin, brand-tinted scrollbars */
|
|
* {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: oklch(var(--border)) transparent;
|
|
}
|
|
*::-webkit-scrollbar {
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
*::-webkit-scrollbar-thumb {
|
|
background: oklch(var(--border));
|
|
border-radius: 9999px;
|
|
border: 2px solid transparent;
|
|
background-clip: padding-box;
|
|
}
|
|
*::-webkit-scrollbar-thumb:hover {
|
|
background: oklch(var(--muted-foreground) / 0.5);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Minimal markdown prose for assistant turns (no typography plugin installed).
|
|
* Scoped so it never leaks into widgets.
|
|
*/
|
|
@layer components {
|
|
/* Standard micro-label: panel headers, nav sections, meta rows. Set in the
|
|
mono register so labels/meta read as a distinct typographic layer from the
|
|
sans body prose (mono needs less tracking than the sans did). */
|
|
.thot-label {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.6875rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: oklch(var(--muted-foreground));
|
|
}
|
|
|
|
.thot-prose {
|
|
color: oklch(var(--foreground));
|
|
font-size: 0.9375rem;
|
|
line-height: 1.65;
|
|
}
|
|
.thot-prose > :first-child { margin-top: 0; }
|
|
.thot-prose > :last-child { margin-bottom: 0; }
|
|
.thot-prose p { margin: 0.6em 0; }
|
|
.thot-prose h1, .thot-prose h2, .thot-prose h3 {
|
|
font-family: var(--font-heading);
|
|
font-weight: 600;
|
|
line-height: 1.25;
|
|
margin: 1.1em 0 0.5em;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
.thot-prose h1 { font-size: 1.45rem; }
|
|
.thot-prose h2 { font-size: 1.2rem; }
|
|
.thot-prose h3 { font-size: 1.02rem; }
|
|
.thot-prose ul, .thot-prose ol { margin: 0.6em 0; padding-left: 1.3em; }
|
|
.thot-prose li { margin: 0.25em 0; }
|
|
.thot-prose a { color: oklch(var(--primary)); text-decoration: underline; text-underline-offset: 2px; }
|
|
.thot-prose code {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.85em;
|
|
background: oklch(var(--muted));
|
|
padding: 0.12em 0.4em;
|
|
border-radius: 0.3rem;
|
|
border: 1px solid oklch(var(--border) / 0.6);
|
|
}
|
|
.thot-prose pre {
|
|
background: oklch(var(--muted));
|
|
padding: 0.9rem 1rem;
|
|
border-radius: calc(var(--radius) + 2px);
|
|
overflow-x: auto;
|
|
border: 1px solid oklch(var(--border) / 0.7);
|
|
box-shadow: var(--shadow-xs);
|
|
}
|
|
.thot-prose pre code { background: transparent; padding: 0; }
|
|
.thot-prose blockquote {
|
|
border-left: 2px solid oklch(var(--border));
|
|
padding-left: 0.9em;
|
|
color: oklch(var(--muted-foreground));
|
|
margin: 0.7em 0;
|
|
}
|
|
.thot-prose table {
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
width: 100%;
|
|
margin: 0.7em 0;
|
|
font-size: 0.875rem;
|
|
border: 1px solid oklch(var(--border) / 0.8);
|
|
border-radius: calc(var(--radius) + 2px);
|
|
overflow: hidden;
|
|
}
|
|
.thot-prose th, .thot-prose td { padding: 0.45em 0.7em; text-align: left; }
|
|
.thot-prose td { border-top: 1px solid oklch(var(--border) / 0.6); font-variant-numeric: tabular-nums; }
|
|
.thot-prose th {
|
|
background: oklch(var(--muted));
|
|
font-size: 0.72rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: oklch(var(--muted-foreground));
|
|
}
|
|
.thot-activity-prose,
|
|
.thot-activity-prose p,
|
|
.thot-activity-prose li,
|
|
.thot-activity-prose a,
|
|
.thot-activity-prose code,
|
|
.thot-activity-prose th,
|
|
.thot-activity-prose td {
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
}
|
|
.thot-activity-prose table { table-layout: fixed; }
|
|
.thot-activity-prose pre {
|
|
overflow-x: hidden;
|
|
white-space: pre-wrap;
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
}
|
|
.thot-activity-prose pre code { white-space: inherit; }
|
|
}
|
|
|
|
|
|
@layer components {
|
|
.thot-awaiting-input {
|
|
background: oklch(var(--success) / 0.14);
|
|
border-color: oklch(var(--success) / 0.55);
|
|
}
|
|
.thot-awaiting-input:focus {
|
|
box-shadow: 0 0 0 3px oklch(var(--success) / 0.18);
|
|
}
|
|
/* Live-activity pulse: a soft ring emanates from the red stop dot while the harness
|
|
is actively working, signalling "something is happening". Ring only (no layout
|
|
properties animated); calm 1.6s ease-out cadence, silenced for reduced motion. */
|
|
.thot-pulse {
|
|
animation: thot-pulse-ring 1.5s ease-in-out infinite;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.thot-pulse { animation: none; }
|
|
}
|
|
.thot-prose p + p { margin-top: 1em; }
|
|
.thot-prose ul { list-style: disc; }
|
|
.thot-prose ol { list-style: decimal; }
|
|
.thot-prose ul ul { list-style: circle; }
|
|
.thot-prose ol ol { list-style: lower-alpha; }
|
|
}
|
|
|
|
@keyframes thot-pulse-ring {
|
|
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 oklch(var(--primary) / 0.4); }
|
|
50% { transform: scale(1.15); box-shadow: 0 0 0 5px oklch(var(--primary) / 0); }
|
|
}
|