/* ====================================================================
 * elfcam-topology — premium glass + brand navy aesthetic
 *
 * Visual language: light-blue ambient gradient base + frosted glass cards
 * + ELFCAM brand navy (#0a3a6c) accent. Designed to fit alongside the
 * breathing-light blue ambients used in the cart badge / Categories btn /
 * product tab wrappers / footer (10.7.33–35).
 *
 * Layout strategy stays:
 *   .elfcam-topo-stage is a positioned canvas (width × height from JSON,
 *   defaults to 1000×560). Everything inside is `position:absolute` so
 *   SVG edges and DOM nodes share the same coordinate system.
 * ==================================================================== */

.elfcam-topo-wrap {
  /* === Brand palette === */
  --topo-brand:           #0a3a6c;
  --topo-brand-soft:      #1f5aa0;
  --topo-brand-tint:      rgba(10, 58, 108, 0.06);
  --topo-brand-tint-2:    rgba(10, 58, 108, 0.12);
  --topo-accent:          #2f7ae0;

  /* === Edge type colors (FS-faithful but subtler) === */
  --topo-100g-fiber:      #e74c8d;
  --topo-25g-fiber:       #2bb673;
  --topo-10g-fiber:       #f29133;
  --topo-1_10g-gigabit:   #2f7ae0;
  --topo-1_5g-poe:        #2f7ae0;

  --topo-fiber:           #e74c8d;
  --topo-ethernet-25g:    #2bb673;
  --topo-ethernet-1g:     #2f7ae0;
  --topo-wifi6:           #8a5cf6;
  --topo-wifi6-mesh:      #8a5cf6;

  /* === Glass tokens === */
  --topo-glass-bg:        rgba(255, 255, 255, 0.62);
  --topo-glass-bg-strong: rgba(255, 255, 255, 0.78);
  --topo-glass-border:    rgba(255, 255, 255, 0.55);
  --topo-glass-shadow:
       0 1px 2px rgba(10, 58, 108, 0.04),
       0 6px 20px rgba(10, 58, 108, 0.08),
       0 18px 50px rgba(10, 58, 108, 0.06);
  --topo-glass-shadow-hover:
       0 2px 4px rgba(10, 58, 108, 0.08),
       0 14px 36px rgba(10, 58, 108, 0.16),
       0 28px 70px rgba(10, 58, 108, 0.12);

  --topo-layer-bg:        linear-gradient(180deg,
                            rgba(230, 240, 252, 0.55) 0%,
                            rgba(245, 250, 255, 0.40) 100%);
  --topo-layer-border:    rgba(10, 58, 108, 0.14);
  --topo-layer-label:     #4d6580;

  --topo-label-color:     #102538;

  /* === Container — plain white. All background tinting / breathing-light
     effects + bg_theme variants were removed per user request: nothing
     should compete with the diagram. === */
  position: relative;
  isolation: isolate;
  margin: 0;
  padding: 0 26px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;
  color: var(--topo-label-color);
  background: var(--wrap-bg, #fff);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ─── Legend (top bar) ─────────────────────────────────────────────── */
.elfcam-topo-legend {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  padding: 10px 14px 18px;
  margin: 0 0 14px;
  font-size: 12.5px;
  color: #3a4d63;
  border-bottom: 1px dashed rgba(10, 58, 108, 0.12);
}
.elfcam-topo-legend,
.elfcam-topo-legend:empty { display: none; }
.elfcam-topo-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  white-space: nowrap;
  letter-spacing: 0.2px;
}
.elfcam-topo-legend-line {
  display: inline-block;
  width: 32px;
  height: 0;
  border-top: 2.4px solid currentColor;
  border-radius: 2px;
}
.elfcam-topo-legend-line--dashed {
  border-top-style: dashed;
}

/* ─── Stage (canvas) ───────────────────────────────────────────────── */
.elfcam-topo-stage {
  position: relative;
  z-index: 1;
  width: var(--topo-canvas-w, 1000px);
  height: var(--topo-canvas-h, 560px);
  margin: 0 auto;
}

/* SVG layer holds the edges */
.elfcam-topo-edges {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 2;
  filter: drop-shadow(0 2px 4px rgba(10, 58, 108, 0.08));
}
.elfcam-topo-edges path {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0.92;
}
/* Edge labels are rendered as DOM, NOT SVG text — that way they get a
   higher z-index than nodes so they're never clipped by card overlaps. */
.elfcam-topo-edge-labels {
  position: absolute;
  inset: 0;
  z-index: 4;        /* ABOVE nodes (3) */
  pointer-events: none;
}
/* Edge tag — flat translucent disc at the visible midpoint of any edge that
   carries cable_product_id. No outer frame, no following text. Flat — no
   3D gradients, no white ring. Hover → frosted product tooltip. */
.elfcam-topo-edge-tag {
  position: absolute;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(47, 122, 224, 0.42);
  color: #fff;
  /* 10.8.93: hand cursor — these icons trigger our product popover, the
     `?` help cursor was a leftover from when they had a `title` attr. */
  cursor: pointer;
  pointer-events: auto;
  transition:
       transform .18s cubic-bezier(.4, 0, .2, 1),
       background .18s ease;
}
.elfcam-topo-edge-tag:hover {
  transform: translate(-50%, -50%) scale(1.18);
  background: rgba(10, 58, 108, 0.78);
}
.elfcam-topo-edge-tag svg {
  width: 14px;
  height: 14px;
  fill: #fff;
}

/* Edge label pill — small caption at the visible midpoint of every edge.
   Default fallback (type label) renders with a slightly muted look so the
   user can tell at a glance whether they've authored a custom label or are
   just seeing the default. */
.elfcam-topo-edge-label {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: 10.5px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #0a3a6c;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(10, 58, 108, 0.18);
  border-radius: 999px;
  padding: 3px 8px;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 1px 2px rgba(10, 58, 108, 0.08);
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Default fallback (label = type) keeps the SAME size & weight as authored
   labels — only a slightly muted colour distinguishes them. Different weight
   makes the same size READ as different sizes optically, which the user
   flagged. Visual cue is now colour/opacity only, not boldness. */
.elfcam-topo-edge-label.is-type-default {
  color: #5a6b80;
  background: rgba(255, 255, 255, 0.78);
  border-color: rgba(10, 58, 108, 0.12);
}
/* Clickable label = label rendered as <a> because the edge carries a
   cable_product_id. 10.8.90: opt back IN to pointer-events (the base rule
   shuts them off so labels don't steal clicks meant for the canvas), add a
   hand cursor, and a subtle hover lift so users see the affordance.
   `!important` on cursor because some parent theme rules try to force
   cursor:help on elements that carry a title attr (the label intentionally
   doesn't have one anymore, but the rule keeps the affordance bulletproof). */
.elfcam-topo-edge-label.is-clickable,
.elfcam-topo-edge-label.is-clickable * {
  pointer-events: auto;
  cursor: pointer !important;
  text-decoration: none;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease, background .12s ease;
}
.elfcam-topo-edge-label.is-clickable:hover,
.elfcam-topo-edge-label.is-clickable:focus-visible {
  transform: translate(-50%, -50%) scale(1.06);
  background: #fff;
  border-color: rgba(10, 58, 108, 0.42);
  box-shadow: 0 4px 10px rgba(10, 58, 108, 0.18);
  outline: none;
}
.elfcam-topo-edge-label.is-clickable.is-type-default:hover {
  color: #0a3a6c;
  background: #fff;
  border-color: rgba(10, 58, 108, 0.32);
}

/* ─── Layer sections (HIDDEN per user request — feature removed) ───── */
.elfcam-topo-layers {
  display: none !important;
}
.elfcam-topo-layer {
  position: absolute;
  left: 6px; right: 6px;
  background: var(--topo-layer-bg);
  border: 1px dashed var(--topo-layer-border);
  border-radius: 14px;
  box-sizing: border-box;
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
}
.elfcam-topo-layer-label {
  position: absolute;
  top: -10px;
  left: 16px;
  padding: 3px 10px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--topo-brand);
  letter-spacing: 1.4px;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(10, 58, 108, 0.12);
  border-radius: 999px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 1px 2px rgba(10, 58, 108, 0.04);
}

/* ─── Nodes (frosted glass cards) ──────────────────────────────────── */
.elfcam-topo-nodes {
  position: absolute;
  inset: 0;
  z-index: 3;
  /* The container spans the whole stage but only the inner cards are meant
     to be clickable. Make the container transparent to events so clicks on
     the SVG edges below it (z:2) actually reach those paths. Re-enable
     pointer-events on the visible card (inner) below. */
  pointer-events: none;
}
.elfcam-topo-node {
  position: absolute;
  width: 0; height: 0;
}
.elfcam-topo-node-inner {
  /* --node-rot: rotation in degrees applied to the card. Set by render.js
     from N.rotation. Text + badge children counter-rotate so they always
     stay upright regardless of the card's tilt. */
  --node-rot: 0deg;
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) rotate(var(--node-rot));
  /* Width/height pick per-node overrides via CSS vars on the outer
     .elfcam-topo-node (set by render.js when N.width / N.height present).
     Falls back to 132px wide (slightly bigger per user spec), auto height. */
  width: var(--node-w, 108px);
  min-height: var(--node-h, 0px);
  /* Re-enable pointer events here (parent .elfcam-topo-nodes opted out). */
  pointer-events: auto;
  /* All glass / blur / gradient overlays removed per user request — plain
     white card with subtle border + shadow only. Per-node override via
     --node-bg (set by render.js when N.bg_color is present). */
  background: var(--node-bg, #fff);
  border: var(--node-border, 1px solid rgba(10, 58, 108, 0.08));
  border-radius: 10px;
  /* Whole `box-shadow` / `border` driven by single CSS vars. render.js
     composes the string (or sets 'none' when off) so y-offset doesn't
     leak through and leave a hairline when blur/spread are zero. */
  box-shadow: var(--node-shadow, 0 1px 3px 0 rgba(10, 58, 108, 0.06));
  padding: 5px 2px 6px;
  text-align: center;
  cursor: default;
  text-decoration: none;
  color: inherit;
  transition:
       box-shadow .25s cubic-bezier(.4, 0, .2, 1),
       transform  .25s cubic-bezier(.4, 0, .2, 1),
       border-color .25s ease;
}
.elfcam-topo-node-inner.is-clickable { cursor: pointer; }
.elfcam-topo-node-inner.is-clickable:hover {
  /* Lift on hover but keep the flat white background. */
  transform: translate(-50%, -50%) translateY(-3px) rotate(var(--node-rot));
  border-color: rgba(47, 122, 224, 0.45);
  box-shadow:
       0 4px 12px rgba(10, 58, 108, 0.10),
       0 0 0 4px rgba(47, 122, 224, 0.08);
}
.elfcam-topo-node-vendor {
  position: relative;
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--topo-brand);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  margin-bottom: 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Counter-rotate so vendor text stays upright when the card is rotated. */
  transform: rotate(calc(-1 * var(--node-rot)));
  transform-origin: center center;
}
.elfcam-topo-node-icon {
  position: relative;
  width: 100%;
  height: 46px;
  display: block;
  margin: 0 auto 6px;
}
/* SFP / SFP+ are small "chip badges", not full-size device icons — render
   them at half the normal icon height so they read as a smaller component
   compared to a switch / router / etc. The node's label below also shrinks
   proportionally so the whole "chip" node reads as a smaller pip. */
