@import "tailwindcss"; /* Bundled icon-only fallback so terminals show Nerd Font glyphs (powerline, devicons, etc.) regardless of which base font the user picks. The font is referenced near the end of the fontFamily fallback chain composed by composeFontFamilyStack() in infrastructure/config/cjkFonts.ts — base text comes from the user's chosen font, missing PUA glyphs fall through to this face. Source: https://github.com/ryanoasis/nerd-fonts (NerdFontsSymbolsOnly, v3.4.0). License: MIT — see public/fonts/SymbolsNerdFont-LICENSE.txt. */ @font-face { font-family: "Symbols Nerd Font Mono"; /* Absolute path resolves against the document origin in both dev (http://localhost:5173) and packaged (app://netcatty), regardless of where the bundled CSS file ends up. A relative ./fonts/... would be resolved against dist/assets/index-*.css in production and 404. */ src: url("/fonts/SymbolsNerdFontMono-Regular.ttf") format("truetype"); font-weight: normal; font-style: normal; font-display: block; } /* Bundled true-monospace CJK fallback. macOS ships only proportional CJK fonts (PingFang, Hiragino) whose glyphs aren't designed to fit a terminal's 2x cell grid — see #931. Sarasa Mono SC (Iosevka + Source Han Sans, OFL-1.1) is a 2:1 metrically-correct CJK monospace and becomes the per-OS default + per-Latin-font recommended pairing in cjkFonts.ts. Subsetted woff2 (~4.8 MB) covers ASCII, CJK Unified Ideographs (main block), Hiragana/Katakana, common punctuation and symbols; rarer Ext-A/B characters fall through to the system fallback stack. Source: https://github.com/be5invis/Sarasa-Gothic (v1.0.37, OFL-1.1). License: see public/fonts/SarasaMono-LICENSE.txt. */ @font-face { font-family: "Sarasa Mono SC"; src: url("/fonts/SarasaMonoSC-Regular.woff2") format("woff2"); font-weight: normal; font-style: normal; font-display: swap; } /* ============================================ Tailwind CSS v4 Theme Configuration ============================================ */ @theme { /* Default border color - ensures 'border' class uses theme color instead of currentColor */ --default-border-color: var(--color-border); /* Colors - mapped from CSS variables */ --color-border: hsl(var(--border)); --color-input: hsl(var(--input)); --color-ring: hsl(var(--ring)); --color-background: hsl(var(--background)); --color-foreground: hsl(var(--foreground)); --color-primary: hsl(var(--primary)); --color-primary-foreground: hsl(var(--primary-foreground)); --color-secondary: hsl(var(--secondary)); --color-secondary-foreground: hsl(var(--secondary-foreground)); --color-destructive: hsl(var(--destructive)); --color-destructive-foreground: hsl(var(--destructive-foreground)); --color-muted: hsl(var(--muted)); --color-muted-foreground: hsl(var(--muted-foreground)); --color-accent: hsl(var(--accent)); --color-accent-foreground: hsl(var(--accent-foreground)); --color-popover: hsl(var(--popover)); --color-popover-foreground: hsl(var(--popover-foreground)); --color-card: hsl(var(--card)); --color-card-foreground: hsl(var(--card-foreground)); /* Border Radius */ --radius-lg: var(--radius); --radius-md: calc(var(--radius) - 2px); --radius-sm: calc(var(--radius) - 4px); /* Fonts */ --font-sans: "Space Grotesk", system-ui, sans-serif; --font-mono: "JetBrains Mono", monospace; /* Animations */ --animate-accordion-down: accordion-down 0.2s ease-out; --animate-accordion-up: accordion-up 0.2s ease-out; --animate-fade-in: fade-in 0.2s ease-out; @keyframes accordion-down { from { height: 0; } to { height: var(--radix-collapsible-content-height); } } @keyframes accordion-up { from { height: var(--radix-collapsible-content-height); } to { height: 0; } } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } } @keyframes top-tab-enter { from { opacity: 0; transform: translateX(-12px); } to { opacity: 1; transform: translateX(0); } } .top-tab-enter { animation: top-tab-enter 220ms cubic-bezier(0.4, 0, 0.2, 1) both; } .top-tab-root-label { display: inline-block; overflow: hidden; white-space: nowrap; vertical-align: middle; max-width: 4.5rem; opacity: 1; transition: max-width 220ms cubic-bezier(0.4, 0, 0.2, 1), opacity 220ms ease, margin 220ms cubic-bezier(0.4, 0, 0.2, 1); } .top-tab-root-label-compact { max-width: 0; opacity: 0; margin-left: 0; } .top-tab-host-tree-toggle-slot { display: flex; flex-shrink: 0; overflow: hidden; transition: width 220ms cubic-bezier(0.4, 0, 0.2, 1), opacity 220ms ease; width: 0; opacity: 0; } .top-tab-host-tree-toggle-slot[data-visible='true'] { width: 1.75rem; opacity: 1; } .top-tab-host-tree-gutter { pointer-events: none; } .top-tab-host-tree-gutter-exit { transition: width 220ms cubic-bezier(0.4, 0, 0.2, 1); } .terminal-topbar { container-type: inline-size; } .terminal-title-cluster { min-width: 8rem; } @container (max-width: 760px) { .terminal-server-stats { display: none; } } @container (max-width: 420px) { .terminal-title-cluster { min-width: 0; } } .host-tree-notes-scroll { scrollbar-width: thin; scrollbar-color: hsl(var(--muted-foreground) / 0.28) transparent; } .host-tree-notes-scroll::-webkit-scrollbar { width: 8px; } .host-tree-notes-scroll::-webkit-scrollbar-track { background: transparent; } .host-tree-notes-scroll::-webkit-scrollbar-thumb { min-height: 28px; border: 2px solid transparent; border-radius: 999px; background-color: hsl(var(--muted-foreground) / 0.18); background-clip: content-box; } .host-tree-notes-scroll:hover::-webkit-scrollbar-thumb { background-color: hsl(var(--muted-foreground) / 0.36); } .vault-drop-indicator-row { position: relative; } [data-vault-reorder-grid="true"] { will-change: transform; transition-property: opacity, box-shadow, border-color, background-color; transition-duration: 150ms; } [data-vault-reorder-dragging="true"] { opacity: 0.45; } .vault-drop-indicator-row::before, .vault-drop-indicator-row::after { content: ""; position: absolute; left: var(--vault-drop-indicator-left, 0.5rem); right: var(--vault-drop-indicator-right, 0.5rem); z-index: 30; height: 2px; border-radius: 999px; background: hsl(var(--primary)); box-shadow: 0 0 0 1px hsl(var(--background)), 0 0 12px hsl(var(--primary) / 0.36); opacity: 0; pointer-events: none; transform: scaleX(0.96); transition: opacity 90ms ease, transform 140ms cubic-bezier(0.2, 0, 0, 1); } .vault-drop-indicator-row::before { top: -1px; } .vault-drop-indicator-row::after { bottom: -1px; } .vault-drop-indicator-row[data-vault-drop-axis="x"]::before, .vault-drop-indicator-row[data-vault-drop-axis="x"]::after { top: var(--vault-drop-indicator-top, 0.5rem); bottom: var(--vault-drop-indicator-bottom, 0.5rem); left: auto; right: auto; width: 2px; height: auto; transform: scaleY(0.96); } .vault-drop-indicator-row[data-vault-drop-axis="x"]::before { left: -1px; } .vault-drop-indicator-row[data-vault-drop-axis="x"]::after { right: -1px; } .vault-drop-indicator-row[data-vault-drop-position="before"]::before, .vault-drop-indicator-row[data-vault-drop-position="after"]::after { opacity: 1; transform: scaleX(1); } .vault-drop-indicator-row[data-vault-drop-axis="x"][data-vault-drop-position="before"]::before, .vault-drop-indicator-row[data-vault-drop-axis="x"][data-vault-drop-position="after"]::after { transform: scaleY(1); } .vault-drop-indicator-row[data-vault-drop-position="inside"] { outline: 1px solid hsl(var(--primary) / 0.55); outline-offset: -1px; background-color: hsl(var(--primary) / 0.08); } @keyframes ripple { 0% { transform: scale(0); opacity: 0.35; } 100% { transform: scale(1); opacity: 0; } } @keyframes split-panel-enter { 0% { width: 0; min-width: 0; opacity: 0; } 55% { opacity: 0.88; } 100% { width: var(--aside-inline-width); min-width: var(--aside-inline-width); opacity: 1; } } .split-panel-enter { animation: split-panel-enter 220ms cubic-bezier(0.24, 0.84, 0.32, 1) both; will-change: width, opacity; } :root { color-scheme: light; } /* Global default border color - ensures 'border' class always uses theme color */ *, *::before, *::after { border-color: hsl(var(--border)); } body { min-height: 100vh; font-family: var(--font-sans); background-color: hsl(var(--background)); color: hsl(var(--foreground)); } ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 220ms; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } html[data-theme-transition], html[data-theme-transition] *, html[data-theme-transition] *::before, html[data-theme-transition] *::after { transition-property: background-color, color, border-color, outline-color, fill, stroke, text-decoration-color; transition-duration: 220ms; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @media (prefers-reduced-motion: reduce) { ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.01ms; } html[data-theme-transition], html[data-theme-transition] *, html[data-theme-transition] *::before, html[data-theme-transition] *::after { transition-duration: 0.01ms; } } .dark { color-scheme: dark; } .dark body { background-color: hsl(var(--background)); color: hsl(var(--foreground)); } .netcatty-shell { position: relative; background: linear-gradient(180deg, hsl(var(--background)) 0%, hsl(var(--background)) 60%, hsl(var(--background) / 0.9) 100%); } /* Slim down xterm 6.0 VS Code scrollbar — wide hit area, thin visual slider */ .xterm .xterm-scrollable-element > .scrollbar.vertical { width: 12px !important; } .xterm .xterm-scrollable-element > .scrollbar.vertical > .slider { width: 6px !important; border-radius: 3px; left: 3px !important; } /* Keep focused IME helper text from making the terminal content drift sideways. */ .xterm, .xterm .xterm-screen, .xterm .xterm-viewport, .xterm .xterm-scrollable-element { overflow-x: hidden !important; } @supports (overflow: clip) { .xterm, .xterm .xterm-screen, .xterm .xterm-viewport, .xterm .xterm-scrollable-element { overflow-x: clip !important; } } @keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } .thinking-shimmer { background: linear-gradient( 90deg, currentColor 0%, currentColor 40%, rgba(255, 255, 255, 0.6) 50%, currentColor 60%, currentColor 100% ); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; animation: thinking-shimmer 1.5s ease-in-out infinite; } @keyframes thinking-shimmer { 0% { background-position: 100% center; } 100% { background-position: -100% center; } } @keyframes progress-shimmer { 0% { transform: translateX(-200%); } 100% { transform: translateX(200%); } } .glass-panel { background: hsl(var(--secondary) / 0.95); border: 1px solid hsl(var(--border) / 0.8); box-shadow: 0 14px 40px hsl(var(--foreground) / 0.12); } .soft-card { background: hsl(var(--card)); border: 1px solid hsl(var(--border) / 0.8); box-shadow: 0 4px 12px hsl(var(--foreground) / 0.06); } @keyframes session-activity-breathe { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.18; } } .session-activity-dot { animation: session-activity-breathe 1.4s ease-in-out infinite; } .card-highlight { position: relative; overflow: hidden; } .card-highlight::before { content: ""; position: absolute; inset: -40%; background: radial-gradient(500px circle at 20% 20%, hsl(var(--primary) / 0.18), transparent 55%); opacity: 0; transition: opacity 180ms ease; } .card-highlight:hover::before { opacity: 1; } .elevate { transition: transform 140ms ease, border-color 140ms ease; } .elevate:hover { transform: translateY(-2px); } ::selection { background: hsl(var(--primary) / 0.22); color: hsl(var(--foreground)); } /* Port Forwarding Traffic Animation */ @keyframes traffic-flow { 0% { stroke-dashoffset: 24; } 100% { stroke-dashoffset: 0; } } @keyframes traffic-flow-reverse { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 24; } } @keyframes pulse-glow { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } } .traffic-line { stroke-dasharray: 8 4; animation: traffic-flow 0.8s linear infinite; } .traffic-line-reverse { stroke-dasharray: 8 4; animation: traffic-flow-reverse 0.8s linear infinite; } .traffic-glow { animation: pulse-glow 1.5s ease-in-out infinite; } /* Custom titlebar drag regions for Electron */ .app-drag { -webkit-app-region: drag; app-region: drag; user-select: none; -webkit-user-select: none; } .app-no-drag { -webkit-app-region: no-drag; app-region: no-drag; user-select: auto; -webkit-user-select: auto; } .app-no-drag * { -webkit-app-region: no-drag; app-region: no-drag; user-select: auto; -webkit-user-select: auto; } /* Window controls: icons sit in the h-7 tab row; hover fills the full h-9 title bar */ [data-section="top-tabs"] .window-control-btn { position: relative; z-index: 0; display: flex; height: 1.75rem; width: 2.5rem; align-items: center; justify-content: center; border-radius: 0; color: var(--top-tabs-muted, hsl(var(--muted-foreground))); transition: color 150ms ease; } [data-section="top-tabs"] .window-control-btn::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; top: calc(1.75rem - 2.25rem); z-index: -1; background: transparent; transition: background-color 150ms ease; } [data-section="top-tabs"] .window-control-btn:hover::before { background: hsl(var(--foreground) / 0.1); } [data-section="top-tabs"] .window-control-btn--close:hover { color: white; } [data-section="top-tabs"] .window-control-btn--close:hover::before { background: rgb(239 68 68); } [data-section="top-tabs"] .top-tab-utility-btn:hover, [data-section="top-tabs"] .top-tab-utility-btn[data-state="open"] { background-color: hsl(var(--foreground) / 0.1); color: var(--top-tabs-muted, hsl(var(--muted-foreground))); } .no-scrollbar { scrollbar-width: none; -ms-overflow-style: none; } .no-scrollbar::-webkit-scrollbar { display: none; } .workspace-pane { outline: none; overflow: visible; } /* Focus-mode terminal list sidebar (split view uses terminal-split-pane). */ [data-section="terminal-workspace-sidebar"] { border-right: var(--terminal-workspace-sidebar-border, none); } /* Dim terminal text in unfocused workspace panes (default) */ .workspace-pane:not(:focus-within) .xterm-screen { opacity: 0.82; } /* Keep current pane fully visible while its context menu / popover is open; focus moves to the menu portal and would otherwise drop :focus-within. */ .workspace-pane[data-menu-open] .xterm-screen { opacity: 1; } /* Border-style focus indicator (opt-in via data attribute) */ [data-workspace-focus="border"] .workspace-pane:not(:focus-within) .xterm-screen { opacity: 1; } [data-workspace-focus="border"] .workspace-pane::after { content: ""; position: absolute; inset: 0; border: 2px solid transparent; pointer-events: none; transition: border-color 120ms ease; z-index: 40; } [data-workspace-focus="border"] .workspace-pane:focus-within::after { border-color: hsl(var(--primary)); } /* ── Streamdown code block overrides ── */ [data-streamdown="code-block"] { position: relative !important; border-radius: 10px !important; background: hsl(var(--muted) / 0.5) !important; overflow: hidden !important; margin: 6px 0 !important; padding: 0 !important; border: none !important; gap: 0 !important; } [data-streamdown="code-block-header"] { height: auto !important; padding: 4px 12px 0 !important; font-size: 11px !important; } [data-streamdown="code-block-header"] span { margin-left: 0 !important; } [data-streamdown="code-block"] > div:has(> [data-streamdown="code-block-actions"]) { position: absolute !important; top: 4px !important; right: 4px !important; z-index: 10 !important; display: flex !important; width: auto !important; height: auto !important; margin: 0 !important; pointer-events: none !important; } [data-streamdown="code-block-actions"] { position: static !important; border: none !important; background: none !important; backdrop-filter: none !important; padding: 0 !important; gap: 2px !important; opacity: 0; transition: opacity 0.15s; } [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"] { opacity: 1; } [data-streamdown="code-block-actions"] button { padding: 4px !important; border-radius: 4px; } [data-streamdown="code-block-body"] { border: none !important; border-radius: 0 !important; background: transparent !important; padding: 0 !important; margin: 0 !important; overflow-x: auto !important; overflow-y: hidden !important; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; font-size: 0 !important; /* collapse whitespace text nodes */ } [data-streamdown="code-block-body"] pre { font-size: 12px !important; /* restore in pre */ } [data-streamdown="code-block"] pre { display: block !important; margin: 0 !important; background: transparent !important; border: none !important; border-radius: 0 !important; padding: 6px 12px 10px !important; width: max-content !important; min-width: 100% !important; font-size: 12px !important; line-height: 1.5 !important; white-space: pre !important; } /* Streamdown table overrides */ .ai-chat-message-content[data-role="assistant"] [data-streamdown="table-wrapper"] { gap: 4px !important; padding: 4px 8px 8px !important; } .ai-chat-message-content[data-role="assistant"] [data-streamdown="table-wrapper"] > div:first-child > button, .ai-chat-message-content[data-role="assistant"] [data-streamdown="table-wrapper"] > div:first-child > div > button { padding: 3px !important; }