/* packages/canvas/src/styles/index.css */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap')
layer(base);

@import 'tailwindcss' prefix(sf);
@import 'tw-animate-css';

@custom-variant dark (&:is(.dark *));

@theme inline {
  --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));

  --radius-lg: var(--radius);
  --radius-md: calc(var(--radius) - 2px);
  --radius-sm: calc(var(--radius) - 4px);
}

/*
  The default border color has changed to `currentcolor` in Tailwind CSS v4,
  so we've added these compatibility styles to make sure everything still
  looks the same as it did with Tailwind CSS v3.

  If we ever want to remove these styles, we need to add an explicit border
  color utility to any element that depends on these defaults.
*/
@layer base {
  *,
  ::after,
  ::before,
  ::backdrop,
  ::file-selector-button {
    border-color: var(--color-gray-200, currentcolor);
  }
}

/* All tokens live on the wrapper, NOT :root. Consumer's :root is untouched. */
.seeflow-canvas-root {
  --background: 240 10% 3.9%;
  --foreground: 0 0% 98%;
  --card: 240 5.9% 10.6%;
  --card-foreground: 0 0% 98%;
  --popover: 240 5.9% 10.6%;
  --popover-foreground: 0 0% 98%;
  --primary: 160 84% 39.4%;
  --primary-foreground: 162 89% 10%;
  --secondary: 240 6% 13%;
  --secondary-foreground: 0 0% 98%;
  --muted: 240 7% 12%;
  --muted-foreground: 240 4.4% 64.9%;
  --accent: 240 3.7% 15.9%;
  --accent-foreground: 0 0% 98%;
  --destructive: 0 84.2% 60.2%;
  --destructive-foreground: 0 0% 98%;
  --border: 240 3.7% 15.9%;
  --input: 240 3.7% 26.1%;
  --ring: 160 84% 39.4%;
  --radius: 0.5rem;

  --bg-canvas: #0a0a0c;
  --emerald-glow: rgba(16, 185, 129, 0.35);
  --shadow-card: 0 4px 12px -2px rgba(0, 0, 0, 0.5);
  --shadow-window: 0 30px 80px -20px rgba(0, 0, 0, 0.6), 0 0 60px -20px var(--emerald-glow);
  --shadow-glow-ok: 0 0 30px -4px rgba(16, 185, 129, 0.45);
  --shadow-glow-pending: 0 0 30px -4px rgba(245, 158, 11, 0.45);
  --ring-selected: 0 0 0 3px rgba(16, 185, 129, 0.22), 0 0 40px -6px rgba(16, 185, 129, 0.55);
  --amber: #f59e0b;
  --amber-hi: #fbbf24;
  --danger: #ef4444;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

  --seeflow-handle-fill: hsl(var(--background));
  --seeflow-handle-border-color: hsl(var(--primary) / 0.6);
  --seeflow-handle-border-width: calc(1px / var(--rf-zoom, 1));
  --seeflow-handle-size: calc(17px / var(--rf-zoom, 1));

  /* Font + background scoped to the wrapper — outside, consumer rules win */
  font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
  color: hsl(var(--foreground));
}

/* Utilities scoped to the wrapper */
.seeflow-canvas-root .seeflow-no-scrollbar {
  scrollbar-width: none;
}
.seeflow-canvas-root .seeflow-no-scrollbar::-webkit-scrollbar {
  display: none;
}

@keyframes seeflow-ping-fast {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  75%,
  100% {
    transform: scale(1.6);
    opacity: 0;
  }
}
.seeflow-canvas-root .animate-ping-fast {
  animation: seeflow-ping-fast 0.7s cubic-bezier(0, 0, 0.2, 1);
}

@keyframes seeflow-node-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4);
  }
  50% {
    box-shadow: 0 0 0 6px rgba(245, 158, 11, 0);
  }
}
.seeflow-canvas-root .seeflow-node-pulse {
  animation: seeflow-node-pulse 1.4s ease-in-out infinite;
}

@keyframes inline-edit-shake {
  0%,
  100% {
    transform: translateX(0);
  }
  20%,
  60% {
    transform: translateX(-3px);
  }
  40%,
  80% {
    transform: translateX(3px);
  }
}
.seeflow-canvas-root .inline-edit-shake {
  animation: inline-edit-shake 0.32s ease-in-out;
}