.elfcam-topo-node-icon[data-icon-key="sfp"],
.elfcam-topo-node-icon[data-icon-key="sfp_plus"] {
  height: 23px;
}
.elfcam-topo-node-icon[data-icon-key="sfp"] ~ .elfcam-topo-node-label,
.elfcam-topo-node-icon[data-icon-key="sfp_plus"] ~ .elfcam-topo-node-label {
  font-size: 9.5px;
  font-weight: 600;
}
/* SFP/SFP+ node card is also visually smaller (narrower default width).
   `var(--node-w, 68px)` keeps explicit user overrides intact — only the
   FALLBACK default is shrunk from 108px → 68px for chip-style nodes. */
.elfcam-topo-node-inner:has(.elfcam-topo-node-icon[data-icon-key="sfp"]),
.elfcam-topo-node-inner:has(.elfcam-topo-node-icon[data-icon-key="sfp_plus"]) {
  width: var(--node-w, 68px);
  padding: 4px 2px 5px;
}
.elfcam-topo-node-icon svg {
  width: auto;
  height: 100%;
  display: block;
  margin: 0 auto;
  filter: drop-shadow(0 1px 2px rgba(10, 58, 108, 0.10));
}
/* Unified icon style — every node-icon SVG (and every shape inside it)
   gets the same stroke colour, width, and line-cap/-join, regardless of
   what the inline SVG markup happens to set. Presentation attributes
   like stroke="#666" / stroke-width="1.6" are overridden here so the
   24-icon family reads as ONE consistent line-art style. Elements that
   opted out with stroke="none" (LED dot fills, tabs) are preserved. */
