/* VerticalsHero — Isometric 3D building floors, with the engineering-drawing
 * chrome we built before (axis, callouts, title block, grid paper).
 *
 * Stack order (top→bottom):
 *   01 DATA   — server racks visible on top, refrigeration unit overhead
 *   02 FARMA  — sealed envelope with AHU on top, HEPA layout on top face
 *   03 FAB    — clear-span manufacturing hall with a wind blade on its jigs
 *   04 EDIF   — taller podium block: mullions, floor plates, entry, mast
 *
 * The order is the one the nav dropdown uses, so the intro and the menu tell
 * the same story in the same sequence.
 *
 * Interaction: clicking a list item OR a 3D block selects it. The selected
 * block lifts ~16px, brightens to full opacity with white-blue edges, and
 * its annotation callouts fade in. The others dim to ~30% opacity. */

/* -------------------- Geometry helpers -------------------- */

// Isometric depth — controls perspective. Lower = flatter / more elevation-like.
const ISO_DX = 36;   // horizontal shift of back faces
const ISO_DY = 18;   // vertical shift of back faces (negative = up)

const isoTop = (W, H) => {
  // Top face polygon — back of slab is up and to the right
  const dx = ISO_DX, dy = -ISO_DY;
  return `0,0 ${W},0 ${W + dx},${dy} ${dx},${dy}`;
};
const isoSide = (W, H) => {
  // Right side face — front-right edge to back-right edge, down to bottom
  const dx = ISO_DX, dy = -ISO_DY;
  return `${W},0 ${W + dx},${dy} ${W + dx},${H + dy} ${W},${H}`;
};

/* -------------------- Floor renderers -------------------- */
/* Each renders a single 3D slab at origin (0,0) of its own <g transform>.
   Width is uniform per floor; height varies by typology character. */

const FLOOR_W = 480;   /* scaled up from 320 — bigger blocks to match the
                          tall menu rows on the left side */

/* Each floor renders its line-art image SELF-CONTAINED inside its slab.
   imgW spans the slab width; imgH derives from aspect; slab H = imgH + label.
   The previous design had the image at 1.5×FLOOR_W, which overhung the slab
   by ~200px and overlapped the neighbours once the per-block scale piled on. */

const FloorDC = ({ active }) => {
  // dc_isometric.png — 1248×832 ≈ 1.5:1
  const W = FLOOR_W;
  const imgW = W;
  const imgH = imgW / 1.5;
  const H = imgH + 18;                // small strip below for the kicker label
  return (
    <g className="vt-3d">
      <image
        href="assets/dc_isometric.png"
        x={0} y={0}
        width={imgW} height={imgH}
        preserveAspectRatio="xMidYMid meet"
        className="vt-iso-drawing"
      />
      <line x1="-8" y1={H} x2={W + ISO_DX + 8} y2={H} className="vt-floor-base" />
      <text x={W / 2} y={H - 4} className="vt-equip-sm" textAnchor="middle" style={{letterSpacing:".14em"}}>
        DATA CENTER · TIER IV · MADRID
      </text>
    </g>
  );
};

const FloorFarma = ({ active }) => {
  // farma_isometric.png — 1.5:1. Rendered 1.25× larger than DC so the lab
  // interior reads at parity, with the extra height absorbed upward — the
  // image's bottom edge stays just above the ground line.
  const W = FLOOR_W;
  const imgW = W * 1.25;
  const imgH = imgW / 1.5;
  const H = (W / 1.5) + 18;          // slab height matches DC so layout is uniform
  // Bottom of the image sits 4px above the ground line; the rest of the
  // 1.25× growth (54px tall) is absorbed upward, into the gap above the slab.
  const yOffset = (H - 4) - imgH;
  return (
    <g className="vt-3d">
      <image
        href="assets/farma_isometric.png"
        x={(W - imgW) / 2} y={yOffset}
        width={imgW} height={imgH}
        preserveAspectRatio="xMidYMid meet"
        className="vt-iso-drawing"
      />
      <line x1="-8" y1={H} x2={W + ISO_DX + 8} y2={H} className="vt-floor-base" />
      <text x={W / 2} y={H - 4} className="vt-equip-sm" textAnchor="middle" style={{letterSpacing:".14em"}}>
        LABORATORIO P3 · GMP · CLEANROOM
      </text>
    </g>
  );
};