/* Status-uplift keyframes — all gated on prefers-reduced-motion: no-preference */
@media (prefers-reduced-motion: no-preference) {
  @keyframes seeflow-ring-spin {
    to {
      transform: rotate(360deg);
    }
  }
  @keyframes seeflow-pill-pop {
    0%,
    100% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.1);
    }
  }
  @keyframes seeflow-play-pop {
    0%,
    100% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.15);
    }
  }
  @keyframes seeflow-success-halo {
    0% {
      box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.45);
    }
    100% {
      box-shadow: 0 0 0 16px rgba(16, 185, 129, 0);
    }
  }
  @keyframes seeflow-edge-handoff {
    0%,
    100% {
      stroke-width: var(--seeflow-edge-base-width, 2px);
    }
    50% {
      stroke-width: calc(var(--seeflow-edge-base-width, 2px) + 1px);
    }
  }

  .seeflow-canvas-root .seeflow-ring-spin {
    animation: seeflow-ring-spin 1.2s linear infinite;
  }
  .seeflow-canvas-root .seeflow-pill-pop {
    animation: seeflow-pill-pop 240ms ease-out;
  }
  .seeflow-canvas-root .seeflow-play-pop {
    animation: seeflow-play-pop 320ms ease-out;
  }
  .seeflow-canvas-root .seeflow-success-halo {
    animation: seeflow-success-halo 600ms cubic-bezier(0, 0, 0.2, 1);
  }
  .seeflow-canvas-root .react-flow__edge[data-handoff="true"] .react-flow__edge-path {
    animation: seeflow-edge-handoff 500ms ease-out;
  }
}

.seeflow-canvas-root .inline-edit-empty::before {
  content: attr(data-placeholder);
  color: hsl(var(--muted-foreground));
  opacity: 0.5;
  font-style: italic;
  pointer-events: none;
}

/* Cursor rules — all confined to the wrapper */
.seeflow-canvas-root .react-flow__node,
.seeflow-canvas-root .react-flow__node * {
  cursor: default;
}
.seeflow-canvas-root .react-flow__node [contenteditable="true"] {
  cursor: text;
}
.seeflow-canvas-root button:not(:disabled) {
  cursor: pointer;
}
.seeflow-canvas-root button:disabled {
  cursor: not-allowed;
}
.seeflow-canvas-root .react-flow__node button:not([data-testid="play-button"]) {
  cursor: default;
}

/* Pane cursor */
.seeflow-canvas-root .react-flow .react-flow__pane.selection {
  cursor: default;
}
.seeflow-canvas-root .react-flow .react-flow__pane.draggable {
  cursor: grab;
}
.seeflow-canvas-root .react-flow .react-flow__pane.dragging {
  cursor: grabbing;
}

/* React Flow visual tweaks */
.seeflow-canvas-root .react-flow__node.connectingfrom {
  opacity: 0.85;
}

.seeflow-canvas-root .react-flow__node .react-flow__handle {
  background: var(--seeflow-handle-fill) !important;
  border: var(--seeflow-handle-border-width) solid var(--seeflow-handle-border-color) !important;
  width: var(--seeflow-handle-size) !important;
  height: var(--seeflow-handle-size) !important;
}

.seeflow-canvas-root .react-flow__handle::after {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
}

.seeflow-canvas-root .react-flow__node.selected:not(.react-flow__node-group),
.seeflow-canvas-root .react-flow__node[data-connect-source="true"]:not(.react-flow__node-group) {
  z-index: 1000 !important;
}

.seeflow-canvas-root .react-flow__node .react-flow__handle,
.seeflow-canvas-root .react-flow__node .react-flow__handle::after {
  cursor: default;
}

.seeflow-canvas-root .react-flow__edgeupdater {
  fill: transparent;
  stroke: transparent;
  opacity: 1;
  pointer-events: all;
  cursor: default;
}

.seeflow-canvas-root .react-flow__edge,
.seeflow-canvas-root .react-flow__edge.selectable,
.seeflow-canvas-root .react-flow__edge .react-flow__edge-path {
  cursor: default;
}

.seeflow-canvas-root .seeflow-connector-endpoint-dot {
  position: absolute;
  width: var(--seeflow-handle-size);
  height: var(--seeflow-handle-size);
  background: var(--seeflow-handle-fill);
  border: var(--seeflow-handle-border-width) solid var(--seeflow-handle-border-color);
  border-radius: 50%;
  pointer-events: none;
  z-index: 2000;
}