.elfcam-topo-node-icon svg [stroke]:not([stroke="none"]) {
  stroke: #333;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Custom image (set via N.image in JSON, picked via admin GUI). When present,
   the image replaces the SVG icon. The icon box grows a bit taller and the
   image fits-inside without cropping. */
.elfcam-topo-node-icon.has-image {
  height: 64px;
}
.elfcam-topo-node-image {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: 100%;
  display: block;
  margin: 0 auto;
  object-fit: contain;
  border: 0;
  box-shadow: none;
  filter: none;
}


.elfcam-topo-node-label {
  position: relative;
  display: block;
  font-size: var(--node-label-size, 12.5px);
  font-weight: 600;
  color: var(--node-label-color, var(--topo-label-color));
  font-family: var(--node-label-font, inherit);
  line-height: 1.3;
  word-break: break-word;
  /* Counter-rotate so the label always faces up regardless of N.rotation.
     `display: block` keeps text-wrap width tied to the card; rotate still
     works on block-level elements. */
  transform: rotate(calc(-1 * var(--node-rot)));
  transform-origin: center center;
}

/* ─── Node badges — flat translucent discs ────────────────────────── */
/* Default position: upper-right corner of the card (blue tint).
   `.is-current` variant: soft red AND centered over the card body so the
   visitor can't miss it. Both variants hover-pop the product tooltip. */
.elfcam-topo-badge {
  position: absolute;
  top: -10px; right: -10px;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: rgba(47, 122, 224, 0.42);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  /* 10.8.92: hand cursor — was `help` (?) because original badges only
     opened the native title tooltip. Now they trigger our popover, so
     pointer is the correct affordance. */
  cursor: pointer;
  z-index: 4;
  pointer-events: auto;
  transition:
       background .18s ease,
       transform  .18s cubic-bezier(.4, 0, .2, 1);
  /* Counter-rotate so badges stay readable when the card is tilted. */
  transform: rotate(calc(-1 * var(--node-rot)));
}
.elfcam-topo-badge:hover {
  transform: rotate(calc(-1 * var(--node-rot))) scale(1.15);
  background: rgba(10, 58, 108, 0.78);
}
.elfcam-topo-badge.is-current {
  /* Centered over the card body — soft red, semi-transparent. Sized to
     match the regular blue badge (30px) so the two read as a single
     visual language; color (red vs blue) is now the only distinguisher. */
  top: 50%; right: auto; left: 50%;
  transform: translate(-50%, -50%) rotate(calc(-1 * var(--node-rot)));
  width: 30px; height: 30px;
  background: rgba(231, 76, 60, 0.42);
  z-index: 5;
}
.elfcam-topo-badge.is-current:hover {
  transform: translate(-50%, -50%) rotate(calc(-1 * var(--node-rot))) scale(1.15);
  background: rgba(192, 57, 43, 0.72);
}
.elfcam-topo-badge.is-current svg {
  width: 14px; height: 14px;
}
.elfcam-topo-badge.is-r {
  background: rgba(192, 57, 43, 0.55);
}
.elfcam-topo-badge svg {
  width: 14px; height: 14px;
  fill: #fff;
}

/* ─── Edge type → stroke color & dasharray ─────────────────────────── */
[data-edge-type="100g_fiber"]    { stroke: var(--topo-100g-fiber); }
[data-edge-type="25g_fiber"]     { stroke: var(--topo-25g-fiber); }
[data-edge-type="10g_fiber"]     { stroke: var(--topo-10g-fiber); }
[data-edge-type="1_10g_gigabit"] { stroke: var(--topo-1_10g-gigabit); }
[data-edge-type="1_5g_poe"]      { stroke: var(--topo-1_5g-poe); stroke-dasharray: 6 5; }

[data-edge-type="fiber"]         { stroke: var(--topo-fiber); }
[data-edge-type="ethernet_25g"]  { stroke: var(--topo-ethernet-25g); }
[data-edge-type="ethernet_1g"]   { stroke: var(--topo-ethernet-1g); }
[data-edge-type="wifi6"]         { stroke: var(--topo-wifi6); stroke-dasharray: 5 4; }
[data-edge-type="wifi6_mesh"]    { stroke: var(--topo-wifi6-mesh); stroke-dasharray: 9 5; stroke-width: 2.6; }
/* Wifi-signal: rendered as 3 concentric semicircle arcs at the source node.
   No dashing (arcs already convey "radio"); narrow stroke so the trio reads
   as one signal cluster rather than three separate strokes. */
[data-edge-type="wifi_signal"]   { stroke: var(--topo-wifi6); stroke-width: 2.4; stroke-dasharray: none; }

/* ─── Current product node ────────────────────────────────────────── */
/* No dark-blue gradient takeover, no "VOUS ÊTES ICI" pill anymore. The
   only differentiator vs a regular product node is the badge color:
   blue (non-current) → soft red (current). The node card itself stays
   the same frosted glass treatment so the diagram reads consistently. */
.elfcam-topo-node-inner.is-current {
  cursor: default;
}

/* ─── Edge hit area (invisible wider stroke for easier hovering) ──── */
.elfcam-topo-edges path.topo-edge-hit {
  stroke: transparent;
  stroke-width: 18;
  fill: none;
  pointer-events: stroke;
  /* 10.8.93: hand cursor on the whole edge stroke — hovering the line
     itself triggers the same popover, so the affordance should match. */
  cursor: pointer;
}
.elfcam-topo-edges path.topo-edge-vis.is-hover {
  stroke-width: 3.2;
  opacity: 1;
  filter: drop-shadow(0 0 6px currentColor);
}

/* ─── Frosted glass tooltip (product preview) ─────────────────────── */
.elfcam-topo-tooltip {
  /* Tooltip is appended to <body> so it can't inherit vars from
     .elfcam-topo-wrap — redeclare the brand palette HERE so children
     (.sku, .title, .price del/ins) resolve to the correct colors. */
  --topo-brand:        #0a3a6c;
  --topo-brand-soft:   #1f5aa0;
  --topo-accent:       #2f7ae0;
  --topo-label-color:  #102538;
  --topo-muted:        #98a3b0;

  position: fixed;
  z-index: 999999;
  width: 196px;
  padding: 9px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 14px;
  box-shadow:
       0 4px 12px rgba(10, 58, 108, 0.10),
       0 22px 48px rgba(10, 58, 108, 0.18);
  pointer-events: none;
  opacity: 0;
  transform: translateY(6px) scale(.97);
  transition:
       opacity .18s cubic-bezier(.4, 0, .2, 1),
       transform .18s cubic-bezier(.4, 0, .2, 1);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.elfcam-topo-tooltip.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.elfcam-topo-tooltip::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 50%;
  border-radius: 14px 14px 0 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.45), transparent);
  pointer-events: none;
}
.elfcam-topo-tooltip-img {
  position: relative;
  display: block;
  width: 100%;
  height: 72px;
  object-fit: contain;
  background: #fff;
  border-radius: 6px;
  margin-bottom: 6px;
  box-shadow: inset 0 0 0 1px rgba(10, 58, 108, 0.06);
}
/* Close button (×) — small but clearly visible circle in the top-right
   corner. White circle with brand-blue stroke + bold × so it reads as an
   actionable affordance, not a passive label. */