/* fab_isometric.png se generó a partir del original entregado por el cliente
   (Blade Blueprint Without Measurements.png, RGB 4944×3349), que venía con el
   fondo azul oscuro y su propia retícula incrustados. Los otros tres dibujos
   son PNG transparentes para que se vea la retícula de la página, así que hubo
   que recortar el fondo por luminancia — el 91,7 % de los píxeles estaba por
   debajo de 24, y la retícula incrustada entre 24 y 47. La línea se tiñe del
   #9BC4E6 exacto de los otros tres dibujos; el -frames:v 1 hace falta porque
   el filtro color genera un flujo infinito:

     ffmpeg -i original.png -filter_complex \
       "[0:v]scale=1248:-1,format=gray,curves=all='0/0 0.16/0 0.35/0.55 1/1'[a];\
        color=c=0x9BC4E6:s=1248x845,format=rgb24[c];\
        [c][a]alphamerge,format=rgba" \
       -frames:v 1 assets/fab_isometric.png

   Si alguna vez se vuelve a exportar el original, ésta es la receta. */
const FloorFab = ({ active }) => {
  // fab_isometric.png — 1248×845 ≈ 1.477:1. La nave sólo ocupa el 62 % del
  // ancho de su lienzo (el data center ocupa el 79 %), así que se dibuja
  // 1,25× mayor —el mismo recurso que ya usa farma— para que las tres naves
  // se lean al mismo tamaño una debajo de otra.
  const W = FLOOR_W;
  const imgW = W * 1.25;
  const imgH = imgW / 1.4769;
  const H = (W / 1.5) + 18;          // misma altura de losa que DC y farma
  // El dibujo lleva mucho margen transparente, así que se coloca por su
  // tinta y no por el lienzo: el borde inferior del dibujo queda 22 px por
  // encima de la línea de suelo, igual que en DC. Lo que sobra del 1,25 %
  // se absorbe hacia arriba, hacia el hueco entre losas.
  const INK_BOTTOM = 766 / 845;      // dónde acaba la tinta dentro del PNG
  const yOffset = (H - 22) - imgH * INK_BOTTOM;
  return (
    <g className="vt-3d">
      <image
        href="assets/fab_isometric.png"
        x={(W - imgW) / 2} y={yOffset}
        width={imgW} height={imgH}
        preserveAspectRatio="xMidYMid meet"
        className="vt-iso-drawing"
      />
      <line x1="-8" y1={H} x2={W + ISO_DX + 8} y2={H} className="vt-floor-base" />
      <text x={W / 2} y={H - 4} className="vt-equip-sm" textAnchor="middle" style={{letterSpacing:".14em"}}>
        PLANTA DE COMPOSITES · PALA EÓLICA
      </text>
    </g>
  );
};

const FloorEdif = ({ active }) => {
  // auditorio_tenerife.png — ≈1.19:1
  const W = FLOOR_W;
  const imgW = Math.round(W * 0.92);
  const imgH = Math.round(imgW / 1.19);
  const H = imgH + 18;
  return (
    <g className="vt-3d">
      <image
        href="assets/auditorio_tenerife.png"
        x={(W - imgW) / 2} y={0}
        width={imgW} height={imgH}
        preserveAspectRatio="xMidYMid meet"
        className="vt-ten-drawing"
      />
      <line x1="-8" y1={H} x2={W + ISO_DX + 8} y2={H} className="vt-floor-base" />
      {/* El arquitecto sale del pie y se pone a la cabeza de la línea de
          suelo, en azul de marca y al doble de tamaño. Dentro del pie iba
          a 8,5 px y al 55 % de opacidad, entre el nombre del edificio y
          el año, y no lo leía nadie. Aquí hay sitio de sobra: el pie va
          centrado y el tramo izquierdo de la línea está vacío. */}
      <text x={-8} y={H - 4} className="vt-arch" textAnchor="start">
        S. CALATRAVA
      </text>
      <text x={W / 2} y={H - 4} className="vt-equip-sm" textAnchor="middle" style={{letterSpacing:".14em"}}>
        AUDITORIO DE TENERIFE · 2003
      </text>
    </g>
  );
};