.seeflow-canvas-root
  .react-flow.seeflow-connecting
  .react-flow__node:not([data-connect-source="true"])
  .react-flow__handle {
  opacity: 0 !important;
}

.seeflow-canvas-root
  .react-flow.seeflow-connecting
  .react-flow__node[data-connect-target="true"]
  > div {
  border-color: hsl(var(--muted-foreground)) !important;
  border-style: dashed !important;
}
.seeflow-canvas-root
  .react-flow.seeflow-connecting
  .react-flow__node-group[data-connect-target="true"] {
  border-color: hsl(var(--muted-foreground)) !important;
  border-style: dashed !important;
}

.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected::after {
  content: "";
  position: absolute;
  inset: calc(-8px / var(--rf-zoom, 1));
  border: calc(1px / var(--rf-zoom, 1)) dashed hsl(var(--primary) / 0.6);
  border-radius: 0;
  pointer-events: none;
  z-index: -1;
}

.seeflow-canvas-root .react-flow__nodesselection-rect {
  background: transparent;
  border: none;
}

.seeflow-canvas-root .react-flow__node-group {
  border: 1px dashed hsl(var(--border));
  background: transparent;
  border-radius: 4px;
}
.seeflow-canvas-root .react-flow__node-group.selected {
  border-color: transparent;
}
.seeflow-canvas-root .react-flow__node-group.selected::after {
  z-index: 0;
}
.seeflow-canvas-root .react-flow__node[data-gated-child="true"] {
  pointer-events: none;
}
.seeflow-canvas-root .react-flow__node-group:has(> [data-testid="group-node"][data-active="true"]) {
  border: 2px solid hsl(var(--primary));
  background: hsl(var(--primary) / 0.04);
}
.seeflow-canvas-root .react-flow__node-group .react-flow__node-group-label {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  padding: 4px 8px;
  font-size: 12px;
  font-weight: 500;
  color: hsl(var(--muted-foreground));
  background: transparent;
  pointer-events: auto;
  z-index: 2;
  user-select: none;
}
.seeflow-canvas-root .react-flow__node-group .react-flow__node-group-label[data-editing="true"] {
  user-select: text;
}

/* US-007 selected-node handle transforms (edit mode only) */
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__handle-top {
  transform: translate(-50%, -50%) translate(0, calc(-8px / var(--rf-zoom, 1)));
}
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__handle-bottom {
  transform: translate(-50%, 50%) translate(0, calc(8px / var(--rf-zoom, 1)));
}
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__handle-left {
  transform: translate(-50%, -50%) translate(calc(-8px / var(--rf-zoom, 1)), 0);
}
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__handle-right {
  transform: translate(50%, -50%) translate(calc(8px / var(--rf-zoom, 1)), 0);
}

.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__resize-control.handle.top.left {
  transform: translate(calc(-8px / var(--rf-zoom, 1)), calc(-8px / var(--rf-zoom, 1)));
}
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__resize-control.handle.top.right {
  transform: translate(calc(8px / var(--rf-zoom, 1)), calc(-8px / var(--rf-zoom, 1)));
}
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__resize-control.handle.bottom.left {
  transform: translate(calc(-8px / var(--rf-zoom, 1)), calc(8px / var(--rf-zoom, 1)));
}
.seeflow-canvas-root[data-mode="edit"] .react-flow__node.selected .react-flow__resize-control.handle.bottom.right {
  transform: translate(calc(8px / var(--rf-zoom, 1)), calc(8px / var(--rf-zoom, 1)));
}

/* View + mini modes: keep the 4 connector handles invisible even when the
   node is selected. We deliberately leave the `.selected::after` dashed
   rectangle alone — view-mode users still want a visual selection
   indicator; only the edit-only connect dots should disappear.

   Wrapped in `@layer base` on purpose: the Handle componentry sets
   `sf:opacity-100!` (Tailwind utility, in `@layer utilities`) when selected.
   For `!important`, EARLIER cascade layers win — putting this override in
   `base` (declared before `utilities`) lets `opacity:0!important` beat
   `.sf\:opacity-100\!`. Unlayered `!important` would lose here. */