.elfcam-topo-tooltip-close {
  position: absolute;
  top: 4px; right: 4px;
  width: 20px; height: 20px;
  display: flex; align-items: center; justify-content: center;
  background: #fff;
  border: 1px solid rgba(10, 58, 108, 0.18);
  color: var(--topo-brand);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  border-radius: 50%;
  padding: 0;
  box-shadow: 0 1px 2px rgba(10, 58, 108, 0.10);
  z-index: 2;
  transition: background .15s, color .15s, border-color .15s, transform .15s;
}
.elfcam-topo-tooltip-close:hover {
  background: var(--topo-brand);
  color: #fff;
  border-color: var(--topo-brand);
  transform: scale(1.1);
}
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-close { display: none; }
/* Action row — "Voir" (link to product page) + "Ajouter au panier"
   (AJAX add-to-cart). Only shown on click-driven product popovers. */
.elfcam-topo-tooltip-actions {
  display: flex;
  gap: 4px;
  margin-top: 7px;
}
.elfcam-topo-tooltip-view,
.elfcam-topo-tooltip-cart {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 5px 6px;
  font-size: 10.5px;
  font-weight: 600;
  border-radius: 5px;
  cursor: pointer;
  border: 1px solid transparent;
  letter-spacing: 0.02em;
  transition: background .15s, color .15s, border-color .15s, transform .12s;
  text-decoration: none;
  text-align: center;
  line-height: 1.2;
  white-space: nowrap;
}
.elfcam-topo-tooltip-view {
  background: #fff;
  color: var(--topo-brand);
  border-color: rgba(47, 122, 224, 0.35);
}
.elfcam-topo-tooltip-view:hover {
  background: rgba(47, 122, 224, 0.08);
  color: var(--topo-brand);
}
.elfcam-topo-tooltip-cart {
  background: var(--topo-accent);
  color: #fff;
}
.elfcam-topo-tooltip-cart:hover {
  background: var(--topo-brand);
  transform: translateY(-1px);
}
.elfcam-topo-tooltip-cart:disabled {
  opacity: 0.65;
  cursor: not-allowed;
  transform: none;
}
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-actions,
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-img,
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-sku { display: none; }
/* Pinned (click-opened) popover gets pointer events so the buttons + close
   work. Hover tooltip stays pointer-events: none (set on base rule). */
