@tailwind base; @tailwind components; @tailwind utilities; /* ── Bundled Nerd Font (Powerline / Oh-My-Zsh / p10k glyphs) ── */ @font-face { font-family: 'MesloLGS NF'; src: url('/fonts/MesloLGS NF Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: 'MesloLGS NF'; src: url('/fonts/MesloLGS NF Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; font-display: swap; } @font-face { font-family: 'MesloLGS NF'; src: url('/fonts/MesloLGS NF Italic.ttf') format('truetype'); font-weight: 400; font-style: italic; font-display: swap; } @font-face { font-family: 'MesloLGS NF'; src: url('/fonts/MesloLGS NF Bold Italic.ttf') format('truetype'); font-weight: 700; font-style: italic; font-display: swap; } /* =================================================================== * OpsXShell v1.4.0 — Professional Theme System * * Default (no data-theme) is dark mode for correct first paint. * useTheme.ts immediately sets data-theme + overrides vars on mount. * * Design: Warp-inspired warm dark + Linear luminance stacking * =================================================================== */ :root { /* ---- Dark (default, matches warm slate-navy palette) ---- */ --color-dark-900: #0f172a; --color-dark-800: #1e293b; --color-dark-700: #334155; --color-dark-600: #475569; /* Semantic surface tokens */ --color-surface-base: #0f172a; --color-surface-raised: #1e293b; --color-surface-overlay: #334155; --color-surface-inset: #475569; --color-surface-hover: #374151; --color-surface-active: #4b5563; /* Text */ --color-text-primary: #f1f5f9; --color-text-secondary: #cbd5e1; --color-text-muted: #94a3b8; --color-text-faint: #64748b; --color-text-dim: #475569; /* Content */ --color-content-primary: #f1f5f9; --color-content-secondary: #cbd5e1; --color-content-tertiary: #94a3b8; --color-content-inverse: #0f172a; /* Border — soft hairlines (not bright slate blocks) */ --color-border: rgba(148, 163, 184, 0.08); --color-border-subtle: rgba(148, 163, 184, 0.06); --color-border-default: rgba(148, 163, 184, 0.10); --color-border-strong: rgba(148, 163, 184, 0.16); --color-border-focus: #3b82f6; /* Legacy border-dark-* remaps (components still use dark-600/700 borders) */ --hairline-soft: rgba(148, 163, 184, 0.06); --hairline: rgba(148, 163, 184, 0.10); --hairline-strong: rgba(148, 163, 184, 0.16); /* Accent (+ RGB channels for Tailwind opacity modifiers) */ --color-accent-blue: #3b82f6; --color-accent-blue-rgb: 59 130 246; --color-accent-blue-hover: #60a5fa; --color-accent-blue-hover-rgb: 96 165 250; --color-accent-green: #10b981; --color-accent-green-rgb: 16 185 129; --color-accent-purple: #8b5cf6; --color-accent-purple-rgb: 139 92 246; --color-accent-orange: #f97316; --color-accent-orange-rgb: 249 115 22; --color-accent-red: #ef4444; --color-accent-red-rgb: 239 68 68; /* Feedback (+ RGB for Tailwind opacity) */ --color-feedback-success: #10b981; --color-feedback-success-rgb: 16 185 129; --color-feedback-success-bg: #064e3b; --color-feedback-warning: #f59e0b; --color-feedback-warning-rgb: 245 158 11; --color-feedback-warning-bg: #78350f; --color-feedback-error: #ef4444; --color-feedback-error-rgb: 239 68 68; --color-feedback-error-bg: #7f1d1d; --color-feedback-info: #3b82f6; --color-feedback-info-rgb: 59 130 246; --color-feedback-info-bg: #1e3a5f; /* Placeholder */ --color-placeholder: #64748b; --color-placeholder-disabled: #475569; /* UI chrome */ --color-scrollbar-track: #1e293b; --color-scrollbar-thumb: #475569; --color-scrollbar-thumb-h: #64748b; --color-shadow-color: rgba(0, 0, 0, 0.4); /* Terminal */ --color-terminal-bg: #0f172a; } * { margin: 0; padding: 0; box-sizing: border-box; } /* Light theme hairlines + chrome tweaks (useTheme also sets tokens) */ [data-theme='light'] { --color-border: rgba(15, 23, 42, 0.06); --color-border-subtle: rgba(15, 23, 42, 0.04); --color-border-default: rgba(15, 23, 42, 0.08); --color-border-strong: rgba(15, 23, 42, 0.12); --hairline-soft: rgba(15, 23, 42, 0.05); --hairline: rgba(15, 23, 42, 0.08); --hairline-strong: rgba(15, 23, 42, 0.12); /* Terminal chrome stays dark regardless of UI theme */ --color-terminal-bg: #0f172a; } [data-theme='light'] .ui-nav-item-active { box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06); } [data-theme='light'] .ui-select-pill { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); } /* * Soften legacy Tailwind border-dark-* / divide-dark-* classes. * Surface colors (bg-dark-*) stay unchanged — only border-color is remapped. */ @layer utilities { .border-dark-900, .border-dark-800 { border-color: var(--hairline-soft) !important; } .border-dark-700 { border-color: var(--hairline) !important; } .border-dark-600 { border-color: var(--hairline-strong) !important; } .border-dark-700\/20, .border-dark-700\/30, .border-dark-600\/30, .border-dark-800\/40, .border-dark-800\/50 { border-color: var(--hairline-soft) !important; } .border-dark-700\/35, .border-dark-700\/40, .border-dark-700\/45, .border-dark-700\/50, .border-dark-700\/60, .border-dark-700\/70, .border-dark-600\/40, .border-dark-600\/50, .border-dark-600\/60 { border-color: var(--hairline) !important; } .border-dark-600\/70, .border-dark-600\/80, .border-dark-500 { border-color: var(--hairline-strong) !important; } /* SFTP / list row separators — even softer */ .border-b.border-dark-700\/20, .border-t.border-dark-700\/20 { border-color: var(--hairline-soft) !important; } .divide-dark-700\/20 > :not([hidden]) ~ :not([hidden]), .divide-dark-700\/30 > :not([hidden]) ~ :not([hidden]), .divide-dark-700\/35 > :not([hidden]) ~ :not([hidden]), .divide-dark-700\/40 > :not([hidden]) ~ :not([hidden]), .divide-dark-700\/50 > :not([hidden]) ~ :not([hidden]), .divide-dark-600 > :not([hidden]) ~ :not([hidden]), .divide-dark-700 > :not([hidden]) ~ :not([hidden]) { border-color: var(--hairline-soft) !important; } .ring-dark-700, .ring-dark-600 { --tw-ring-color: var(--hairline) !important; } } html, body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: var(--color-surface-base) !important; color: var(--color-text-primary); overflow: hidden; } #root { width: 100vw; height: 100vh; } /* Window chrome utilities * Tauri: drag region must be large enough to move the window. * Interactive controls inside drag areas MUST set no-drag. */ .drag, .titlebar-drag { -webkit-app-region: drag; app-region: drag; } .no-drag, .titlebar-no-drag { -webkit-app-region: no-drag; app-region: no-drag; } /* SFTP cross-pane pointer drag — block native text selection while dragging files */ body.sftp-pointer-dragging, body.sftp-pointer-dragging * { -webkit-user-select: none !important; user-select: none !important; cursor: grabbing !important; } /* Buttons/inputs inside drag regions stay clickable */ .titlebar-drag button, .titlebar-drag a, .titlebar-drag input, .titlebar-drag select, .titlebar-drag textarea, .titlebar-drag [role='button'], .drag button, .drag a, .drag input { -webkit-app-region: no-drag; app-region: no-drag; } /* Custom scrollbar */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: var(--color-scrollbar-track); } ::-webkit-scrollbar-thumb { background: var(--color-scrollbar-thumb); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: var(--color-scrollbar-thumb-h); } /* Terminal styles — always dark canvas (independent of UI light/dark) */ .terminal-container, .terminal-surface { background-color: var(--color-terminal-bg); } .terminal-overlay-scrim { background-color: color-mix(in srgb, var(--color-terminal-bg) 92%, transparent); } /* XTerm.js styles */ .xterm { padding: 4px; } .xterm .xterm-viewport { background-color: transparent !important; } .xterm .xterm-screen { background-color: transparent !important; } /* Animation */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .animate-fade-in { animation: fadeIn 0.2s ease-in-out; } @keyframes scaleIn { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: scale(1); } } .animate-scale-in { animation: scaleIn 0.15s ease-out; } /* Slide animations */ @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } @keyframes slideInUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } @keyframes scaleIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } } @keyframes pulse-subtle { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } @keyframes pfDashFlow { to { stroke-dashoffset: -16; } } @keyframes pfTunnelPulse { 0%, 100% { opacity: 0.22; stroke-dashoffset: 0; } 50% { opacity: 1; stroke-dashoffset: -14; } } .pf-tunnel-dash { animation: pfTunnelPulse 1.35s ease-in-out infinite; } .animate-slide-in-right { animation: slideInRight 0.3s cubic-bezier(0.16, 1, 0.3, 1); } .animate-slide-out-right { animation: slideOutRight 0.2s ease-in forwards; } .animate-slide-in-up { animation: slideInUp 0.25s cubic-bezier(0.16, 1, 0.3, 1); } .animate-scale-in { animation: scaleIn 0.2s cubic-bezier(0.16, 1, 0.3, 1); } .animate-pulse-subtle { animation: pulse-subtle 2s ease-in-out infinite; } /* Focus ring */ button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--color-accent-blue); outline-offset: 2px; } button:focus:not(:focus-visible), a:focus:not(:focus-visible) { outline: none; } /* Button press feedback */ button:active:not(:disabled) { transform: scale(0.98); } button { transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease, transform 0.1s ease; } /* ── Form controls (use plain CSS: CSS-var colors don't support /opacity in @apply) ── */ .ui-label { display: block; font-size: 0.75rem; font-weight: 500; color: var(--color-text-muted); margin-bottom: 0.375rem; letter-spacing: 0.025em; } .ui-input, .ui-input-sm, .ui-select, .ui-textarea, .ui-select-pill { width: 100%; border-radius: 0.5rem; background-color: var(--color-surface-base); border: 1px solid var(--color-border-default); color: var(--color-content-primary); transition: border-color 0.15s ease, box-shadow 0.15s ease; } .ui-input, .ui-select, .ui-textarea { padding: 0.5rem 0.75rem; font-size: 0.875rem; } .ui-input-sm { padding: 0.375rem 0.75rem; font-size: 0.75rem; } .ui-input::placeholder, .ui-input-sm::placeholder, .ui-textarea::placeholder { color: var(--color-placeholder); } .ui-input:focus, .ui-input-sm:focus, .ui-select:focus, .ui-textarea:focus, .ui-select-pill:focus { outline: none; border-color: var(--color-accent-blue); box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.18); } .ui-input:disabled, .ui-input-sm:disabled { opacity: 0.5; cursor: not-allowed; } .ui-select { appearance: none; cursor: pointer; padding-right: 2rem; } .ui-textarea { resize: vertical; min-height: 80px; font-family: ui-monospace, Menlo, Monaco, Consolas, monospace; line-height: 1.625; } .ui-section { border-radius: 0.75rem; border: 1px solid var(--color-border-default); background-color: var(--color-surface-raised); padding: 1rem; } .ui-section-title { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-faint); } .ui-divider { border-top: 1px solid var(--color-border-default); } .ui-icon-box { width: 2.5rem; height: 2.5rem; border-radius: 0.75rem; display: flex; align-items: center; justify-content: center; flex-shrink: 0; background-color: color-mix(in srgb, var(--color-accent-blue) 12%, transparent); border: 1px solid color-mix(in srgb, var(--color-accent-blue) 22%, transparent); color: var(--color-accent-blue); } .ui-chip { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.125rem 0.5rem; border-radius: 0.375rem; font-size: 11px; font-weight: 500; background-color: var(--color-surface-overlay); color: var(--color-text-muted); border: 1px solid var(--color-border-default); } .ui-nav-item { width: 100%; display: flex; align-items: center; gap: 0.75rem; padding: 0.625rem 0.75rem; border-radius: 0.75rem; text-align: left; transition: background-color 0.15s ease, color 0.15s ease; color: var(--color-text-muted); border: 1px solid transparent; } .ui-nav-item:hover { background-color: var(--color-surface-overlay); color: var(--color-content-primary); } .ui-nav-item-active { background-color: color-mix(in srgb, var(--color-accent-blue) 12%, transparent); color: var(--color-content-primary); border-color: color-mix(in srgb, var(--color-accent-blue) 28%, transparent); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); } .launcher-card { display: flex; align-items: center; gap: 0.625rem; border-radius: 0.75rem; border: 1px solid var(--color-border-default); background: var(--color-surface-raised); padding: 0.75rem; text-align: left; color: var(--color-content-primary); } .launcher-card:hover, .launcher-row:hover { border-color: color-mix(in srgb, var(--color-accent-blue) 45%, transparent); background: var(--color-surface-overlay); } .launcher-card span, .launcher-row span { display: flex; min-width: 0; flex-direction: column; } .launcher-card strong, .launcher-row strong { font-size: 0.75rem; font-weight: 600; color: var(--color-content-primary); } .launcher-card small, .launcher-row small { margin-top: 0.125rem; font-size: 0.625rem; color: var(--color-text-faint); } .launcher-row { display: flex; width: 100%; align-items: center; gap: 0.625rem; border-radius: 0.625rem; border: 1px solid transparent; padding: 0.625rem 0.75rem; color: var(--color-content-primary); } .launcher-badge { display: inline-flex !important; flex-direction: row !important; flex-shrink: 0; border-radius: 0.375rem; background: var(--color-surface-overlay); padding: 0.125rem 0.375rem; font-size: 0.625rem; color: var(--color-text-muted); } /* Pill select — compact control on the right */ .ui-select-pill { width: auto; max-width: 200px; appearance: none; cursor: pointer; padding: 0.375rem 2rem 0.375rem 0.75rem; font-size: 0.75rem; border-radius: 0.75rem; background-color: var(--color-surface-base); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.6rem center; } /* Drawer enter from right */ @keyframes slideInRightDrawer { from { transform: translateX(100%); opacity: 0.6; } to { transform: translateX(0); opacity: 1; } } .animate-slide-in-right { animation: slideInRightDrawer 0.22s cubic-bezier(0.16, 1, 0.3, 1); } /* Skeleton shimmer */ @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } .animate-shimmer { background: linear-gradient(90deg, var(--color-surface-raised) 25%, var(--color-surface-overlay) 50%, var(--color-surface-raised) 75%); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; } /* SFTP dual-pane: drop trailing meta first so ordinary names stay readable */ .sftp-file-panel { container-type: inline-size; container-name: sftp-panel; } .sftp-file-panel .sftp-col-name { flex: 1 1 auto; min-width: 8rem; } @container sftp-panel (max-width: 480px) { .sftp-col-type { display: none; } } @container sftp-panel (max-width: 400px) { .sftp-col-size { display: none; } } @container sftp-panel (max-width: 320px) { .sftp-col-modified { display: none; } }