@layer base {
  .seeflow-canvas-root[data-mode="view"] .react-flow__node.selected .react-flow__handle,
  .seeflow-canvas-root[data-mode="mini"] .react-flow__node.selected .react-flow__handle {
    opacity: 0 !important;
    pointer-events: none !important;
  }
}

/* React Flow Controls panel theming */
.seeflow-canvas-root .react-flow__controls {
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}
.seeflow-canvas-root .react-flow__controls-button {
  background: hsl(var(--secondary));
  color: hsl(var(--foreground));
  border-bottom: 1px solid hsl(var(--border));
}
.seeflow-canvas-root .react-flow__controls-button:hover {
  background: hsl(var(--accent));
}
.seeflow-canvas-root .react-flow__controls-button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.seeflow-canvas-root .react-flow__controls-button svg {
  fill: currentColor;
}

/* Cursor-tracking glow overlay. Bright dots aligned with the React Flow
   <Background gap={12} size={0.6} /> grid, masked by a radial gradient
   centered on (--mx, --my). The component writes --mx/--my, background-
   position, and background-size; this rule supplies the geometry and the
   leave-fade transition. */
.seeflow-canvas-root .glow-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;

  background-image: radial-gradient(rgba(255, 255, 255, 0.55) 0.6px, transparent 1px);

  --mx: 50%;
  --my: 50%;
  /* Static mask gradient with an EXPLICIT 240px circle inside a 480px box.
     The 240px sizing is load-bearing: without it, `radial-gradient(circle, ...)`
     defaults to `farthest-corner` (~339px in a 480×480 box), which stretches
     the gradient to the diagonals and makes the reveal look square. With the
     explicit 240px, the gradient is a true 240px-radius circle and the
     surrounding 480×480 box pads it with transparency (mask-repeat: no-repeat
     extends transparency outside the box too).
     The gradient bitmap stays identical for every frame, so Chromium caches
     the rasterized layer and just RE-COMPOSITES it at a new offset when
     --mx/--my change. Re-centering the gradient (old form: `circle 240px
     at var(--mx) var(--my)`) forces a full repaint of the 1920×1080+ overlay
     every frame; mask-position is a composite-only op on the cached bitmap. */
  -webkit-mask-image: radial-gradient(
    circle 240px at center,
    rgba(0, 0, 0, 1) 0%,
    rgba(0, 0, 0, 0.6) 35%,
    rgba(0, 0, 0, 0) 100%
  );
  mask-image: radial-gradient(
    circle 240px at center,
    rgba(0, 0, 0, 1) 0%,
    rgba(0, 0, 0, 0.6) 35%,
    rgba(0, 0, 0, 0) 100%
  );
  -webkit-mask-size: 480px 480px;
  mask-size: 480px 480px;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  /* Subtract 240px (half mask-size) so the gradient center lands AT the
     cursor, matching the old `at var(--mx) var(--my)` semantics. The JS-side
     handler still writes --mx/--my as cursor-relative px, so tests and the
     rAF batching contract are unchanged. */
  -webkit-mask-position: calc(var(--mx) - 240px) calc(var(--my) - 240px);
  mask-position: calc(var(--mx) - 240px) calc(var(--my) - 240px);

  /* Promote the overlay to its own compositor layer so per-frame mask shifts
     stay isolated from the rest of the canvas (nodes, edges, dots). Without
     this, every --mx/--my update invalidates the canvas paint tree, stretching
     pointerover-bundled paint work into 30+ ms tasks. */
  transform: translateZ(0);
  will-change: mask-position, opacity;
  /* `contain: strict` tells the browser this element's layout, style, and
     paint cannot affect anything outside its box — so a mask shift here
     doesn't propagate invalidations into sibling subtrees (the React Flow
     pane is the next sibling and contains every node/edge). */
  contain: strict;

  opacity: 0;
  /* Fade-in stays snappy (120ms, see [data-active='true'] below) so a fresh
     mousemove lights up immediately. Fade-out is slow (800ms) and runs when
     `data-active` flips back to `false` from either mouseleave or the 1.2s
     idle timer in createGlowHandlers — the glow lingers and dissipates. */
  transition: opacity 800ms ease-out;
}
.seeflow-canvas-root .glow-overlay[data-active='true'] {
  opacity: 1;
  transition: opacity 120ms ease-out;
}