.elfcam-topo-tooltip.is-product.is-visible { pointer-events: auto; }
.elfcam-topo-tooltip-meta {
  position: relative;
}
.elfcam-topo-tooltip-sku {
  display: inline-block;
  font-size: 9px !important;
  font-weight: 700 !important;
  letter-spacing: 0.8px !important;
  text-transform: uppercase !important;
  color: var(--topo-brand) !important;
  background: rgba(47, 122, 224, 0.10) !important;
  padding: 2px 6px;
  border-radius: 999px;
  margin-bottom: 4px;
}
.elfcam-topo-tooltip-title {
  font-size: 11.5px !important;
  font-weight: 600 !important;
  color: var(--topo-label-color) !important;
  line-height: 1.28 !important;
  margin-bottom: 6px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* ============================================================
 * Price block — WC structure mapped to a clear visual hierarchy:
 *   del  → old/regular price : SMALL, MUTED, struck through, comes 1st
 *   ins  → new/sale price    : BIG, BOLD, brand color, soft tinted bg, comes 2nd
 *   .screen-reader-text      → "Le prix initial était :" — hidden visually
 *
 * !important is used on size/weight/color/decoration because WC's own
 * theme stylesheets ship rules on `del`/`ins`/`.amount` that can override
 * us via specificity, and because the tooltip lives outside .elfcam-topo-wrap
 * — declaring once on the container isn't enough; child elements inherit
 * (forced via `inherit !important` on the wrappers).
 * ============================================================ */
.elfcam-topo-tooltip-price {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 12px;
  color: var(--topo-brand);
  line-height: 1.1;
  margin: 0;
}

/* a11y hide — screen readers still pick this up, visually invisible */
.elfcam-topo-tooltip-price .screen-reader-text {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* ── OLD price (del) — small, muted, struck through ─────────────────── */
.elfcam-topo-tooltip-price del {
  order: 1;
  display: inline-flex;
  align-items: baseline;
  font-size: 11px !important;
  font-weight: 500 !important;
  color: var(--topo-muted) !important;
  text-decoration: line-through !important;
  text-decoration-color: var(--topo-muted) !important;
  text-decoration-thickness: 1.5px !important;
  text-underline-offset: 0;
  opacity: 0.85;
  background: transparent !important;
}
.elfcam-topo-tooltip-price del *,
.elfcam-topo-tooltip-price del .amount,
.elfcam-topo-tooltip-price del .woocommerce-Price-amount,
.elfcam-topo-tooltip-price del bdi,
.elfcam-topo-tooltip-price del .woocommerce-Price-currencySymbol {
  color: inherit !important;
  font-size: inherit !important;
  font-weight: inherit !important;
  text-decoration: inherit !important;
  background: transparent !important;
}

/* ── NEW / SALE price (ins) — big, bold, brand color, soft tinted bg ─ */
.elfcam-topo-tooltip-price ins {
  order: 2;
  display: inline-flex;
  align-items: baseline;
  font-size: 15px !important;
  font-weight: 800 !important;
  color: var(--topo-brand) !important;
  text-decoration: none !important;
  letter-spacing: -0.3px;
  padding: 1px 6px;
  background: linear-gradient(180deg,
       rgba(47, 122, 224, 0.12) 0%,
       rgba(47, 122, 224, 0.04) 100%) !important;
  border-radius: 6px;
  border: 1px solid rgba(47, 122, 224, 0.18);
  box-shadow: 0 1px 2px rgba(10, 58, 108, 0.04);
}
.elfcam-topo-tooltip-price ins *,
.elfcam-topo-tooltip-price ins .amount,
.elfcam-topo-tooltip-price ins .woocommerce-Price-amount,
.elfcam-topo-tooltip-price ins bdi,
.elfcam-topo-tooltip-price ins .woocommerce-Price-currencySymbol {
  color: inherit !important;
  font-size: inherit !important;
  font-weight: inherit !important;
  text-decoration: inherit !important;
  background: transparent !important;
}

/* Currency symbol slightly smaller for visual rhythm */
.elfcam-topo-tooltip-price del .woocommerce-Price-currencySymbol {
  font-size: 0.85em !important;
  margin-left: 1px;
}
.elfcam-topo-tooltip-price ins .woocommerce-Price-currencySymbol {
  font-size: 0.72em !important;
  margin-left: 3px;
  opacity: 0.85;
}

/* WC wrappers — keep layout clean */
.elfcam-topo-tooltip-price .amount,
.elfcam-topo-tooltip-price .woocommerce-Price-amount {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
}
.elfcam-topo-tooltip-price bdi {
  display: inline-flex;
  align-items: baseline;
  unicode-bidi: isolate;
}

/* ── NO-SALE case: a single .amount at root (no del/ins wrapper) ───── */
.elfcam-topo-tooltip-price > .woocommerce-Price-amount,
.elfcam-topo-tooltip-price > .amount {
  font-size: 20px !important;
  font-weight: 800 !important;
  color: var(--topo-brand) !important;
  letter-spacing: -0.3px;
}
.elfcam-topo-tooltip-price > .woocommerce-Price-amount .woocommerce-Price-currencySymbol,
.elfcam-topo-tooltip-price > .amount .woocommerce-Price-currencySymbol {
  font-size: 0.72em !important;
  opacity: 0.85;
}

/* Generic (non-product) edge tooltip variant — only shows type label */
.elfcam-topo-tooltip.is-generic {
  width: auto;
  max-width: 220px;
  padding: 8px 12px;
}
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-img,
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-sku,
.elfcam-topo-tooltip.is-generic .elfcam-topo-tooltip-price { display: none; }

/* ─── Solution wrapper (multi-card with tab strip) ─────────────────── */
.elfcam-topo-solution {
  margin: 28px 0;
}
.elfcam-topo-solution-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--topo-brand);
  margin: 0 0 18px;
  letter-spacing: -0.2px;
}
/* Frameless tabs to match the borderless / shadowless .elfcam-topo-wrap.
   Active tab gets a soft blue breathing-light underline to mark it without
   any heavy box framing. Inactive tabs are subtly recessed via color only. */