// Slab heights — kept in sync with the FloorXxx renderers above.
const DC_H    = Math.round(FLOOR_W / 1.5) + 18;            // = 338
const FARMA_H = DC_H;
const FAB_H   = DC_H;
const EDIF_H  = Math.round(Math.round(FLOOR_W * 0.92) / 1.19) + 18;

/* -------------------- Block (wraps a floor renderer w/ chrome) -------------------- */

const Block = ({ id, y, isActive, label, codeShort, codeNum, onSelect, children }) => {
  // When active: lift up by 16px. When another is active: dim heavily.
  const dy = isActive ? -16 : 0;
  return (
    <g
      className={"vt-block " + (isActive ? "is-active" : "")}
      transform={`translate(80, ${y + dy})`}
      onClick={() => onSelect(id)}
      style={{cursor:"pointer"}}
      data-id={id}
    >
      {children}
      {/* Block identifier on the left edge */}
      <g className="vt-block-id" transform="translate(-44, 0)">
        <line x1="0" y1="-4" x2="36" y2="-4" className="vt-block-id-line" />
        <text x="32" y="-8" className="vt-block-id-num" textAnchor="end">{codeNum}</text>
        <text x="32" y="-20" className="vt-block-id-lbl" textAnchor="end">{codeShort}</text>
      </g>
    </g>
  );
};

/* -------------------- Callouts (live annotation overlay) -------------------- */

const Callout = ({ x, y, anchorX, anchorY, label, delay = 0 }) => (
  <g className="vt-callout" style={{animationDelay: delay + "ms"}}>
    <circle cx={anchorX} cy={anchorY} r="2.5" className="vt-callout-dot" />
    <polyline
      points={`${anchorX},${anchorY} ${x - 8},${anchorY} ${x},${y}`}
      className="vt-callout-leader"
      fill="none"
    />
    <line x1={x} y1={y} x2={x + 128} y2={y} className="vt-callout-base" />
    <text x={x + 4} y={y - 5} className="vt-callout-text">{label}</text>
  </g>
);

/* -------------------- Title block (engineering corner) -------------------- */

/* The .vt-title CSS animation animates `transform: translateX(...)`, which in
   modern browsers overrides any SVG `transform` attribute applied to the same
   element — that's why the title block was rendering at (0,0). Wrap the
   .vt-title group inside an outer positioning <g> so the SVG transform lives
   on the outer group (unaffected by CSS) and the animation lives on the
   inner one. */
/* The outer wrapper uses CSS `transform` (not the SVG transform attribute) so
   it can transition smoothly when the title block follows the active drawing.
   SVG attribute transforms don't animate via CSS; CSS transforms on SVG
   elements do, in modern browsers. The inner .vt-title keeps its own fade-in
   animation independent of the position. */
