AUDIT / src /index.css
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
40.7 kB
/* S437: import gerarchia z-index — variabili --z-* disponibili globalmente */
@import './styles/z-index.css';
/* S_THEME: theme.css source per --card-bg-streaming, .stream-progress-bar,
--terminal-*, .agent-chip e token mancanti da index.css.
index.css :root viene DOPO → sovrascrive token duplicati (Glow Glass prevale). */
@import './styles/theme.css';
/* =========================================================
GLOBAL RESET + TYPOGRAPHY
min-width: 0 globale previene flex shrink collapse
========================================================= */
*,
*::before,
*::after {
box-sizing: border-box;
min-width: 0;
}
html, body, #root {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
-webkit-text-size-adjust: 100%;
}
/* iOS: html deve bloccare lo scroll del documento root */
html {
overflow: hidden;
height: var(--vh, 100dvh);
}
/* =========================================================
STRUCTURAL TOKENS — radius, type stacks, transitions
Color tokens live in theme.css (Glow Glass canonical source)
========================================================= */
:root {
/* ─��� Font stacks ── */
--font-display: "Geist", "Inter", sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", "Fira Code", monospace;
--font-sans: Inter, system-ui, -apple-system, sans-serif;
/* ── Radius — tight square corners ── */
--radius-sm: 6px;
--radius-md: 8px;
--radius-lg: 8px; /* P10-CSS: IDE-style compact */
--radius-xl: 10px; /* P10-CSS: IDE-style compact */
--radius-2xl: 16px; /* P10-CSS: ridotto da 24px */
/* ── Transitions ── */
--transition-fast: 130ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-normal: 220ms cubic-bezier(0.4, 0, 0.2, 1);
/* G5: base font size token */
--base-font-size: 16px;
}
/* =========================================================
BUG #3: Focus outline — remove browser default dashed/yellow ring
Use :focus-visible (keyboard only) for accessibility compliance
Safari: outline: none on :focus is safe — Safari uses :focus-visible natively
========================================================= */
*:focus {
outline: none;
}
*:focus-visible {
outline: 2px solid rgba(99, 102, 241, 0.55);
outline-offset: 2px;
border-radius: 4px;
}
/* Touch/mobile users never need keyboard focus ring */
@media (hover: none) {
*:focus-visible {
outline: none;
}
}
/* G5+G7: mobile-first overrides */
@media (max-width: 767px) {
:root {
--base-font-size: 14px;
}
body {
font-size: var(--base-font-size);
}
/* G7: sidebar overlay full width on mobile */
.sidebar-overlay > div {
width: calc(100vw - 24px) !important;
max-width: calc(100vw - 24px) !important;
}
}
/* ── Glass cards — glow edition (S250-GLASS) ── */
.glass-card {
background: rgba(8, 10, 28, 0.58); /* Zen: semi-trasparente — il body gradient si intravede */
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow:
0 0 0 1px rgba(99,102,241,0.08),
0 8px 32px rgba(0, 0, 0, 0.50),
inset 0 1px 0 rgba(255, 255, 255, 0.06),
inset 0 -1px 0 rgba(0,0,0,0.3);
border-radius: var(--radius-lg);
backdrop-filter: blur(8px) saturate(120%); /* P10-CSS: ridotto, mobile già disabilitato da @media */
-webkit-backdrop-filter: blur(8px) saturate(120%);
}
.glass-card:hover {
border-color: rgba(99, 102, 241, 0.36);
box-shadow:
0 0 0 1px rgba(99,102,241,0.18),
0 0 20px 0 rgba(99,102,241,0.12),
0 8px 32px rgba(0, 0, 0, 0.50),
inset 0 1px 0 rgba(255, 255, 255, 0.06);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.glass-card:active {
will-change: transform;
transform: scale(0.97);
transition: transform 0.12s ease;
}
/* Glassmorphism — backdrop-filter attivo, sfondo intravisto (Zen/Manus) */
.glass-surface {
background: rgba(6, 8, 22, 0.58); /* Zen: 0.94 → 0.58 */
border: 1px solid rgba(99, 102, 241, 0.18);
box-shadow:
0 0 0 1px rgba(0,212,255,0.06),
0 8px 40px rgba(0, 0, 0, 0.65);
}
/* =========================================================
BODY — Deep space background with subtle radial depth
========================================================= */
body {
background:
radial-gradient(ellipse at 15% 0%, rgba(99, 102, 241, 0.12) 0%, transparent 48%),
radial-gradient(ellipse at 85% 5%, rgba(139, 92, 246, 0.08) 0%, transparent 42%),
radial-gradient(ellipse at 50% 100%, rgba(0, 212, 255, 0.07) 0%, transparent 50%),
radial-gradient(ellipse at 90% 60%, rgba(99, 102, 241, 0.05) 0%, transparent 38%),
#04050e;
color: var(--text);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
height: var(--vh, 100dvh);
overflow: hidden;
overscroll-behavior: none;
}
/* =========================================================
MAIN APP LAYOUT
========================================================= */
.app-layout {
display: flex;
width: 100%;
height: var(--vh, 100dvh);
overflow: hidden;
background: transparent;
}
#root {
width: 100%;
height: var(--vh, 100dvh);
display: flex;
}
/* =========================================================
SIDEBAR DESKTOP — refined futuristic border
========================================================= */
.sidebar-desktop {
flex-shrink: 0;
width: 260px;
border-right: 1px solid rgba(99, 102, 241, 0.18);
box-shadow:
2px 0 32px -4px rgba(0, 0, 0, 0.70),
1px 0 0 0 rgba(99, 102, 241, 0.08),
inset -1px 0 0 rgba(0, 212, 255, 0.04);
background: rgba(5, 6, 16, 0.82); /* Zen: più trasparente */
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(160%);
will-change: transform; /* GAP-8: forza GPU compositing layer — backdrop-filter safe su Safari */
z-index: 10;
}
[data-theme="light"] .sidebar-desktop {
background: var(--bg2);
box-shadow: 2px 0 16px -4px rgba(0, 0, 0, 0.10);
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
background: transparent;
position: relative;
overflow: hidden;
height: var(--vh, 100dvh);
}
/* =========================================================
CONV-ROW — ⋯ menu button visibility (S253)
Desktop: hidden, shown on row hover or when menu open
Touch devices: always visible at 45% opacity (no hover available)
========================================================= */
.conv-row .conv-menu-btn { opacity: 0; transition: opacity 0.15s; }
.conv-row:hover .conv-menu-btn { opacity: 0.7; }
.conv-row:hover .conv-menu-btn:hover { opacity: 1; background: rgba(59,130,246,0.08) !important; }
/* Touch / Mobile: no hover state → always show */
@media (hover: none) {
.conv-row .conv-menu-btn { opacity: 0.45 !important; }
}
/* Sidebar mobile overlay */
.sidebar-overlay {
position: fixed;
inset: 0;
z-index: var(--z-modal-backdrop);
background: rgba(0, 0, 0, 0.72); /* G2-S202: darker solid bg replaces expensive blur */
/* backdrop-filter removed — G2-S202: was expensive on mobile Safari */
display: flex;
animation: overlay-fade-in 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}
.sidebar-overlay > div {
animation: sidebar-slide-in 0.24s cubic-bezier(0.16, 1, 0.3, 1);
}
/* ── S86-UX: Chat card animations ─────────────────────────────────────── */
@keyframes message-slide-in {
from { opacity: 0; transform: translateY(10px) scale(0.985); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes bounce-dot {
0%, 80%, 100% { transform: scale(0.55); opacity: 0.35; }
40% { transform: scale(1); opacity: 1; }
}
@keyframes stream-border-pulse {
0%, 100% {
border-color: rgba(0,212,255,0.22);
box-shadow:
0 0 0 1px rgba(0,212,255,0.08),
0 4px 20px rgba(0,0,0,0.38);
}
50% {
border-color: rgba(0,212,255,0.60);
box-shadow:
0 0 0 1px rgba(0,212,255,0.20),
0 0 28px 0 rgba(0,212,255,0.22),
0 0 56px 0 rgba(99,102,241,0.10),
0 4px 20px rgba(0,0,0,0.38);
}
}
@keyframes waveform-bar {
0%, 100% { transform: scaleY(0.4); opacity: 0.5; }
50% { transform: scaleY(1.0); opacity: 1.0; }
}
/* CSS class applied to AI card while streaming */
.chat-card-streaming {
animation: stream-border-pulse 2.2s ease-in-out infinite;
}
/* S313-perf: content-visibility — browser skips rendering off-screen messages
Riduce il tempo di paint su conversazioni lunghe del ~60% su mobile */
.chat-message-item {
content-visibility: auto;
contain-intrinsic-size: 0 90px;
}
@keyframes overlay-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes sidebar-slide-in {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
/* ── Tab pane fade+lift on switch ── */
.tab-pane-active {
animation: tab-fade-in 0.18s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes tab-fade-in {
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
/* =========================================================
SCROLLBAR — minimal, indigo tint
========================================================= */
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
::-webkit-scrollbar-thumb {
background: rgba(99,102,241,0.28);
border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(99,102,241,0.50);
box-shadow: 0 0 6px rgba(99,102,241,0.4);
}
::-webkit-scrollbar-track {
background: transparent;
}
/* =========================================================
CHAT INPUT CONTAINER — focus glow (CSS-only, no JS)
========================================================= */
.chat-input-container {
max-width: 700px;
margin: 0 auto;
display: flex;
flex-direction: column;
border-radius: var(--radius-xl);
background: rgba(8, 10, 28, 0.90);
border: 1px solid rgba(99, 102, 241, 0.22);
overflow: visible;
backdrop-filter: none;
-webkit-backdrop-filter: none;
box-shadow:
0 0 0 1px rgba(99,102,241,0.06),
0 8px 32px rgba(0,0,0,0.40),
inset 0 1px 0 rgba(255,255,255,0.05);
transition: border-color var(--transition-normal), box-shadow var(--transition-normal);
}
.chat-input-container:focus-within {
border-color: rgba(99, 102, 241, 0.55);
box-shadow:
0 0 0 3px rgba(99, 102, 241, 0.14),
0 0 32px 0 rgba(99, 102, 241, 0.18),
0 0 64px 0 rgba(0, 212, 255, 0.08),
0 8px 32px rgba(0,0,0,0.40),
inset 0 1px 0 rgba(255,255,255,0.06);
}
/* =========================================================
COMMIT BADGE — monospace futuristic
========================================================= */
.commit-badge {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.62rem;
padding: 3px 7px;
border-radius: var(--radius-sm);
background: rgba(99,102,241,0.06);
border: 1px solid rgba(99,102,241,0.16);
color: var(--text-muted);
text-decoration: none;
font-family: var(--font-mono);
letter-spacing: 0.04em;
transition: border-color var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast);
}
.commit-badge:hover {
border-color: rgba(0, 212, 255, 0.35);
color: var(--accent);
box-shadow: 0 0 8px rgba(0,212,255,0.18);
}
/* =========================================================
CODE / PRE
========================================================= */
pre, code {
overflow-x: auto;
font-family: var(--font-mono);
}
/* =========================================================
ANIMAZIONI — GPU-safe (opacity + box-shadow only)
Tutte rispettano prefers-reduced-motion
========================================================= */
/* Glow che respira lentamente — tab attivo, elementi chiave */
@keyframes neon-breathe {
0%, 100% { text-shadow: 0 0 6px rgba(0, 212, 255, 0.25); }
50% { text-shadow: 0 0 14px rgba(0, 212, 255, 0.55), 0 0 4px rgba(59,130,246,0.3); }
}
/* Pulsazione dot streaming — più elettrico */
@keyframes brain-glow {
0%, 100% { filter: drop-shadow(0 0 2px rgba(59,130,246,0.2)); }
50% { filter: drop-shadow(0 0 10px rgba(0, 212, 255, 0.6)); }
}
@keyframes neuron-pulse {
0%, 100% { opacity: 0.3; transform: scale(0.7); }
50% { opacity: 1; transform: scale(1.3); }
}
@keyframes neuron-line-pulse {
0%, 100% { opacity: 0.08; }
50% { opacity: 0.5; }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Dot streaming — più cyan, più elettrico */
@keyframes pulse-dot {
0%, 100% { opacity: 1; transform: scale(1); box-shadow: 0 0 4px var(--accent); }
50% { opacity: 0.3; transform: scale(0.7); box-shadow: 0 0 2px transparent; }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.35; }
}
@keyframes chip-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
@keyframes dot-blink {
0%, 100% { opacity: 0.15; }
50% { opacity: 1; }
}
@keyframes chip-fadein {
from { opacity: 0; transform: translateY(3px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse-ring {
0% { transform: scale(1); opacity: 0.6; }
100% { transform: scale(1.6); opacity: 0; }
}
@keyframes mem-tab-pulse {
0% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 rgba(59,130,246,0.7); }
50% { transform: scale(1.2); opacity: 0.85; box-shadow: 0 0 0 4px rgba(59,130,246,0); }
100% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 rgba(59,130,246,0.7); }
}
@keyframes blink-cursor {
0%, 100% { opacity: 0.8; }
50% { opacity: 0; }
}
/* =========================================================
P42-REPLIT — Replit-style streaming text effect
- replit-para-reveal : fade-in per nuovi <p> durante streaming
- replit-chunk-flash : flash cyan al cursore ogni ~22 char
Zero layout-thrashing: solo opacity (GPU-accelerated)
========================================================= */
@keyframes replit-para-reveal {
from { opacity: 0; }
to { opacity: 1; }
}
/* Ogni nuovo <p> dentro una card in streaming appare con fade-in */
.chat-card-streaming p,
.chat-card-streaming li {
animation: replit-para-reveal 0.30s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes replit-chunk-flash {
0% { opacity: 0.85; width: 5px; }
60% { opacity: 0.25; width: 3px; }
100% { opacity: 0; width: 0; }
}
/* Span effimero — rimontato ogni ~22 char di streaming per dare il pop Replit */
.replit-chunk-glow {
display: inline-block;
height: 0.85em;
width: 0;
border-radius: 2px;
background: rgba(0, 212, 255, 0.55);
vertical-align: middle;
margin-left: 1px;
animation: replit-chunk-flash 0.42s ease-out forwards;
pointer-events: none;
flex-shrink: 0;
}
/* Shimmer skeleton — più freddo/cyan */
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
/* Glow pulse — input / card attive */
@keyframes glow-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0); }
50% {
box-shadow:
0 0 22px 2px rgba(99,102,241,0.22),
0 0 8px 0 rgba(0, 212, 255, 0.14);
}
}
/* Glow breathe — per elementi accent (logo, avatar, dot attivo) */
@keyframes glow-breathe {
0%, 100% {
box-shadow: 0 0 4px rgba(0,212,255,0.20), 0 0 12px rgba(99,102,241,0.10);
}
50% {
box-shadow: 0 0 16px rgba(0,212,255,0.55), 0 0 32px rgba(99,102,241,0.25), 0 0 48px rgba(139,92,246,0.12);
}
}
/* Indigo glow for buttons */
@keyframes indigo-glow-pulse {
0%, 100% { box-shadow: 0 0 8px rgba(99,102,241,0.25); }
50% { box-shadow: 0 0 20px rgba(99,102,241,0.55), 0 0 40px rgba(99,102,241,0.18); }
}
@keyframes avatar-pulse {
0%, 100% { opacity: 1; filter: drop-shadow(0 0 0 rgba(0, 212, 255, 0)); }
50% { opacity: 0.7; filter: drop-shadow(0 0 5px rgba(0, 212, 255, 0.45)); }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Scan line futuristica — solo per decorazione leggera su desktop */
@keyframes scan-line {
0% { transform: translateY(-100%); opacity: 0; }
10% { opacity: 0.025; }
90% { opacity: 0.025; }
100% { transform: translateY(200%); opacity: 0; }
}
/* =========================================================
PROSE — Markdown (ChatMessage)
========================================================= */
.prose {
color: var(--text, #e4e4f0);
font-size: 0.92rem;
line-height: 1.72;
word-break: break-word;
overflow-wrap: anywhere;
}
.prose p { margin-bottom: 1.2rem; }
.prose p:last-child { margin-bottom: 0; }
/* S739: heading typography — erano completamente assenti, titoli Markdown invisibili */
.prose h1, .prose h2, .prose h3, .prose h4 {
font-weight: 700;
line-height: 1.3;
margin-top: 1.5rem;
margin-bottom: 0.55rem;
color: var(--text-bright, #f0f0ff);
}
.prose h1:first-child, .prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose h1 { font-size: 1.45rem; }
.prose h2 { font-size: 1.22rem; border-bottom: 1px solid rgba(99,102,241,0.18); padding-bottom: 0.2rem; }
.prose h3 { font-size: 1.06rem; }
.prose h4 { font-size: 0.97rem; font-weight: 600; }
/* S739: liste, citazioni, tabelle, link */
.prose ul, .prose ol { padding-left: 1.4rem; margin-bottom: 1rem; }
.prose li { margin-bottom: 0.28rem; }
.prose li > ul, .prose li > ol { margin-bottom: 0; margin-top: 0.2rem; }
.prose blockquote {
border-left: 3px solid var(--accent, #6366f1);
padding: 0.1rem 0 0.1rem 0.9rem;
margin: 1rem 0;
color: var(--text-muted, #a0a0b8);
font-style: italic;
}
.prose hr { border: none; border-top: 1px solid var(--border-indigo); margin: 1.4rem 0; }
.prose a { color: #818cf8; text-decoration: underline; text-underline-offset: 2px; }
.prose a:hover { color: #a5b4fc; }
.prose strong { font-weight: 700; color: var(--text-bright, #f0f0ff); }
.prose em { font-style: italic; }
.prose table { width: 100%; border-collapse: collapse; margin-bottom: 1rem; font-size: 0.87em; }
.prose th {
background: rgba(99,102,241,0.12);
padding: 0.38rem 0.7rem;
border: 1px solid var(--border-indigo);
text-align: left;
font-weight: 600;
color: var(--text-bright, #f0f0ff);
}
.prose td { padding: 0.38rem 0.7rem; border: 1px solid rgba(99,102,241,0.12); }
.prose tr:nth-child(even) td { background: rgba(99,102,241,0.04); }
.prose pre {
border-radius: 10px;
overflow-x: auto;
font-size: 0.82em;
background: rgba(8, 8, 20, 0.8);
border: 1px solid var(--border-indigo);
}
.prose code:not(pre code) {
background: rgba(59,130,246,0.1);
border: 1px solid rgba(59,130,246,0.15);
border-radius: var(--radius-sm);
padding: 1px 5px;
font-size: 0.85em;
color: #93c5fd;
font-family: var(--font-mono);
}
/* =========================================================
AGENT TIMELINE — AgentStepsPanel
========================================================= */
.agent-step-card {
border-radius: 10px;
overflow: hidden;
transition: border-color var(--transition-normal);
}
.agent-step-card-running {
border-color: rgba(0, 212, 255, 0.25);
background: rgba(0, 212, 255, 0.03);
}
.agent-step-card-done {
border-color: rgba(74, 222, 128, 0.12);
}
.agent-step-card-error {
border-color: rgba(239, 68, 68, 0.28);
background: rgba(239, 68, 68, 0.03);
}
/* =========================================================
INPUT AREA — bottom safe area iOS
========================================================= */
.input-area {
flex-shrink: 0;
border-top: 1px solid rgba(99, 102, 241, 0.20);
box-shadow: 0 -1px 0 0 rgba(0, 212, 255, 0.06), 0 -8px 32px rgba(99,102,241,0.04);
background: rgba(4, 5, 14, 0.96);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
will-change: transform; /* GAP-8: compositing layer per backdrop-filter corretto durante scroll */
padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
}
/* =========================================================
iOS / SAFARI PERFORMANCE
========================================================= */
/* S243: iOS Safari — DISABLE backdrop-filter (correzione: era un bug che aggiungeva blur invece di rimuoverlo) */
/* S437: aggiunto [aria-live] per AgentLiveBar (che usa backdropFilter inline) */
/* S741: aggiunto .input-area — blur(20px) durante scrolling era il principale bottleneck su iPhone */
.ios .sidebar-desktop,
.ios .glass,
.ios [aria-live] { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
.ios .token-overlay { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
/* S741: .input-area usa blur(20px) — visibile sempre durante scroll → rimosso su iOS */
/* .network-banner light-theme ha blur(14px) ma solo su [data-theme="light"] → impatto minore, invariato */
.ios .chat-input-glass { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; } /* GAP-8: mancava */
.ios .network-banner { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; } /* GAP-8: mancava */
.ios .input-area { backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
background: rgba(4, 5, 14, 0.99) !important; }
/* =========================================================
MOBILE
========================================================= */
@media (max-width: 768px) {
.sidebar-desktop { display: none !important; }
.main-content { width: 100%; height: var(--vh, 100dvh); }
}
/* =========================================================
iOS SAFE AREA fallback
========================================================= */
@supports (-webkit-touch-callout: none) {
.main-content { height: var(--vh, 100dvh); }
}
/* =========================================================
MOBILE KEYBOARD FIX
========================================================= */
textarea {
touch-action: manipulation; /* previene zoom doppio tap e migliora risposta iOS */
}
textarea:focus {
scroll-margin-bottom: 0; /* rimosso: causava scroll jump aggiuntivo su Safari */
outline: none;
}
.messages-scroll {
scroll-padding-bottom: 80px;
overscroll-behavior-y: contain;
}
/* =========================================================
SUGGESTION CARDS
========================================================= */
.premium-suggestion-card {
transition: background var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast) !important;
border: 1px solid rgba(99,102,241,0.14);
border-radius: var(--radius-md) !important;
}
.premium-suggestion-card:hover {
border-color: rgba(99,102,241,0.38);
box-shadow:
0 0 0 1px rgba(99,102,241,0.12),
0 0 16px 0 rgba(99,102,241,0.12),
0 4px 16px rgba(0,0,0,0.28);
background: rgba(99,102,241,0.06) !important;
}
/* =========================================================
GPU LAYER HINTS — parsimoniosi
========================================================= */
[style*="pulse-dot"],
[style*="spin-slow"],
[style*="pulse-ring"] {
will-change: transform, opacity;
}
/* =========================================================
PREFERS-REDUCED-MOTION — iPhone "Riduci movimento"
========================================================= */
@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;
}
@keyframes fadeIn {
from { opacity: 1; transform: none; }
to { opacity: 1; transform: none; }
}
@keyframes fadeInUp {
from { opacity: 1; transform: none; }
to { opacity: 1; transform: none; }
}
.chat-input-container:focus-within {
box-shadow: none;
}
}
/* =========================================================
DESKTOP ONLY — background depth grid (very subtle)
Solo su schermi > 768px, non impatta mobile/iPhone
========================================================= */
@media (min-width: 769px) {
body::after {
content: '';
position: fixed;
inset: 0;
background-image:
linear-gradient(rgba(99, 102, 241, 0.028) 1px, transparent 1px),
linear-gradient(90deg, rgba(99, 102, 241, 0.028) 1px, transparent 1px);
background-size: 42px 42px;
pointer-events: none;
z-index: 0;
}
.app-layout, .main-content, .sidebar-desktop {
position: relative;
z-index: 1;
}
}
/* =========================================================
MIGLIORIA 1 — Waveform bars TypingIndicator
========================================================= */
@keyframes waveform-bar {
0%, 100% { transform: scaleY(0.3); opacity: 0.4; }
50% { transform: scaleY(1); opacity: 1; }
}
/* =========================================================
MIGLIORIA 4 — Cinematic message entrance
========================================================= */
@keyframes message-slide-in {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* =========================================================
MIGLIORIA 5 — EmptyState logo ring glow pulse
========================================================= */
@keyframes neon-ring-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(0,212,255,0), 0 8px 24px rgba(59,130,246,0.12); }
50% { box-shadow: 0 0 0 8px rgba(0,212,255,0.06), 0 8px 32px rgba(59,130,246,0.22), 0 0 40px rgba(0,212,255,0.08); }
}
/* Boot flicker — per EmptyState title */
@keyframes boot-flicker {
0% { opacity: 0; }
10% { opacity: 0.9; }
12% { opacity: 0.2; }
14% { opacity: 1; }
100% { opacity: 1; }
}
/* Staggered fade for EmptyState cards */
@keyframes card-enter {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* =========================================================
TEMA CHIARO — S_THEME (light/dark/auto)
Attivato dalla classe data-theme="light" su <html>
========================================================= */
[data-theme="light"] {
--bg: #f2f3fc;
--bg2: #e8eaf7;
--bg3: #dfe1f4;
--bg4: #d5d7ee;
--bg-surface: #e4e6f5;
--border: rgba(0, 0, 0, 0.08);
--border-indigo: rgba(99,102,241,0.28);
--border-cyan: rgba(0, 150, 200, 0.26);
--border-glow: rgba(99, 102, 241, 0.45);
--text: #0c0d24;
--text-muted: #42446a;
--text-dim: #7070a0;
--primary: #6366f1;
--primary-light: #818cf8;
--primary-dark: #4f46e5;
--accent: #0096c7;
--accent-dim: rgba(0, 150, 200, 0.12);
--accent-glow: rgba(0, 150, 200, 0.30);
--glow-indigo: rgba(99,102,241,0.18);
--glow-cyan: rgba(0, 150, 200, 0.14);
--glow-violet: rgba(139,92,246,0.15);
--avatar-primary: #6366f1;
--avatar-accent: #0096c7;
--narration-text: rgba(80, 60, 220, 0.75);
/* footer button vars — S232 light theme */
--footer-btn-color: #4a4a6a;
--footer-btn-bg: rgba(0,0,0,0.04);
--footer-btn-border: rgba(0,0,0,0.12);
/* ── Chat message card — light theme ── */
--card-bg: rgba(255, 255, 255, 0.90);
--card-bg-error: rgba(255, 240, 240, 0.95);
--card-border-streaming: rgba(0, 150, 200, 0.38);
--card-border-idle: rgba(0, 0, 0, 0.10);
--card-border-error: rgba(239, 68, 68, 0.32);
--card-shadow-streaming: 0 0 0 1px rgba(0,150,200,0.12), 0 4px 24px rgba(0,0,0,0.10);
--card-shadow-idle: 0 2px 12px rgba(0,0,0,0.08);
/* ── User bubble — light theme ── */
--user-bubble-bg: rgba(59,130,246,0.16);
--user-bubble-text: #0a1f3d;
--user-bubble-border: rgba(59,130,246,0.32);
}
/* ── NetworkBanner — light theme overrides (bg più opaco per leggibilità) ── */
[data-theme="light"] .network-banner {
background: rgba(200, 200, 220, 0.72) !important;
backdrop-filter: blur(14px) saturate(140%);
-webkit-backdrop-filter: blur(14px) saturate(140%);
}
[data-theme="light"] .network-banner[data-quality="offline"],
[data-theme="light"] .network-banner[data-quality="poor"] {
background: rgba(254, 226, 226, 0.85) !important;
}
[data-theme="light"] .network-banner[data-quality="reconnected"] {
background: rgba(220, 252, 231, 0.85) !important;
}
[data-theme="light"] body {
background:
radial-gradient(ellipse at 15% 0%, rgba(99, 102, 241, 0.10) 0%, transparent 48%),
radial-gradient(ellipse at 85% 5%, rgba(139, 92, 246, 0.06) 0%, transparent 42%),
radial-gradient(ellipse at 50% 100%, rgba(0, 150, 200, 0.07) 0%, transparent 50%),
#eeeff9;
color: var(--text);
}
/* ── Glass input bar + sidebar menu — glow edition ── */
.chat-input-glass {
background: rgba(6, 7, 20, 0.90);
border: 1px solid rgba(99, 102, 241, 0.22);
backdrop-filter: none;
-webkit-backdrop-filter: none;
box-shadow:
0 -4px 32px rgba(0,0,0,0.38),
0 -1px 0 rgba(0,212,255,0.06),
inset 0 1px 0 rgba(255,255,255,0.05);
}
[data-theme="light"] .chat-input-glass {
background: rgba(242, 244, 255, 0.94);
border: 1px solid rgba(99,102,241,0.18);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
will-change: transform; /* GAP-8 */
box-shadow: 0 -4px 24px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.9);
}
.sidebar-glass {
background: rgba(5, 6, 16, 0.96);
border-right: 1px solid rgba(99,102,241,0.18);
box-shadow:
2px 0 40px rgba(0,0,0,0.50),
inset -1px 0 0 rgba(0,212,255,0.04);
}
[data-theme="light"] .sidebar-glass {
background: rgba(246, 246, 255, 0.96);
border-right: 1px solid rgba(99,102,241,0.12);
box-shadow: 2px 0 18px rgba(0,0,0,0.05);
}
[data-theme="light"] .sidebar-desktop {
background: rgba(245, 246, 255, 0.96);
border-right: 1px solid rgba(37, 99, 235, 0.12);
box-shadow: 2px 0 20px rgba(37,99,235,0.05);
}
/* S313-iOS: keyboard height compensation — set dynamically by ChatInputBar visualViewport */
:root {
--keyboard-height: 0px;
}
/* =========================================================
S752-FE — Modern touch: gradient brand + micro-interactions
========================================================= */
/* Agent brand gradient — indigo→cyan, usato nella SidebarHeader */
.agent-brand-gradient {
background: linear-gradient(90deg,
var(--primary-light) 0%,
var(--accent) 65%,
var(--primary) 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 700;
letter-spacing: -0.025em;
will-change: auto;
}
/* Send button active scale — micro-feedback su tap/click */
.send-action-btn:active {
transform: scale(0.82) !important;
transition: transform 0.07s ease !important;
}
/* New-chat button pulse on hover (Replit-style) */
.new-chat-btn:hover {
box-shadow:
0 0 0 3px rgba(99,102,241,0.18),
0 6px 20px rgba(99,102,241,0.30) !important;
}
.new-chat-btn:active {
transform: scale(0.97) !important;
}
/* Conv-row active: indigo left indicator bar */
.conv-row-active::before {
content: '';
position: absolute;
left: 0;
top: 20%;
height: 60%;
width: 2px;
border-radius: 0 2px 2px 0;
background: var(--primary);
opacity: 0.9;
}
/* =========================================================
GAP-7: Safari Performance Gate — mobile backdrop-filter
backdrop-filter: blur() è MOLTO costoso su iPhone (Safari WebKit):
- Forza GPU compositing layer su ogni elemento con la proprietà
- Con molti elementi .glass-card in chat: frame drop visibili su A13 e inferiori
Fix: su max-width 768px (mobile) → disabilita backdrop-filter,
usa solid background come fallback visivamente accettabile.
ChatMessage.tsx è già wrapped in memo() → nessuna modifica necessaria.
content-visibility: auto su .chat-message-item già presente (S313-perf).
========================================================= */
@media (max-width: 768px) {
.glass-card {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: rgba(4, 5, 14, 0.94) !important;
}
.glass-surface {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: rgba(6, 8, 22, 0.96) !important;
}
.sidebar-desktop {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: rgba(5, 6, 16, 0.97) !important;
}
/* Mantieni will-change: transform per GPU compositing — necessario per smooth scroll */
.sidebar-desktop,
.glass-card {
will-change: transform;
}
}
/* =========================================================
P43-REPLIT: Typewriter reveal (risposte non-streaming)
- .replit-typewriter : ogni <p> appare con stagger 50ms
- .replit-stream-done : flash indigo al completamento streaming
GPU-safe: solo opacity + transform (compositor-only).
Rispetta prefers-reduced-motion.
========================================================= */
@keyframes replit-typewriter-reveal {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
/* Stagger per i primi 8 paragrafi, poi tutti a 350ms */
.replit-typewriter p:nth-child(1) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 0ms both; }
.replit-typewriter p:nth-child(2) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 55ms both; }
.replit-typewriter p:nth-child(3) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 110ms both; }
.replit-typewriter p:nth-child(4) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 165ms both; }
.replit-typewriter p:nth-child(5) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 220ms both; }
.replit-typewriter p:nth-child(6) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 275ms both; }
.replit-typewriter p:nth-child(7) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 330ms both; }
.replit-typewriter p:nth-child(n+8) { animation: replit-typewriter-reveal 0.32s cubic-bezier(0.16,1,0.3,1) 350ms both; }
/* Liste inline stagger */
.replit-typewriter li:nth-child(1) { animation: replit-typewriter-reveal 0.28s cubic-bezier(0.16,1,0.3,1) 30ms both; }
.replit-typewriter li:nth-child(2) { animation: replit-typewriter-reveal 0.28s cubic-bezier(0.16,1,0.3,1) 75ms both; }
.replit-typewriter li:nth-child(3) { animation: replit-typewriter-reveal 0.28s cubic-bezier(0.16,1,0.3,1) 120ms both; }
.replit-typewriter li:nth-child(4) { animation: replit-typewriter-reveal 0.28s cubic-bezier(0.16,1,0.3,1) 165ms both; }
.replit-typewriter li:nth-child(n+5) { animation: replit-typewriter-reveal 0.28s cubic-bezier(0.16,1,0.3,1) 200ms both; }
/* Completamento streaming: brightness flash indigo sottile */
@keyframes replit-stream-done {
0% { filter: brightness(1) drop-shadow(0 0 0px rgba(129,140,248,0)); }
20% { filter: brightness(1.06) drop-shadow(0 0 6px rgba(129,140,248,0.22)); }
100% { filter: brightness(1) drop-shadow(0 0 0px rgba(129,140,248,0)); }
}
.replit-stream-done {
animation: replit-stream-done 0.65s ease-out forwards;
}
/* Rispetta prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
.replit-typewriter p,
.replit-typewriter li,
.replit-stream-done {
animation: none !important;
opacity: 1 !important;
transform: none !important;
filter: none !important;
}
}
/* =========================================================
UI-MOB1 — Mobile header optimisation
========================================================= */
.chat-header-bar {
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
}
.header-badge-desktop { display: contents; }
@media (max-width: 768px) {
.chat-header-bar {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
.header-badge-desktop { display: none !important; }
}
/* =========================================================
REPLIT SPLIT LAYOUT — desktop: chat sx, workspace dx
S-SPLIT: pannelli affiancati sempre visibili su desktop
========================================================= */
.split-chat-pane {
width: 400px;
flex-shrink: 0;
display: flex;
flex-direction: column;
overflow: hidden;
border-right: 1px solid rgba(99, 102, 241, 0.18);
background: rgba(4, 5, 14, 0.97);
}
.split-divider {
width: 4px;
flex-shrink: 0;
cursor: col-resize;
background: rgba(99, 102, 241, 0.0);
display: flex;
align-items: center;
justify-content: center;
transition: background var(--transition-fast);
}
.split-divider::after {
content: "";
display: block;
width: 2px;
height: 48px;
border-radius: 9px;
background: rgba(99, 102, 241, 0.28);
transition: background var(--transition-fast), height var(--transition-fast);
}
.split-divider:hover {
background: rgba(99, 102, 241, 0.06);
}
.split-divider:hover::after {
background: rgba(99, 102, 241, 0.55);
height: 64px;
}
.split-workspace-pane {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
/* =========================================================
GAP-4/5: MOTION REDUCTION & MOBILE HOVER OPTIMIZATION
========================================================= */
@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;
}
.sidebar-overlay, .sidebar-overlay > div, .glass-card, .message-slide-in,
.fadeIn, .fadeInUp, .aml-glow, .replit-typewriter *, .replit-stream-done {
animation: none !important;
}
}
@media (hover: none) {
/* Disable hover effects on touch devices to prevent "sticky" hover states */
.glass-card:hover, .glass-surface:hover, .chat-header-bar:hover {
box-shadow: var(--shadow-default, none) !important;
border-color: rgba(255,255,255,0.1) !important;
transition: none !important;
}
button:hover { background: initial !important; }
.conv-row:hover .conv-menu-btn { opacity: 0.45 !important; }
}
@media (max-width: 768px) {
.chat-header-bar {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
will-change: auto;
}
}
/* =========================================================
GAP-6: TOUCH INTERACTION OPTIMIZATION
========================================================= */
a, button, [role="button"], .clickable {
touch-action: manipulation; /* Removes 300ms tap delay */
}
.glass-card, .conv-row, .sidebar-item {
user-select: none; /* Prevents accidental text selection during scroll */
-webkit-user-select: none;
}
/* Allow selection only in messages and inputs */
.message-content, input, textarea {
user-select: text !important;
-webkit-user-select: text !important;
}
/* Performance: content-visibility for off-screen sections */
.admin-panel-content, .settings-content {
content-visibility: auto;
contain-intrinsic-size: 0 500px;
}
/* S-MOBILE-ULTRA: iPhone Safari Optimization */
@media (max-width: 768px) {
/* DOM Flattening: Rimuove ombre e layer complessi su mobile */
* {
box-shadow: none !important;
text-shadow: none !important;
}
/* Semplifica le transizioni per evitare jank */
.chat-message, .tool-result {
transition: none !important;
will-change: auto !important;
}
/* Forza il rendering accelerato solo per lo scroll principale */
.main-content {
-webkit-overflow-scrolling: touch;
scroll-behavior: auto; /* Disabilita smooth scroll su mobile per performance */
}
/* Lazy Tool Rendering: Nasconde i dettagli dei tool di default su mobile */
.tool-details {
display: none;
}
.tool-details.expanded {
display: block;
}
}
/* =========================================================
MIGLIORIA 6 — Caret blink animation (GAP-UI-CARET)
========================================================= */
@keyframes caret-blink {
0%, 70%, 100% { opacity: 1; }
20%, 50% { opacity: 0; }
}
.animate-caret-blink {
animation: caret-blink 1.25s ease-in-out infinite;
}