.elfcam-topo-tabs {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0;
  margin: 0 0 8px;
  background: transparent;
  position: relative;
  z-index: 2;
}
.elfcam-topo-tab {
  position: relative;
  background: transparent;
  border: 0;
  padding: 8px 16px 10px;
  font-size: 13px;
  font-weight: 600;
  color: #6b7785;
  border-radius: 0;
  cursor: pointer;
  transition: color .2s ease;
  white-space: nowrap;
}
.elfcam-topo-tab:hover {
  color: var(--topo-brand);
}
.elfcam-topo-tab.is-active {
  color: var(--topo-brand);
  z-index: 1;
}
/* Breathing-light underline under the active tab — soft blue bar with a
   gentle opacity + scale pulse so the tab feels "alive" without competing
   with the diagram. ::after sits on the tab's bottom edge, transform-origin
   centered so it breathes outward. */
.elfcam-topo-tab::after {
  content: "";
  position: absolute;
  left: 12px; right: 12px; bottom: 2px;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg,
       rgba(47, 122, 224, 0)   0%,
       rgba(47, 122, 224, 1) 50%,
       rgba(47, 122, 224, 0) 100%);
  box-shadow: 0 0 12px 2px rgba(47, 122, 224, 0.55);
  opacity: 0;
  transform: scaleX(0.4);
  transform-origin: center center;
  transition: opacity .25s ease, transform .25s ease;
  pointer-events: none;
}
.elfcam-topo-tab.is-active::after {
  opacity: 1;
  transform: scaleX(1);
  animation: elfcamTopoTabBreath 2.6s ease-in-out infinite;
}
@keyframes elfcamTopoTabBreath {
  0%, 100% {
    opacity: 0.65;
    box-shadow: 0 0 8px 1px rgba(47, 122, 224, 0.35);
    transform: scaleX(0.92);
  }
  50% {
    opacity: 1;
    box-shadow: 0 0 18px 3px rgba(47, 122, 224, 0.65);
    transform: scaleX(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  .elfcam-topo-tab.is-active::after { animation: none; }
}
.elfcam-topo-panels {
  position: relative;
}
.elfcam-topo-panel {
  display: none;
}
.elfcam-topo-panel.is-active {
  display: block;
}
/* Tabs already sit naturally above the borderless wrap; no extra wrap
   tweak needed when tabs are present. (Old folder-tab overlap removed
   along with the wrap's border-radius / shadow.) */

/* ─── Mobile / narrow screens ───────────────────────────────────────── */
@media (max-width: 1100px) {
  .elfcam-topo-wrap { padding: 0 16px; }
  .elfcam-topo-legend { gap: 10px 18px; font-size: 12px; padding: 6px 8px 14px; }
}

/* ─── Editor: resize + rotation handles on the selected node ──────────
   Children of .elfcam-topo-node-inner so they INHERIT its rotation —
   handles stay visually attached to the rotated card's corners + top.
   Only rendered when the node is `.is-selected` (added by admin.js).
   Default rule hides them; the .is-selected rule explicitly re-shows
   them — without `display: block` here the cascade leaves them hidden. */
.elfcam-topo-node-handle,
.elfcam-topo-node-rot-handle { display: none; }
.elfcam-topo-node.is-selected .elfcam-topo-node-handle {
  display: block;
  position: absolute;
  width: 18px;
  height: 18px;
  background: #2f7ae0;
  border: 3px solid #fff;
  border-radius: 50%;
  z-index: 100;
  pointer-events: auto !important;
  box-shadow: 0 0 0 1px #0a3a6c, 0 0 12px rgba(47, 122, 224, 0.6);
  animation: elfcamTopoHandlePulse 1.8s ease-in-out infinite;
}
@keyframes elfcamTopoHandlePulse {
  0%, 100% { box-shadow: 0 0 0 1px #0a3a6c, 0 0 8px  rgba(47, 122, 224, 0.4); }
  50%      { box-shadow: 0 0 0 1px #0a3a6c, 0 0 14px rgba(47, 122, 224, 0.85); }
}
.elfcam-topo-node.is-selected .elfcam-topo-node-handle:hover {
  background: #0a3a6c;
  transform: scale(1.3);
  animation: none;
}
.elfcam-topo-node.is-selected .elfcam-topo-node-handle.is-tl { top: -11px;    left: -11px;  cursor: nwse-resize; }
.elfcam-topo-node.is-selected .elfcam-topo-node-handle.is-tr { top: -11px;    right: -11px; cursor: nesw-resize; }
.elfcam-topo-node.is-selected .elfcam-topo-node-handle.is-bl { bottom: -11px; left: -11px;  cursor: nesw-resize; }
.elfcam-topo-node.is-selected .elfcam-topo-node-handle.is-br { bottom: -11px; right: -11px; cursor: nwse-resize; }
/* Rotation handle: small circle floating above the card with a stem line. */
.elfcam-topo-node.is-selected .elfcam-topo-node-rot-handle {
  display: block;
  position: absolute;
  top: -30px;
  left: 50%;
  transform: translateX(-50%);
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #2bb673;
  cursor: grab;
  z-index: 6;
  pointer-events: auto;
  box-shadow: 0 1px 3px rgba(10, 58, 108, 0.25);
}
.elfcam-topo-node.is-selected .elfcam-topo-node-rot-handle::before {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  width: 2px;
  height: 16px;
  background: #2bb673;
  transform: translateX(-50%);
}
.elfcam-topo-node.is-selected .elfcam-topo-node-rot-handle:active {
  cursor: grabbing;
}
/* Visual hint: a dashed outline on the inner card while selected, so users
   can SEE the rotation-aware bounding box that the handles operate on. */
.elfcam-topo-node.is-selected .elfcam-topo-node-inner {
  outline: 1px dashed rgba(47, 122, 224, 0.55);
  outline-offset: 1px;
}

/* ─── Fusion group — paired nodes that drag together ────────────────────
   Each member of a fusion group (either the parent or a child whose
   `fused_to` points at another node) is marked .is-fused in render.js.
   Border thickened + colored brand-navy to mark the "fused" boundary
   where they touch each other. */
.elfcam-topo-node.is-fused .elfcam-topo-node-inner {
  border-color: var(--topo-brand, #0a3a6c);
  border-width: 2px;
}

/* ─── Reduced-motion: disable badge pulse ─────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .elfcam-topo-badge { animation: none; }
  .elfcam-topo-node-inner { transition: none; }
}