const TitleBlock = ({ activeLabel, dwg, scale, rev, loc, x, y }) => (
  <g
    className="vt-title-wrap"
    style={{transform: `translate(${x}px, ${y}px)`}}
  >
    <g className="vt-title">
      <rect x="0" y="0" width="260" height="100" className="vt-title-box" />
      <line x1="0" y1="34" x2="260" y2="34" />
      <line x1="0" y1="60" x2="260" y2="60" />
      <line x1="150" y1="34" x2="150" y2="100" />
      {/* Brand mark (isotype) — sits in the header band on the left;
          wordmark text moved right to make room. */}
      <image
        href="assets/logo_isotype.png"
        x="8" y="8" width="34" height="18"
        preserveAspectRatio="xMidYMid meet"
      />
      <text x="48" y="22" className="vt-title-h">AGUILERA INGENIEROS</text>
      <text x="10" y="50" className="vt-title-k">DWG</text>
      <text x="40" y="50" className="vt-title-v">{dwg}</text>
      <text x="160" y="50" className="vt-title-k">SCALE</text>
      <text x="200" y="50" className="vt-title-v">{scale}</text>
      <text x="10" y="76" className="vt-title-k">SECTION</text>
      <text x="10" y="92" className="vt-title-v" style={{fontSize:9}}>{activeLabel}</text>
      <text x="160" y="76" className="vt-title-k">REV</text>
      <text x="200" y="76" className="vt-title-v">{rev}</text>
      <text x="164" y="92" className="vt-title-k" style={{letterSpacing:".08em"}}>{loc}</text>
    </g>
  </g>
);

const DimensionAxis = ({ marks }) => (
  <g className="vt-axis" transform="translate(80, 0)">
    <line x1="0" y1={marks[0].y - 6} x2="0" y2={marks[marks.length-1].y + 6} />
    {marks.map((m, i) => (
      <g key={i} transform={`translate(0, ${m.y})`}>
        <line x1="-5" y1="0" x2="5" y2="0" />
        <text x="-10" y="3" className="vt-axis-l">{m.l}</text>
      </g>
    ))}
  </g>
);

/* -------------------- The drawing itself -------------------- */

