@import "shadcn/dist/tailwind.css"; @tailwind base; @tailwind components; @tailwind utilities; html, body, #root { height: 100%; } body { @apply bg-background text-foreground; } .reactflow-wrapper { width: 100%; height: 100%; min-height: 0; } /* Canvas pane cursors: crosshair for selection box, grab/grabbing for middle-click pan */ .react-flow__pane { cursor: crosshair; } .react-flow.react-flow--panning .react-flow__pane { cursor: grabbing; } .react-flow.react-flow--selecting .react-flow__pane { cursor: crosshair; } /* Node specific overrides */ .react-flow__node { display: block; overflow: visible; } /* Focused node: clear focus ring so keyboard/screen reader users see which node is active */ .react-flow__node:focus { outline: none; } .react-flow__node:focus-visible>div:first-child { border-color: hsl(var(--primary) / 0.5); box-shadow: 0 0 0 2px hsl(var(--primary) / 0.15); } .dark .react-flow__node:focus-visible>div:first-child { border-color: hsl(var(--primary) / 0.35); box-shadow: 0 0 0 2px hsl(var(--primary) / 0.1); } .react-flow__node .react-flow__handle { box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.06); } .dark .react-flow__node .react-flow__handle { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.15); } /* Connection validation: valid targets clearly highlighted (from drag start and when hovering), invalid dimmed while connecting */ .react-flow__handle.react-flow__handle-connecting.react-flow__handle-valid, .react-flow__handle.connection-valid-target { background: hsl(var(--primary)) !important; color: hsl(var(--primary-foreground)); box-shadow: 0 0 0 3px hsl(var(--primary)), 0 0 12px 2px hsl(var(--primary) / 0.5) !important; opacity: 1 !important; } .react-flow__handle.react-flow__handle-connecting.react-flow__handle-valid svg, .react-flow__handle.connection-valid-target svg { color: inherit; } .dark .react-flow__handle.react-flow__handle-connecting.react-flow__handle-valid, .dark .react-flow__handle.connection-valid-target { background: hsl(var(--primary)) !important; box-shadow: 0 0 0 3px hsl(var(--primary)), 0 0 14px 4px hsl(var(--primary) / 0.6) !important; } .react-flow__handle.react-flow__handle-connecting:not(.react-flow__handle-valid):not(.connection-valid-target), .react-flow__handle.connection-invalid-target { opacity: 0.25 !important; pointer-events: none; } /* Viewport control buttons: same look as React Flow controls; Lucide icons use stroke. */ .rendering-viewport .react-flow__controls-button svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } /* Rendering node: diagram/wireframe SVG must fit inside the content area. */ .rendering-diagram { display: flex; align-items: center; justify-content: center; } .rendering-diagram svg { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; } .dark .rendering-diagram svg { filter: invert(0.92) hue-rotate(180deg); } /* Wireframe: fit HTML preview in viewport (inspired by https://www.wireweave.org/ ). */ .rendering-wireframe__viewport { container-type: inline-size; container-name: wireframe; flex: 1; min-height: 0; overflow: auto; display: flex; justify-content: center; align-items: flex-start; } .rendering-wireframe__content { width: 1200px; /* Scale to fit container width so content stays within the node */ zoom: calc(100cqw / 1200px); min-height: min-content; } /* Animated React Flow edges: ant trail always on; color shows updating path */ .react-flow__edge path.animated-edge-path, .animated-edge-path { stroke-width: 4; stroke: hsl(var(--foreground) / 0.5); fill: none; stroke-dasharray: 8 6; stroke-dashoffset: 0; animation: edge-flow 0.5s linear infinite; will-change: stroke-dashoffset; transform: translateZ(0); backface-visibility: hidden; transition: stroke 0.2s ease-out; } .react-flow__edge path.animated-edge-path.animated-edge-path--updating, .animated-edge-path.animated-edge-path--updating { stroke: hsl(217 91% 60%); transition: none; } .react-flow__edge path.animated-edge-path.animated-edge-path--paused, .animated-edge-path.animated-edge-path--paused { stroke: hsl(45 98% 50%); transition: none; } .react-flow__edge path.animated-edge-path.animated-edge-path--error, .animated-edge-path.animated-edge-path--error { stroke: hsl(0 70% 50%); transition: none; } @keyframes edge-flow { 0% { stroke-dashoffset: 14; } 100% { stroke-dashoffset: 0; } } /* Connection path roles: pushing (trigger) — classes on BaseNode root. Updating nodes have no extra border so rendering doesn’t flash. */ /* Connection path trigger: amber cue to match manual/dirty state (e.g. Run button). */ .connection-path-trigger { /*box-shadow: inset 3px 0 0 hsl(38 92% 50%);*/ } .dark .connection-path-trigger { /*box-shadow: inset 3px 0 0 hsl(45 93% 58%); */ } /* NodeResizer: snappy drag; no border/background on handles; keep resize cursors. */ .react-flow__resize-control { touch-action: none; user-select: none; } .react-flow__resize-control, .react-flow__resize-control * { transition: none; } /* Subtle resize handles so they don’t compete with the selected-node ring (primary). Shown only when node is selected. */ /* Single bottom-right resize handle: no border, transparent so only the corner grab area remains. */ /* Handles: invisible (no border/background); cursor unchanged so resize cursors stay. */ .base-node-resize-handle, .base-node-resize-handle * { border: none !important; background: transparent !important; } /* Resize cursor per position (keep cursor style change on hover). */ .react-flow__resize-control.top.left { cursor: nw-resize; } .react-flow__resize-control.top.right { cursor: ne-resize; } .react-flow__resize-control.bottom.left { cursor: sw-resize; } .react-flow__resize-control.bottom.right { cursor: se-resize; } .react-flow__resize-control.top { cursor: n-resize; } .react-flow__resize-control.right { cursor: e-resize; } .react-flow__resize-control.bottom { cursor: s-resize; } .react-flow__resize-control.left { cursor: w-resize; } /* Small helper for monospace pre output */ pre { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Courier New', monospace; } /* Compact scrollbars that respect light/dark mode */ * { scrollbar-width: thin; scrollbar-color: hsl(var(--muted-foreground) / 0.35) transparent; } .dark * { scrollbar-color: hsl(var(--muted-foreground) / 0.4) transparent; } *::-webkit-scrollbar { width: 6px; height: 6px; } *::-webkit-scrollbar-track { background: transparent; } *::-webkit-scrollbar-thumb { background: hsl(var(--muted-foreground) / 0.35); border-radius: 3px; } *::-webkit-scrollbar-thumb:hover { background: hsl(var(--muted-foreground) / 0.5); } *::-webkit-scrollbar-corner { background: transparent; } /* Override Tailwind preflight: use theme border color instead of hardcoded #e5e7eb so nodes and sidebar borders respect the design system. */ *, ::before, ::after { border-color: hsl(var(--border)); } .dark *::-webkit-scrollbar-thumb { background: hsl(var(--muted-foreground) / 0.4); } .dark *::-webkit-scrollbar-thumb:hover { background: hsl(var(--muted-foreground) / 0.55); } @layer base { :root { --background: 0 0% 100%; --foreground: 0 0% 3.9%; --card: 0 0% 100%; --card-foreground: 0 0% 3.9%; --popover: 0 0% 100%; --popover-foreground: 0 0% 3.9%; --primary: 0 0% 9%; --primary-foreground: 0 0% 98%; --secondary: 0 0% 96.1%; --secondary-foreground: 0 0% 9%; --muted: 0 0% 96.1%; --muted-foreground: 0 0% 45.1%; --accent: 0 0% 96.1%; --accent-foreground: 0 0% 9%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 0 0% 89.8%; --input: 0 0% 89.8%; --ring: 0 0% 3.9%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; --sidebar-background: 0 0% 98%; --sidebar-foreground: 240 5.3% 26.1%; --sidebar-primary: 240 5.9% 10%; --sidebar-primary-foreground: 0 0% 98%; --sidebar-accent: 240 4.8% 95.9%; --sidebar-accent-foreground: 240 5.9% 10%; --sidebar-border: 220 13% 91%; --sidebar-ring: 217.2 91.2% 59.8%; } .dark { --background: 0 0% 3.9%; --foreground: 0 0% 98%; --card: 0 0% 3.9%; --card-foreground: 0 0% 98%; --popover: 0 0% 3.9%; --popover-foreground: 0 0% 98%; --primary: 0 0% 98%; --primary-foreground: 0 0% 9%; --secondary: 0 0% 14.9%; --secondary-foreground: 0 0% 98%; --muted: 0 0% 14.9%; --muted-foreground: 0 0% 63.9%; --accent: 0 0% 14.9%; --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 0 0% 98%; --border: 0 0% 14.9%; --input: 0 0% 14.9%; --ring: 0 0% 83.1%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; --sidebar-background: 240 5.9% 10%; --sidebar-foreground: 240 4.8% 95.9%; --sidebar-primary: 224.3 76.3% 48%; --sidebar-primary-foreground: 0 0% 100%; --sidebar-accent: 240 3.7% 15.9%; --sidebar-accent-foreground: 240 4.8% 95.9%; --sidebar-border: 240 3.7% 15.9%; --sidebar-ring: 217.2 91.2% 59.8%; } } /* Nunjucks tokens in code editor: distinct from main language (Prism) */ .token.nunjucks-var { color: #7c3aed; } .token.nunjucks-tag { color: #c2410c; } .token.nunjucks-comment { color: #0d9488; } .dark .token.nunjucks-var { color: #a78bfa; } .dark .token.nunjucks-tag { color: #fb923c; } .dark .token.nunjucks-comment { color: #2dd4bf; } /* Prism default theme adds a background to .token.operator (e.g. "="); remove it in our code editor */ .code-editor .token.operator, .code-editor .token.entity, .code-editor .token.url { background: none; }