daf5b7be92
- Changes panel: a working file opens in a CodeMirror editor with its diff inline (DiffEditor.tsx, @codemirror/merge, lazy-loaded); Changes folds the unchanged lines, Whole file shows them; side by side at 50/50 and full width; a Revert per block; original from HEAD or the branch's merge base (`mergeBase` on GET /git/changes?range=branch) - Every diff: green = line added whole, red = removed whole, blue = edited in place (--diff-mod) - History drawn as a commit graph (#58: `parents` on GET /git/log, --date-order; lib/commitGraph.ts) - Per-workspace never-stage list, drawn orange in the Files tree (--kept); closed Terminal rows past three deleted - Status bar plan quota for Claude (per account) and Codex (GET /api/pricing/quota, server/lib/quota.js); compact hover panel that stays open, Refresh button with a spinner - Settings → Appearance holds language, theme and pet; Ctrl+Shift+C copies; compact sidebar navigation and short session times
741 lines
29 KiB
CSS
741 lines
29 KiB
CSS
/**
|
||
* @file index.css
|
||
* @description Global styles and Tailwind CSS configuration for the application.
|
||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||
*/
|
||
|
||
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
/* Palette sourced from Radix Colors (radix-ui.com/colors), an accessible
|
||
* 12-step system built for UI, contrast-checked with APCA — chosen after
|
||
* three rounds of hand-picked values that kept overshooting (too flat, then
|
||
* too dark, then too glaring). Radix's own role guidance for a 12-step scale:
|
||
* 1-2 app/chrome background 6-8 borders (6 subtle, 7-8 stronger)
|
||
* 3-5 component background 9-10 solid (buttons, vibrant fills)
|
||
* 11-12 text (11 secondary, 12 primary)
|
||
* The DARK theme follows it literally: `slate` supplies surface/border/fg and
|
||
* `blue` supplies accent. The LIGHT theme keeps the structure (same roles, same
|
||
* rising-lightness ramp) but swaps the hue family for Claude's own warm ivory
|
||
* and terracotta, because a cool near-white page reads as glare. `green`/
|
||
* `red`/`amber` step 11 supplies the status tokens — the same step used for
|
||
* fg-secondary, since 11 is Radix's guaranteed-readable-text step. Values are
|
||
* the literal Radix hex constants (steps 1/2/3/4/5/6/7/9/11/12), converted to
|
||
* RGB triplets for Tailwind's `<alpha-value>` opacity support.
|
||
*/
|
||
:root,
|
||
[data-mode="light"] {
|
||
/* Light theme — the CSS default, so it applies with no class on <html>.
|
||
`[data-mode="light"]` lets a light theme preview re-scope inside a dark
|
||
page (the Settings appearance cards).
|
||
Radix `slate` light, RE-ORDERED so lightness rises with the index exactly
|
||
as it does in dark: the page is the DARKEST step and a card is brighter
|
||
than the page it sits on. The previous light block used the scale in its
|
||
native direction, which inverted the whole language — a near-white page
|
||
(glare) under grey cards that read as recessed, and every `hover:bg-*`
|
||
darkening instead of lifting.
|
||
|
||
The HUE is warm — Claude's own ivory/terracotta family, not a cool slate.
|
||
A cool near-white ramp kept reading as glare no matter how far the page
|
||
step was pulled down, because the brightest thing on screen was also the
|
||
bluest. Cream lowers the perceived glare at the same measured lightness
|
||
and gives every neutral a warm cast to sit against. The whole ramp sits
|
||
one notch below Claude's own ivory (#faf9f5 card) and no step reaches pure
|
||
white: at full brightness a large light panel still tires the eye. */
|
||
--surface-0: 232 229 218; /* #e8e5da — page background */
|
||
--surface-1: 236 233 223; /* #ece9df — sub-headers inside a card */
|
||
--surface-2: 240 237 228; /* #f0ede4 — inputs, chips, toolbars */
|
||
--surface-3: 245 243 236; /* #f5f3ec — `.card`, Claude's ivory a step down */
|
||
--surface-4: 249 247 241; /* #f9f7f1 — hover / active lift */
|
||
--surface-5: 252 251 247; /* #fcfbf7 — topmost, rare */
|
||
--border: 207 201 184; /* #cfc9b8 */
|
||
--border-light: 222 217 204; /* #ded9cc */
|
||
/* Claude's terracotta (#cc785c), taken down two steps: the brand value is
|
||
3.1:1 on ivory and 3.3:1 under white text, so it works as a large fill and
|
||
fails as a link or a button label. This is 5.3:1 as text on a card and
|
||
5.9:1 white-on-accent. Dark mode keeps its blue — inverting a warm accent
|
||
onto a near-black page is a different problem with a different answer.
|
||
One more step down (#a34a28 → #924224) so `text-accent` also clears 4.5:1
|
||
on its own selected chip (`bg-accent/15–20`), not just on the card. */
|
||
--accent: 146 66 36; /* #924224 */
|
||
--accent-hover: 122 55 30; /* #7a371e */
|
||
/* The FILL behind white text (`.btn-primary`, a Switch's on-track, a ticked
|
||
Checkbox, `bg-accent-solid`). In light themes the text accent is already
|
||
dark enough to carry white, so it simply IS the accent — resolved per
|
||
scope, like `--tint-base`. Dark themes declare their own: there the text
|
||
accent must be LIGHT to read on a near-black card, which makes it a fill
|
||
white text cannot sit on (3.3:1). */
|
||
--accent-solid: var(--accent);
|
||
--accent-solid-hover: var(--accent-hover);
|
||
--fg-primary: 31 30 27; /* #1f1e1b — 15.0:1 on a card */
|
||
--fg-secondary: 96 91 83; /* #605b53 — 6.1:1 */
|
||
/* Measured, not eyeballed: this carries real content (timestamps, counts),
|
||
so it is held above 4.5:1 — 5.4:1 on a card, 4.7:1 on the page — and still
|
||
sits a clear step below fg-secondary. */
|
||
--fg-muted: 104 99 89; /* #686359 */
|
||
/* Warm and soft, to match the surfaces: a neutral-grey shadow goes muddy
|
||
over cream. The SHADOW carries "raised" here, not the quiet border. */
|
||
--shadow-card: 0 1px 2px rgb(60 50 30 / 0.05), 0 2px 6px rgb(60 50 30 / 0.07);
|
||
/* Status: darkened two steps from Radix 11. One step cleared 4.5:1 against
|
||
the card; these also clear it against the status's OWN densest chip (a
|
||
20% tint of itself — 4.5:1), which is where the label actually sits. */
|
||
--status-success: 22 104 66; /* #166842 */
|
||
--status-danger: 170 36 41; /* #aa2429 */
|
||
--status-warning: 133 79 0; /* #854f00 */
|
||
/* What every status/accent FILL is pre-mixed against (see `tint()` in
|
||
tailwind.config.js) — the card surface, because that is where chips and
|
||
alert strips overwhelmingly sit. */
|
||
/* Re-declared by every scope that sets a theme (this block, `.dark`), so it
|
||
resolves against THAT scope's surface-3; a new scope must re-declare it too. */
|
||
--tint-base: var(--surface-3);
|
||
/* Syntax colours for `lib/highlight.ts`. Same hues as the dark set, taken
|
||
four steps down the Tailwind ramp (300 → 700): a pastel keyword is legible
|
||
on a near-black code block and invisible on a light one. */
|
||
--syn-comment: 107 114 128; /* gray-500 #6b7280 */
|
||
--syn-string: 4 120 87; /* emerald-700 #047857 */
|
||
--syn-number: 194 65 12; /* orange-700 #c2410c */
|
||
--syn-keyword: 109 40 217; /* violet-700 #6d28d9 */
|
||
--syn-builtin: 3 105 161; /* sky-700 #0369a1 */
|
||
--syn-function: 180 83 9; /* amber-700 #b45309 */
|
||
--syn-operator: 190 24 93; /* pink-700 #be185d */
|
||
--syn-punctuation: 107 114 128; /* gray-500 #6b7280 */
|
||
--syn-property: 14 116 144; /* cyan-700 #0e7490 */
|
||
--syn-tag: 190 18 60; /* rose-700 #be123c */
|
||
--syn-plain: var(--fg-primary);
|
||
--syn-add: 4 120 87; /* emerald-700 */
|
||
--syn-del: 185 28 28; /* red-700 #b91c1c */
|
||
/* A diff line edited in place (not wholly added or removed). */
|
||
--diff-mod: 29 78 216; /* blue-700 #1d4ed8 */
|
||
/* A file on the Changes panel's never-stage list (orange, apart from amber M). */
|
||
--kept: 194 65 12; /* orange-700 #c2410c */
|
||
}
|
||
|
||
.dark {
|
||
/* Radix `slate` (dark), steps 1/2/3/4/5/6/7. The scale is built in the
|
||
opposite direction on purpose — step 6 is LIGHTER than step 3/4 here,
|
||
which is what makes borders rise off a panel instead of sinking into it
|
||
(the bug in the previous, hand-picked dark border). */
|
||
--surface-0: 17 17 19; /* slate-1 #111113 */
|
||
--surface-1: 24 25 27; /* slate-2 #18191b */
|
||
--surface-2: 33 34 37; /* slate-3 #212225 */
|
||
--surface-3: 39 42 45; /* slate-4 #272a2d */
|
||
--surface-4: 46 49 53; /* slate-5 #2e3135 */
|
||
--surface-5: 54 58 63; /* slate-6 #363a3f */
|
||
--border: 54 58 63; /* slate-6 #363a3f */
|
||
--border-light: 67 72 78; /* slate-7 #43484e */
|
||
/* Text/indicator accent is Radix blue-11 (its text step): blue-9 #0090ff was
|
||
4.4:1 on a card and 3.4:1 on its own selected chip. The solid blue-9 fill
|
||
lives on as the hue of `--accent-solid`, darkened until white text on it
|
||
clears 4.5:1 (it was 3.3:1). */
|
||
--accent: 112 184 255; /* blue-11 #70b8ff */
|
||
--accent-hover: 158 206 255; /* #9eceff */
|
||
--accent-solid: 0 118 209; /* #0076d1 — white 4.8:1 */
|
||
--accent-solid-hover: 0 102 181; /* #0066b5 */
|
||
--fg-primary: 237 238 240; /* slate-12 #edeef0 */
|
||
--fg-secondary: 176 180 186; /* slate-11 #b0b4ba */
|
||
/* slate-9 #696e77 was 2.8:1 on a card. Raised until it clears 4.5:1 on the
|
||
hover lift (surface-4) too, still a clear step below fg-secondary. */
|
||
--fg-muted: 150 154 160; /* #969aa0 */
|
||
/* Dark surfaces already separate by lightness step; a shadow adds nothing
|
||
visible against a dark page and reads as a stray dark smudge. */
|
||
--shadow-card: none;
|
||
--status-success: 61 214 140; /* green-11 dark #3dd68c */
|
||
--status-danger: 255 149 146; /* red-11 dark #ff9592 */
|
||
--status-warning: 255 202 22; /* amber-11 dark #ffca16 */
|
||
--tint-base: var(--surface-3);
|
||
/* Unchanged from the pastels this file has always drawn code in. */
|
||
--syn-comment: 107 114 128; /* gray-500 #6b7280 */
|
||
--syn-string: 110 231 183; /* emerald-300 #6ee7b7 */
|
||
--syn-number: 253 186 116; /* orange-300 #fdba74 */
|
||
--syn-keyword: 196 181 253; /* violet-300 #c4b5fd */
|
||
--syn-builtin: 125 211 252; /* sky-300 #7dd3fc */
|
||
--syn-function: 254 240 138; /* yellow-200 #fef08a */
|
||
--syn-operator: 249 168 212; /* pink-300 #f9a8d4 */
|
||
--syn-punctuation: 156 163 175; /* gray-400 #9ca3af */
|
||
--syn-property: 103 232 249; /* cyan-300 #67e8f9 */
|
||
--syn-tag: 253 164 175; /* rose-300 #fda4af */
|
||
--syn-plain: 229 231 235; /* gray-200 #e5e7eb — kept off fg-primary so Default is unchanged */
|
||
--syn-add: 110 231 183; /* emerald-300 */
|
||
--syn-del: 252 165 165; /* red-300 #fca5a5 */
|
||
--diff-mod: 96 165 250; /* blue-400 #60a5fa */
|
||
--kept: 251 146 60; /* orange-400 #fb923c */
|
||
}
|
||
|
||
/* Named themes (`data-theme` on <html>, set by hooks/useTheme.ts, picked in
|
||
* Settings → Appearance). Each overrides only what differs from its mode's
|
||
* default block above; `--tint-base` and light `--syn-plain` are derived there.
|
||
* Dark ones pair with `.dark`, light ones with `[data-mode="light"]`, so a
|
||
* theme can never paint over the wrong mode whatever order the attributes
|
||
* land in. Contrast is held to the defaults' own bar: fg-muted ≥4.5:1 on a
|
||
* light card, status labels ≥4.5:1 on their own /20 chip in light themes. */
|
||
.dark[data-theme="darcula"] {
|
||
--surface-0: 30 31 32; /* #1e1f20 */
|
||
--surface-1: 35 36 37; /* #232425 */
|
||
--surface-2: 39 41 42; /* #27292a */
|
||
--surface-3: 43 43 43; /* #2b2b2b */
|
||
--surface-4: 51 53 55; /* #333537 */
|
||
--surface-5: 60 63 65; /* #3c3f41 */
|
||
--border: 63 66 68; /* #3f4244 */
|
||
--border-light: 77 81 83; /* #4d5153 */
|
||
--accent: 132 189 219; /* #84bddb */
|
||
--accent-hover: 168 209 230; /* #a8d1e6 */
|
||
--accent-solid: 45 124 167; /* #2d7ca7 */
|
||
--accent-solid-hover: 38 106 143; /* #266a8f */
|
||
--fg-primary: 220 220 220; /* #dcdcdc */
|
||
--fg-secondary: 169 183 198; /* #a9b7c6 */
|
||
--fg-muted: 154 159 163; /* #9a9fa3 */
|
||
--status-success: 132 200 141; /* #84c88d */
|
||
--status-danger: 255 153 150; /* #ff9996 */
|
||
--status-warning: 232 191 106; /* #e8bf6a */
|
||
--syn-comment: 128 128 128; /* #808080 */
|
||
--syn-string: 127 159 105; /* #7f9f69 */
|
||
--syn-number: 104 151 187; /* #6897bb */
|
||
--syn-keyword: 204 120 50; /* #cc7832 */
|
||
--syn-builtin: 136 136 198; /* #8888c6 */
|
||
--syn-function: 255 198 109; /* #ffc66d */
|
||
--syn-operator: 169 183 198; /* #a9b7c6 */
|
||
--syn-punctuation: 169 183 198; /* #a9b7c6 */
|
||
--syn-property: 168 139 192; /* #a88bc0 */
|
||
--syn-tag: 232 191 106; /* #e8bf6a */
|
||
--syn-add: 106 171 115; /* #6aab73 */
|
||
--syn-del: 255 107 104; /* #ff6b68 */
|
||
--syn-plain: 169 183 198; /* #a9b7c6 */
|
||
}
|
||
|
||
.dark[data-theme="apple-dark"] {
|
||
--surface-0: 22 22 24; /* #161618 */
|
||
--surface-1: 28 28 30; /* #1c1c1e */
|
||
--surface-2: 36 36 38; /* #242426 */
|
||
--surface-3: 44 44 46; /* #2c2c2e */
|
||
--surface-4: 54 54 56; /* #363638 */
|
||
--surface-5: 58 58 60; /* #3a3a3c */
|
||
--border: 56 56 58; /* #38383a */
|
||
--border-light: 72 72 74; /* #48484a */
|
||
--accent: 123 189 255; /* #7bbdff */
|
||
--accent-hover: 163 209 255; /* #a3d1ff */
|
||
--accent-solid: 9 115 222; /* #0973de */
|
||
--accent-solid-hover: 7 98 190; /* #0762be */
|
||
--fg-primary: 245 245 247; /* #f5f5f7 */
|
||
/* Lifted with fg-muted so the two stay a visible step apart. */
|
||
--fg-secondary: 199 199 204; /* #c7c7cc */
|
||
--fg-muted: 160 160 164; /* #a0a0a4 */
|
||
--status-success: 48 209 88; /* #30d158 */
|
||
--status-danger: 255 155 150; /* #ff9b96 */
|
||
--status-warning: 255 214 10; /* #ffd60a */
|
||
--syn-comment: 127 140 152; /* #7f8c98 */
|
||
--syn-string: 252 106 93; /* #fc6a5d */
|
||
--syn-number: 208 191 105; /* #d0bf69 */
|
||
--syn-keyword: 252 95 163; /* #fc5fa3 */
|
||
--syn-builtin: 93 216 255; /* #5dd8ff */
|
||
--syn-function: 103 183 164; /* #67b7a4 */
|
||
--syn-operator: 161 161 166; /* #a1a1a6 */
|
||
--syn-punctuation: 161 161 166; /* #a1a1a6 */
|
||
--syn-property: 181 140 240; /* #b58cf0 */
|
||
--syn-tag: 252 95 163; /* #fc5fa3 */
|
||
--syn-add: 48 209 88; /* #30d158 */
|
||
--syn-del: 255 105 97; /* #ff6961 */
|
||
--syn-plain: 229 229 234; /* #e5e5ea */
|
||
}
|
||
|
||
/* Orca Dark — Orca's neutral palette (src/renderer/src/assets/main.css `.dark`).
|
||
* Accent is NOT Orca's #e5e5e5 primary: AgentHub puts white text on `bg-accent`
|
||
* (.btn-primary), which would read 1.3:1 there. Text accent and the solid fill
|
||
* behind white text (`--accent-solid`) are two shades of #3b82f6, each held at
|
||
* 4.5:1. Syntax colours are Apple Dark's. */
|
||
.dark[data-theme="orca-dark"] {
|
||
--surface-0: 10 10 10; /* #0a0a0a */
|
||
--surface-1: 17 17 17; /* #111111 */
|
||
--surface-2: 23 23 23; /* #171717 */
|
||
--surface-3: 28 28 28; /* #1c1c1c */
|
||
--surface-4: 38 38 38; /* #262626 */
|
||
--surface-5: 64 64 64; /* #404040 */
|
||
--border: 31 31 31; /* #1f1f1f */
|
||
--border-light: 46 46 46; /* #2e2e2e */
|
||
--accent: 106 160 248; /* #6aa0f8 */
|
||
--accent-hover: 147 187 250; /* #93bbfa */
|
||
--accent-solid: 52 114 216; /* #3472d8 */
|
||
--accent-solid-hover: 44 97 184; /* #2c61b8 */
|
||
--fg-primary: 250 250 250; /* #fafafa */
|
||
--fg-secondary: 212 212 212; /* #d4d4d4 */
|
||
--fg-muted: 161 161 161; /* #a1a1a1 */
|
||
--status-success: 134 239 172; /* #86efac */
|
||
--status-danger: 255 112 115; /* #ff7073 */
|
||
--status-warning: 234 179 8; /* #eab308 */
|
||
--syn-comment: 127 140 152; /* #7f8c98 */
|
||
--syn-string: 252 106 93; /* #fc6a5d */
|
||
--syn-number: 208 191 105; /* #d0bf69 */
|
||
--syn-keyword: 252 95 163; /* #fc5fa3 */
|
||
--syn-builtin: 93 216 255; /* #5dd8ff */
|
||
--syn-function: 103 183 164; /* #67b7a4 */
|
||
--syn-operator: 161 161 166; /* #a1a1a6 */
|
||
--syn-punctuation: 161 161 166; /* #a1a1a6 */
|
||
--syn-property: 181 140 240; /* #b58cf0 */
|
||
--syn-tag: 252 95 163; /* #fc5fa3 */
|
||
--syn-add: 48 209 88; /* #30d158 */
|
||
--syn-del: 255 105 97; /* #ff6961 */
|
||
--syn-plain: 229 229 234; /* #e5e5ea */
|
||
}
|
||
|
||
[data-mode="light"][data-theme="apple-light"] {
|
||
--surface-0: 233 233 238; /* #e9e9ee */
|
||
--surface-1: 239 239 244; /* #efeff4 */
|
||
--surface-2: 245 245 248; /* #f5f5f8 */
|
||
--surface-3: 251 251 253; /* #fbfbfd */
|
||
--surface-4: 253 253 254; /* #fdfdfe */
|
||
--surface-5: 255 255 255; /* #ffffff */
|
||
--border: 209 209 214; /* #d1d1d6 */
|
||
--border-light: 229 229 234; /* #e5e5ea */
|
||
--accent: 0 92 184; /* #005cb8 */
|
||
--accent-hover: 0 76 153; /* #004c99 */
|
||
--fg-primary: 29 29 31; /* #1d1d1f */
|
||
--fg-secondary: 81 81 84; /* #515154 */
|
||
--fg-muted: 103 103 108; /* #67676c */
|
||
--shadow-card: 0 1px 2px rgb(0 0 0 / 0.05), 0 2px 6px rgb(0 0 0 / 0.06);
|
||
--status-success: 23 107 46; /* #176b2e */
|
||
--status-danger: 179 0 14; /* #b3000e */
|
||
--status-warning: 122 79 0; /* #7a4f00 */
|
||
--syn-comment: 93 108 121; /* #5d6c79 */
|
||
--syn-string: 196 26 22; /* #c41a16 */
|
||
--syn-number: 28 0 207; /* #1c00cf */
|
||
--syn-keyword: 155 35 147; /* #9b2393 */
|
||
--syn-builtin: 11 79 121; /* #0b4f79 */
|
||
--syn-function: 50 109 116; /* #326d74 */
|
||
--syn-operator: 75 75 80; /* #4b4b50 */
|
||
--syn-punctuation: 75 75 80; /* #4b4b50 */
|
||
--syn-property: 57 0 160; /* #3900a0 */
|
||
--syn-tag: 155 35 147; /* #9b2393 */
|
||
--syn-add: 31 122 54; /* #1f7a36 */
|
||
--syn-del: 197 0 15; /* #c5000f */
|
||
}
|
||
|
||
[data-mode="light"][data-theme="intellij-light"] {
|
||
--surface-0: 236 238 242; /* #eceef2 */
|
||
--surface-1: 241 242 245; /* #f1f2f5 */
|
||
--surface-2: 247 248 250; /* #f7f8fa */
|
||
--surface-3: 253 253 254; /* #fdfdfe */
|
||
--surface-4: 255 255 255; /* #ffffff */
|
||
--surface-5: 255 255 255; /* #ffffff */
|
||
--border: 211 213 219; /* #d3d5db */
|
||
--border-light: 228 230 235; /* #e4e6eb */
|
||
--accent: 40 90 192; /* #285ac0 */
|
||
--accent-hover: 33 74 158; /* #214a9e */
|
||
--fg-primary: 30 31 34; /* #1e1f22 */
|
||
--fg-secondary: 73 75 87; /* #494b57 */
|
||
--fg-muted: 103 106 120; /* #676a78 */
|
||
--shadow-card: 0 1px 2px rgb(30 31 34 / 0.05), 0 2px 6px rgb(30 31 34 / 0.06);
|
||
--status-success: 23 106 45; /* #176a2d */
|
||
--status-danger: 173 28 38; /* #ad1c26 */
|
||
--status-warning: 124 82 0; /* #7c5200 */
|
||
--syn-comment: 112 112 112; /* #707070 */
|
||
--syn-string: 6 125 23; /* #067d17 */
|
||
--syn-number: 23 80 235; /* #1750eb */
|
||
--syn-keyword: 0 51 179; /* #0033b3 */
|
||
--syn-builtin: 0 0 128; /* #000080 */
|
||
--syn-function: 0 98 122; /* #00627a */
|
||
--syn-operator: 75 75 75; /* #4b4b4b */
|
||
--syn-punctuation: 75 75 75; /* #4b4b4b */
|
||
--syn-property: 135 16 148; /* #871094 */
|
||
--syn-tag: 0 51 179; /* #0033b3 */
|
||
--syn-add: 6 125 23; /* #067d17 */
|
||
--syn-del: 192 31 42; /* #c01f2a */
|
||
}
|
||
|
||
[data-mode="light"][data-theme="canvas"] {
|
||
--surface-0: 227 220 205; /* #e3dccd */
|
||
--surface-1: 232 226 212; /* #e8e2d4 */
|
||
--surface-2: 237 232 220; /* #ede8dc */
|
||
--surface-3: 243 239 230; /* #f3efe6 */
|
||
--surface-4: 247 244 237; /* #f7f4ed */
|
||
--surface-5: 250 248 243; /* #faf8f3 */
|
||
--border: 203 191 166; /* #cbbfa6 */
|
||
--border-light: 220 211 192; /* #dcd3c0 */
|
||
--accent: 119 80 47; /* #77502f */
|
||
--accent-hover: 102 63 34; /* #663f22 */
|
||
--fg-primary: 43 38 32; /* #2b2620 */
|
||
--fg-secondary: 94 85 74; /* #5e554a */
|
||
--fg-muted: 104 94 82; /* #685e52 */
|
||
--status-success: 54 93 36; /* #365d24 */
|
||
--status-danger: 147 41 31; /* #93291f */
|
||
--status-warning: 109 76 0; /* #6d4c00 */
|
||
--syn-comment: 125 114 98; /* #7d7262 */
|
||
--syn-string: 77 107 31; /* #4d6b1f */
|
||
--syn-number: 154 91 0; /* #9a5b00 */
|
||
--syn-keyword: 138 59 18; /* #8a3b12 */
|
||
--syn-builtin: 47 94 110; /* #2f5e6e */
|
||
--syn-function: 122 90 0; /* #7a5a00 */
|
||
--syn-operator: 138 48 80; /* #8a3050 */
|
||
--syn-punctuation: 107 97 85; /* #6b6155 */
|
||
--syn-property: 59 102 96; /* #3b6660 */
|
||
--syn-tag: 142 47 47; /* #8e2f2f */
|
||
--syn-add: 77 107 31; /* #4d6b1f */
|
||
--syn-del: 159 47 35; /* #9f2f23 */
|
||
}
|
||
|
||
/* Orca Light — Orca's neutral palette (`:root`). The surfaces rise with their
|
||
* index, so the page sits below a #fafafa card instead of Orca's white-on-white;
|
||
* status colours are two steps darker than Orca's so each label clears 4.5:1 on
|
||
* its own /20 chip. Syntax colours are Apple Light's. */
|
||
[data-mode="light"][data-theme="orca-light"] {
|
||
--surface-0: 235 235 235; /* #ebebeb */
|
||
--surface-1: 240 240 240; /* #f0f0f0 */
|
||
--surface-2: 245 245 245; /* #f5f5f5 */
|
||
--surface-3: 250 250 250; /* #fafafa */
|
||
--surface-4: 253 253 253; /* #fdfdfd */
|
||
--surface-5: 255 255 255; /* #ffffff */
|
||
--border: 229 229 229; /* #e5e5e5 */
|
||
--border-light: 212 212 212; /* #d4d4d4 */
|
||
--accent: 23 23 23; /* #171717 */
|
||
--accent-hover: 38 38 38; /* #262626 */
|
||
--fg-primary: 10 10 10; /* #0a0a0a */
|
||
--fg-secondary: 64 64 64; /* #404040 */
|
||
--fg-muted: 105 105 105; /* #696969 */
|
||
--status-success: 22 101 52; /* #166534 */
|
||
--status-danger: 168 0 15; /* #a8000f */
|
||
--status-warning: 133 77 14; /* #854d0e */
|
||
--syn-comment: 93 108 121; /* #5d6c79 */
|
||
--syn-string: 196 26 22; /* #c41a16 */
|
||
--syn-number: 28 0 207; /* #1c00cf */
|
||
--syn-keyword: 155 35 147; /* #9b2393 */
|
||
--syn-builtin: 11 79 121; /* #0b4f79 */
|
||
--syn-function: 50 109 116; /* #326d74 */
|
||
--syn-operator: 75 75 80; /* #4b4b50 */
|
||
--syn-punctuation: 75 75 80; /* #4b4b50 */
|
||
--syn-property: 57 0 160; /* #3900a0 */
|
||
--syn-tag: 155 35 147; /* #9b2393 */
|
||
--syn-add: 31 122 54; /* #1f7a36 */
|
||
--syn-del: 197 0 15; /* #c5000f */
|
||
}
|
||
|
||
@layer base {
|
||
* {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgb(var(--border)) rgb(var(--surface-2));
|
||
}
|
||
|
||
*::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
*::-webkit-scrollbar-track {
|
||
background: rgb(var(--surface-2));
|
||
}
|
||
|
||
*::-webkit-scrollbar-thumb {
|
||
background: rgb(var(--border));
|
||
border-radius: 3px;
|
||
}
|
||
|
||
*::-webkit-scrollbar-thumb:hover {
|
||
background: rgb(var(--border-light));
|
||
}
|
||
|
||
::selection {
|
||
background: rgb(var(--accent) / 0.3);
|
||
color: rgb(var(--fg-primary));
|
||
}
|
||
|
||
::-webkit-calendar-picker-indicator {
|
||
cursor: pointer;
|
||
opacity: 0.7;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
/* The browser draws this glyph near-black. Inverting it is a DARK-mode fix;
|
||
applied globally it painted a white icon onto a white field. */
|
||
.dark ::-webkit-calendar-picker-indicator {
|
||
filter: invert(1) brightness(0.8);
|
||
}
|
||
|
||
::-webkit-calendar-picker-indicator:hover {
|
||
opacity: 1;
|
||
}
|
||
|
||
.time-input-custom::-webkit-calendar-picker-indicator {
|
||
display: none;
|
||
-webkit-appearance: none;
|
||
}
|
||
}
|
||
|
||
@layer components {
|
||
.card {
|
||
@apply bg-surface-3 border border-border rounded-xl;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.card-hover {
|
||
@apply card transition-all duration-200 hover:border-border-light hover:bg-surface-4;
|
||
}
|
||
|
||
/* Buttons. Every variant shares one keyboard focus ring (`focus-visible`
|
||
only, so a mouse click draws none). `.btn-primary` fills with
|
||
`accent-solid`, the shade white text clears 4.5:1 on in every theme —
|
||
never `bg-accent` behind white text. */
|
||
.btn-primary {
|
||
@apply inline-flex items-center gap-2 px-4 py-2 bg-accent-solid hover:bg-accent-solid-hover text-white text-sm font-medium rounded-lg transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-50;
|
||
}
|
||
|
||
.btn-secondary {
|
||
@apply inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-fg-primary bg-surface-2 border border-border-light hover:bg-surface-4 rounded-lg transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 disabled:cursor-not-allowed disabled:opacity-50;
|
||
}
|
||
|
||
.btn-danger {
|
||
@apply inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-danger bg-status-danger/10 border border-status-danger/40 hover:bg-status-danger/20 rounded-lg transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-status-danger/60 disabled:cursor-not-allowed disabled:opacity-50;
|
||
}
|
||
|
||
.btn-ghost {
|
||
@apply inline-flex items-center gap-2 px-3 py-1.5 text-sm text-fg-secondary hover:text-fg-primary hover:bg-surface-4 rounded-lg transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 disabled:cursor-not-allowed disabled:opacity-50;
|
||
}
|
||
|
||
/* An icon-only button: a 28px target (WCAG 2.5.8 asks ≥24px). It has no
|
||
text, so give it an `aria-label` (its `title` becomes the tooltip). */
|
||
.btn-icon {
|
||
@apply inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-fg-secondary hover:text-fg-primary hover:bg-surface-4 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 disabled:cursor-not-allowed disabled:opacity-50;
|
||
}
|
||
|
||
.badge {
|
||
@apply inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full border whitespace-nowrap shrink-0;
|
||
}
|
||
|
||
.input {
|
||
@apply bg-surface-2 border border-border rounded-lg px-3 py-2 text-sm text-fg-primary placeholder-fg-muted focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent/30 transition-colors;
|
||
}
|
||
|
||
/* A native <select>, themed: the browser's arrow is replaced by one drawn in
|
||
fg-muted (the native one is near-black, invisible on a dark field), and the
|
||
open list follows the mode through `color-scheme`. */
|
||
.select {
|
||
@apply input appearance-none cursor-pointer pr-8 bg-no-repeat disabled:cursor-not-allowed disabled:opacity-60;
|
||
background-position: right 0.6rem center;
|
||
background-size: 0.75rem;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23888' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5l3 3 3-3'/%3E%3C/svg%3E");
|
||
}
|
||
}
|
||
|
||
/* The open list of a native <select> and the date picker follow the mode. */
|
||
:root,
|
||
[data-mode="light"] {
|
||
color-scheme: light;
|
||
}
|
||
.dark {
|
||
color-scheme: dark;
|
||
}
|
||
|
||
/* Vestibular safety (WCAG 2.3.3): with the OS asking for reduced motion, every
|
||
animation and transition collapses to an instant change — the pulsing
|
||
status dots, `animate-ping`, fade/slide-in, spinners included (a spinner
|
||
still shows; it just stops turning). Outside any layer so it beats the
|
||
utilities. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.01ms !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
}
|
||
|
||
@layer utilities {
|
||
/* Hide the scrollbar on horizontally scrollable strips (e.g. the Settings
|
||
TOC) where chevron affordances replace it. */
|
||
.no-scrollbar {
|
||
scrollbar-width: none;
|
||
}
|
||
.no-scrollbar::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
/* Visually hidden until focused — first Tab lands here before the sidebar. */
|
||
.skip-to-content {
|
||
position: absolute;
|
||
left: 0.75rem;
|
||
top: 0.75rem;
|
||
z-index: 100;
|
||
padding: 0.5rem 0.75rem;
|
||
border-radius: 0.5rem;
|
||
border: 1px solid rgb(var(--border-light));
|
||
background: rgb(var(--surface-2));
|
||
color: rgb(var(--fg-primary));
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
transform: translateY(-150%);
|
||
transition: transform 0.15s ease;
|
||
}
|
||
.skip-to-content:focus {
|
||
transform: translateY(0);
|
||
outline: 2px solid rgb(var(--accent));
|
||
outline-offset: 2px;
|
||
}
|
||
}
|
||
|
||
@keyframes pulse-dot {
|
||
0%,
|
||
100% {
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
opacity: 0.4;
|
||
}
|
||
}
|
||
|
||
.animate-pulse-dot {
|
||
animation: pulse-dot 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||
}
|
||
|
||
/* Rendered markdown (MarkdownPreview.tsx): theme tokens only, so every theme
|
||
reads it; no typography plugin. */
|
||
.md-preview {
|
||
color: rgb(var(--fg-secondary));
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.md-preview > :first-child {
|
||
margin-top: 0;
|
||
}
|
||
.md-preview h1,
|
||
.md-preview h2,
|
||
.md-preview h3,
|
||
.md-preview h4,
|
||
.md-preview h5,
|
||
.md-preview h6 {
|
||
color: rgb(var(--fg-primary));
|
||
font-weight: 600;
|
||
line-height: 1.3;
|
||
margin: 1.4em 0 0.6em;
|
||
}
|
||
.md-preview h1 {
|
||
font-size: 1.6em;
|
||
padding-bottom: 0.3em;
|
||
border-bottom: 1px solid rgb(var(--border));
|
||
}
|
||
.md-preview h2 {
|
||
font-size: 1.3em;
|
||
padding-bottom: 0.25em;
|
||
border-bottom: 1px solid rgb(var(--border));
|
||
}
|
||
.md-preview h3 {
|
||
font-size: 1.12em;
|
||
}
|
||
.md-preview h4,
|
||
.md-preview h5,
|
||
.md-preview h6 {
|
||
font-size: 1em;
|
||
}
|
||
.md-preview p,
|
||
.md-preview ul,
|
||
.md-preview ol,
|
||
.md-preview blockquote,
|
||
.md-preview pre,
|
||
.md-preview table {
|
||
margin: 0 0 0.9em;
|
||
}
|
||
.md-preview ul {
|
||
list-style: disc;
|
||
padding-left: 1.5em;
|
||
}
|
||
.md-preview ol {
|
||
list-style: decimal;
|
||
padding-left: 1.5em;
|
||
}
|
||
.md-preview li + li {
|
||
margin-top: 0.2em;
|
||
}
|
||
.md-preview li > ul,
|
||
.md-preview li > ol {
|
||
margin: 0.2em 0 0;
|
||
}
|
||
.md-preview li.task-list-item {
|
||
list-style: none;
|
||
margin-left: -1.3em;
|
||
}
|
||
.md-preview li.task-list-item input {
|
||
margin-right: 0.4em;
|
||
vertical-align: middle;
|
||
}
|
||
.md-preview a {
|
||
color: rgb(var(--accent));
|
||
text-decoration: underline;
|
||
text-underline-offset: 2px;
|
||
}
|
||
.md-preview strong {
|
||
color: rgb(var(--fg-primary));
|
||
font-weight: 600;
|
||
}
|
||
.md-preview code {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 0.88em;
|
||
padding: 0.1em 0.35em;
|
||
border-radius: 4px;
|
||
background: rgb(var(--surface-3));
|
||
color: rgb(var(--fg-primary));
|
||
}
|
||
.md-preview pre,
|
||
.md-preview .md-frontmatter {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
padding: 0.75em 1em;
|
||
border-radius: 6px;
|
||
border: 1px solid rgb(var(--border));
|
||
background: rgb(var(--surface-0));
|
||
overflow-x: auto;
|
||
white-space: pre;
|
||
}
|
||
.md-preview .md-frontmatter {
|
||
margin: 0 0 1em;
|
||
color: rgb(var(--fg-muted));
|
||
}
|
||
.md-preview pre code {
|
||
padding: 0;
|
||
background: none;
|
||
font-size: inherit;
|
||
}
|
||
.md-preview blockquote {
|
||
padding: 0 1em;
|
||
border-left: 3px solid rgb(var(--border));
|
||
color: rgb(var(--fg-muted));
|
||
}
|
||
.md-preview hr {
|
||
margin: 1.5em 0;
|
||
border: 0;
|
||
border-top: 1px solid rgb(var(--border));
|
||
}
|
||
.md-preview table {
|
||
display: block;
|
||
max-width: 100%;
|
||
overflow-x: auto;
|
||
border-collapse: collapse;
|
||
}
|
||
.md-preview th,
|
||
.md-preview td {
|
||
padding: 0.35em 0.75em;
|
||
border: 1px solid rgb(var(--border));
|
||
}
|
||
.md-preview th {
|
||
color: rgb(var(--fg-primary));
|
||
font-weight: 600;
|
||
background: rgb(var(--surface-2));
|
||
}
|
||
.md-preview img {
|
||
max-width: 100%;
|
||
}
|
||
|
||
/* The run console's terminal attaches to tmux, which repaints the whole pane
|
||
on every frame — xterm's own scrollback stays empty forever, so its
|
||
viewport scrollbar renders as a groove with no thumb that never moves.
|
||
Scrolling there goes through mouse reports to the pane's program, not the
|
||
DOM, so hide the dead affordance. Deliberately OUTSIDE `@layer utilities`:
|
||
Tailwind purges a layered rule whose class never appears in the source, and
|
||
xterm creates `.xterm-viewport` at runtime. */
|
||
.xterm-viewport {
|
||
scrollbar-width: none;
|
||
}
|
||
.xterm-viewport::-webkit-scrollbar {
|
||
display: none;
|
||
}
|