const ElevationDrawing = ({ activeId, verticals, onSelect }) => {
  // Floor heights come from the renderers above (DC_H / FARMA_H / EDIF_H).
  // Gaps between slabs — modest at rest, wider when a slab is active so the
  // "exploded technical drawing" effect reads cleanly. With the images now
  // contained inside their slabs, gaps no longer have to absorb image overhang.
  const GAP        = 24;
  const GAP_OPEN   = 56;

  const gap = (a, b) =>
    (activeId === a || activeId === b) ? GAP_OPEN : GAP;
  // TOP gives clearance for the "01 / DATA" block-id label that sits above
  // the first slab (label text extends ~20 px above yDC, so TOP needs ≥ ~24).
  const TOP = 50;
  const yDC    = TOP;
  const yFarma = yDC    + DC_H    + gap("cpd",   "farma");
  const yFab   = yFarma + FARMA_H + gap("farma", "fab");
  const yEdif  = yFab   + FAB_H   + gap("fab",   "edif");

  // Alto del lienzo: la pila entera con TODOS los huecos abiertos, más un
  // margen inferior. Es una constante —no depende de activeId—, así que el
  // viewBox no cambia al seleccionar y el dibujo nunca se reescala.
  const SVG_H = TOP + DC_H + FARMA_H + FAB_H + EDIF_H + 3 * GAP_OPEN + 13;

  // Section markers on the left axis — one per block, not altitudes.
  const dimMarks = [
    { l: "001", y: yDC },
    { l: "002", y: yFarma },
    { l: "003", y: yFab },
    { l: "004", y: yEdif },
  ];

  // Callout coords — per active vertical; positioned in absolute SVG space.
  // We point at structures on the active block and line them up to the right gutter.
  const v = (id) => verticals.find(x => x.id === id);
  const callouts = {
    cpd: [
      { anchorX: 440 + 250, anchorY: yDC + 18,           x: 580, y: yDC + 8,   label: v("cpd").notes[0] },
      { anchorX: 200, anchorY: yDC + 70,                  x: 580, y: yDC + 56,  label: v("cpd").notes[1] },
      { anchorX: 320, anchorY: yDC + 22,                  x: 580, y: yDC + 104, label: v("cpd").notes[2] },
    ],
    farma: [
      { anchorX: 320, anchorY: yFarma - 4,                x: 580, y: yFarma + 8,    label: v("farma").notes[0] },
      { anchorX: 240, anchorY: yFarma + 50,               x: 580, y: yFarma + 62,   label: v("farma").notes[1] },
      { anchorX: 200, anchorY: yFarma + FARMA_H - 26,     x: 580, y: yFarma + 110,  label: v("farma").notes[2] },
    ],
    fab: [
      { anchorX: 300, anchorY: yFab + 20,                 x: 580, y: yFab + 8,    label: v("fab").notes[0] },
      { anchorX: 260, anchorY: yFab + 150,                x: 580, y: yFab + 62,   label: v("fab").notes[1] },
      { anchorX: 400, anchorY: yFab + FAB_H - 60,         x: 580, y: yFab + 110,  label: v("fab").notes[2] },
    ],
    edif: [
      { anchorX: 460, anchorY: yEdif - 20,                x: 580, y: yEdif + 8,    label: v("edif").notes[0] },
      { anchorX: 380, anchorY: yEdif + 100,               x: 580, y: yEdif + 100,  label: v("edif").notes[1] },
      { anchorX: 220, anchorY: yEdif + EDIF_H - 12,       x: 580, y: yEdif + 200,  label: v("edif").notes[2] },
    ],
  };
  const activeCallouts = callouts[activeId] || [];

  const activeLabel = (v(activeId) || {}).label || "";

  // Title block follows the active drawing — vertically centred on its slab,
  // pinned to the right gutter. Per-drawing DWG number and project location.
  const slabY = { cpd: yDC,    farma: yFarma,    fab: yFab,     edif: yEdif    };
  const slabH = { cpd: DC_H,   farma: FARMA_H,   fab: FAB_H,    edif: EDIF_H   };
  // La escala la fijó el cliente y responde a lo dibujado, no a la variedad:
  // la sala técnica y el laboratorio son parecidos de tamaño y comparten
  // 1:100, la nave va a detalle y el auditorio es lo más grande. La revisión
  // sí cambia en los cuatro: un mismo REV repetido delata que es decorado.
  const meta  = {
    cpd:   { dwg: "AI-2026-01", scale: "1:100", rev: "04", loc: "MADRID · ESP"   },
    farma: { dwg: "AI-2026-02", scale: "1:100", rev: "02", loc: "MADRID · ESP"   },
    fab:   { dwg: "AI-2026-03", scale: "1:50",  rev: "01", loc: "MADRID · ESP"   },
    edif:  { dwg: "AI-2026-04", scale: "1:200", rev: "03", loc: "TENERIFE · ESP" },
  };
  const titleY = (slabY[activeId] ?? yEdif) + (slabH[activeId] ?? EDIF_H) / 2 - 50;
  const titleMeta = meta[activeId] || meta.cpd;
  const titleX = 620;
  return (
    <svg
      viewBox={"0 0 900 " + SVG_H}
      className="vt-svg"
      role="img"
      aria-label="Aguilera Ingenieros — verticals isometric"
    >
      <defs>
        <pattern id="vt-grid" width="20" height="20" patternUnits="userSpaceOnUse">
          <path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(245,245,243,0.05)" strokeWidth="0.4"/>
        </pattern>
      </defs>
      <rect x="0" y="0" width="900" height={SVG_H} fill="url(#vt-grid)" />

      {/* Corner registration marks — kept clear of the dimension axis (x=80)
          and the first section marker (y=50) so they don't clash with "001". */}
      <g transform="translate(40, 30)">
        <line x1="0" y1="0" x2="14" y2="0" className="vt-corner" />
        <line x1="0" y1="0" x2="0" y2="14" className="vt-corner" />
      </g>
      <g transform="translate(860, 30)">
        <line x1="0" y1="0" x2="-14" y2="0" className="vt-corner" />
        <line x1="0" y1="0" x2="0" y2="14" className="vt-corner" />
      </g>
      <g transform={`translate(40, ${SVG_H - 20})`}>
        <line x1="0" y1="0" x2="14" y2="0" className="vt-corner" />
        <line x1="0" y1="0" x2="0" y2="-14" className="vt-corner" />
      </g>

      {/* Live dimension axis on the left */}
      <DimensionAxis marks={dimMarks} />

      <Block id="cpd"   y={yDC}    isActive={activeId === "cpd"}   label="Data Centers"        codeShort="DATA"  codeNum="01" onSelect={onSelect}>
        <FloorDC active={activeId === "cpd"} />
      </Block>

      <Block id="farma" y={yFarma} isActive={activeId === "farma"} label="Farma & Bioseguridad" codeShort="FARMA" codeNum="02" onSelect={onSelect}>
        <FloorFarma active={activeId === "farma"} />
      </Block>

      <Block id="fab"   y={yFab}   isActive={activeId === "fab"}   label="Industria Avanzada"  codeShort="FAB"   codeNum="03" onSelect={onSelect}>
        <FloorFab active={activeId === "fab"} />
      </Block>

      <Block id="edif"  y={yEdif}  isActive={activeId === "edif"}  label="Edificios Singulares" codeShort="EDIF"  codeNum="04" onSelect={onSelect}>
        <FloorEdif active={activeId === "edif"} />
      </Block>

      {/* Engineering title block — follows the active drawing vertically */}
      <TitleBlock
        activeLabel={activeLabel}
        dwg={titleMeta.dwg}
        scale={titleMeta.scale}
        rev={titleMeta.rev}
        loc={titleMeta.loc}
        x={titleX}
        y={titleY}
      />
    </svg>
  );
};

/* -------------------- Hero — list on left, drawing on right -------------------- */

const VerticalsHero = ({ setRoute }) => {
  const { t } = useLang();
  const H = t.home;
  const verticals = H.verticals;
  const [active, setActive] = React.useState(verticals[0].id);

  /* Vertical ↔ sector mapping — verticals.id (cpd / farma / edif) lines
     up with the sector's `cat` field; we look it up once per render
     and pass the matched sector.id into setRoute so the row can
     navigate to the sector landing page. */
  const sectorFor = (vId) => {
    const s = (t.sectors || []).find(s => s.cat === vId);
    return s && s.id;
  };
  /* Per-row mobile thumbnail — picks the same isometric asset used in
     the desktop SVG blueprints. Hidden on desktop via CSS; only paints
     below 640px so the architectural identity carries over to phones
     where the full SVG illustration is hidden. */
  const thumbFor = (vId) => ({
    cpd:   "assets/dc_isometric.png",
    farma: "assets/farma_isometric.png",
    fab:   "assets/fab_isometric.png",
    edif:  "assets/auditorio_tenerife.png",
  }[vId]);
  const goSector = (vId, e) => {
    if (e && e.stopPropagation) e.stopPropagation();
    const sid = sectorFor(vId);
    if (sid && typeof setRoute === "function") {
      setRoute({route: "sector", sectorId: sid});
      window.scrollTo({top: 0, behavior: "smooth"});
    }
  };

  /* Scroll activation — compute which row's vertical centre is closest
     to the viewport centre and mark it active. Single winner per
     frame, so no oscillation between adjacent rows. Skipped below
     900px (where the layout is a single column and pinning to centre
     makes no visual sense, and the rAF loop was the source of the
     "lag on a minimised window" effect users reported earlier). */
  React.useEffect(() => {
    if (typeof window === "undefined") return;
    const DESKTOP_BP = 900;
    let raf = 0;
    let attached = false;
    let cleanupAttached = null;

    const compute = () => {
      raf = 0;
      const items = document.querySelectorAll(".vhero__item[data-id]");
      if (items.length === 0) return;
      const vpCentre = window.innerHeight / 2;
      let closestId = null;
      let closestDist = Infinity;
      items.forEach((el) => {
        const r = el.getBoundingClientRect();
        if (r.bottom < 0 || r.top > window.innerHeight) return;
        const c = r.top + r.height / 2;
        const d = Math.abs(c - vpCentre);
        if (d < closestDist) { closestDist = d; closestId = el.getAttribute("data-id"); }
      });
      if (closestId) {
        setActive((prev) => (prev === closestId ? prev : closestId));
      }
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(compute); };

    const attach = () => {
      if (attached) return;
      attached = true;
      window.addEventListener("scroll", onScroll, { passive: true });
      compute();
      cleanupAttached = () => {
        window.removeEventListener("scroll", onScroll);
        if (raf) { cancelAnimationFrame(raf); raf = 0; }
      };
    };
    const detach = () => {
      if (!attached) return;
      attached = false;
      if (cleanupAttached) cleanupAttached();
      cleanupAttached = null;
    };

    const evaluate = () => {
      if (window.innerWidth >= DESKTOP_BP) attach();
      else detach();
    };
    evaluate();
    window.addEventListener("resize", evaluate, { passive: true });
    return () => {
      window.removeEventListener("resize", evaluate);
      detach();
    };
  }, []);

  /* Click: set active immediately + smooth-scroll the page to centre
     that row. Scroll listener keeps tracking as the page glides into
     place and ultimately settles on the clicked row. */
  const selectAndScroll = (id, el) => {
    setActive(id);
    if (el && typeof el.scrollIntoView === "function") {
      el.scrollIntoView({ behavior: "smooth", block: "center" });
    }
  };

  return (
    <section className="vhero">
      <div className="vhero__intro-wrap">
        {/* Eyebrow ("Three verticals") and sub-paragraph were dropped per
            user request — the headline stands alone now, the verticals
            list below carries the rest of the story. */}
        <h1 className="vhero__title">{H.heroTitle}</h1>
      </div>

      <div className="vhero__inner">
        <ul className="vhero__list">
          {verticals.map((v, i) => {
            const isActive = v.id === active;
            return (
              <li key={v.id}
                data-id={v.id}
                data-pos={i}
                className={"vhero__item " + (isActive ? "is-active" : "")}
                onClick={(e) => selectAndScroll(v.id, e.currentTarget)}>
                <span className="vhero__num">{v.tag}</span>
                <div className="vhero__body">
                  {/* Mobile-only isometric thumbnail — sits at the top of
                      the body. CSS hides it on desktop. */}
                  {thumbFor(v.id) && (
                    <img className="vhero__thumb"
                         src={thumbFor(v.id)}
                         alt=""
                         loading="lazy"
                         aria-hidden="true" />
                  )}
                  <div className="vhero__label">{v.label}</div>
                  <div className="vhero__short">{v.short}</div>
                  {/* Antes aquí iban los dos párrafos largos del área y
                      debajo el enlace. El cliente los quitó el 23 de
                      septiembre: cada área se presenta con una sola frase
                      —la de arriba— y el enlace. El texto largo sigue en
                      los datos y en el CMS, sin pintarse, porque la ficha
                      del área lo usa.
                      El bloque sigue existiendo para que el enlace se
                      pliegue en las filas inactivas igual que antes. */}
                  {sectorFor(v.id) && (
                    <div className="vhero__expand">
                      <a className="vhero__sector-link"
                         role="link"
                         onClick={(e) => goSector(v.id, e)}>
                        {t.proyectos && t.proyectos.viewSector
                          ? t.proyectos.viewSector
                          : "Ver sector"} →
                      </a>
                    </div>
                  )}
                </div>
                {/* Right-edge arrow — visual indicator that the row is
                    clickable. Routes to the sector page (separate from
                    the row's activate-and-scroll behaviour); stops
                    propagation so clicking the arrow doesn't double-fire. */}
                <a className="vhero__arrow"
                   role="link"
                   aria-label={"Sector — " + v.label}
                   onClick={(e) => goSector(v.id, e)}>
                  →
                </a>
              </li>
            );
          })}
        </ul>

        <div className="vhero__right">
          <ElevationDrawing
            activeId={active}
            verticals={verticals}
            onSelect={(id) => {
              const el = document.querySelector(`.vhero__item[data-id="${id}"]`);
              selectAndScroll(id, el);
            }}
          />
        </div>
      </div>
    </section>
  );
};

Object.assign(window, { VerticalsHero, ElevationDrawing });
