// PROTOTYPE - Frame anatomy diagram, redrawn as inline SVG.
//
// Replaces brochure page 3, whose labelled frame drawing is baked into a
// full-page 72 DPI raster (612x791px on a 612x792pt page) and so cannot be
// lifted at any usable size. Everything here is vector, themes with the rest
// of the site through the CSS custom properties, and unlike the PDF it can be
// interrogated: pick a member and it highlights in place.
//
// Not wired into routing. See _proto-frame-anatomy.html for the harness.
//
// COPY STATUS: the member names are the brochure's own labels. The one-line
// descriptions are written here, drawing on what the brochure states
// elsewhere (clear spans 30'-200', purlin strapping, base angle sealing, jamb
// and header cover trim) - they need Brad's sign-off before this ships.

//. PROJECTION ,
// Axonometric, hand-rolled rather than a 3D lib: the whole point is a drawing
// that stays crisp and themeable at any size. Building X runs left-right along
// the front endwall, Y runs back-right into the page, Z is up.
const FA_OX = 55;      // screen origin, front-left base corner
const FA_OY = 372;
const FA_KY = 0.55;    // how far +Y pushes right
const FA_KZ = 0.32;    // how far +Y lifts up the page

const faX = (x, y) => FA_OX + x + FA_KY * y;
const faY = (y, z) => FA_OY - FA_KZ * y - z;
const faPt = ([x, y, z]) => `${faX(x, y).toFixed(1)},${faY(y, z).toFixed(1)}`;
const faPoly = (pts) => pts.map(faPt).join(' ');

//. BUILDING ,
// A 300 x 260 clear-span gable with a framed opening in the front endwall.
// Nominal units, not feet - the diagram is schematic.
const FA_W = 360;      // span, gable to gable
const FA_D = 310;      // length, front endwall to back
const FA_EAVE = 145;
const FA_RIDGE = 210;
const FA_MID = FA_W / 2;
const FA_BAY = 155;    // the one interior rigid frame we draw
const FA_DOOR = { x0: 132, x1: 228, head: 84 };
const FA_GIRT = [48, 108];   // girt heights up the wall

// Height of the roof plane at a given X, used to sit purlins on the slope.
const faRoofZ = (x) => x <= FA_MID
  ? FA_EAVE + (x / FA_MID) * (FA_RIDGE - FA_EAVE)
  : FA_EAVE + ((FA_W - x) / FA_MID) * (FA_RIDGE - FA_EAVE);

const faPurlin = (x) => [[x, 0, faRoofZ(x)], [x, FA_D, faRoofZ(x)]];

//. THE STRUCTURE, ALWAYS VISIBLE ,
// Drawn once underneath as a dim ghost so the building reads as a building
// even when a single member is lit. Members are drawn again on top.
const FA_GHOST = [
  // front endwall
  [[0, 0, 0], [FA_W, 0, 0]],
  [[0, 0, 0], [0, 0, FA_EAVE]],
  [[FA_W, 0, 0], [FA_W, 0, FA_EAVE]],
  [[0, 0, FA_EAVE], [FA_MID, 0, FA_RIDGE]],
  [[FA_W, 0, FA_EAVE], [FA_MID, 0, FA_RIDGE]],
  // back endwall
  [[0, FA_D, 0], [FA_W, FA_D, 0]],
  [[0, FA_D, 0], [0, FA_D, FA_EAVE]],
  [[FA_W, FA_D, 0], [FA_W, FA_D, FA_EAVE]],
  [[0, FA_D, FA_EAVE], [FA_MID, FA_D, FA_RIDGE]],
  [[FA_W, FA_D, FA_EAVE], [FA_MID, FA_D, FA_RIDGE]],
  // ridge, eaves, base
  [[FA_MID, 0, FA_RIDGE], [FA_MID, FA_D, FA_RIDGE]],
  [[0, 0, FA_EAVE], [0, FA_D, FA_EAVE]],
  [[FA_W, 0, FA_EAVE], [FA_W, FA_D, FA_EAVE]],
  [[0, 0, 0], [0, FA_D, 0]],
  [[FA_W, 0, 0], [FA_W, FA_D, 0]],
  // interior rigid frame
  [[0, FA_BAY, 0], [0, FA_BAY, FA_EAVE]],
  [[FA_W, FA_BAY, 0], [FA_W, FA_BAY, FA_EAVE]],
  [[0, FA_BAY, FA_EAVE], [FA_MID, FA_BAY, FA_RIDGE]],
  [[FA_W, FA_BAY, FA_EAVE], [FA_MID, FA_BAY, FA_RIDGE]],
];

// Faint plane fills, just enough to tell which faces point at you.
const FA_PLANES = [
  [[0, 0, 0], [FA_W, 0, 0], [FA_W, 0, FA_EAVE], [FA_MID, 0, FA_RIDGE], [0, 0, FA_EAVE]],
  [[FA_W, 0, 0], [FA_W, FA_D, 0], [FA_W, FA_D, FA_EAVE], [FA_W, 0, FA_EAVE]],
  [[FA_MID, 0, FA_RIDGE], [FA_W, 0, FA_EAVE], [FA_W, FA_D, FA_EAVE], [FA_MID, FA_D, FA_RIDGE]],
  [[0, 0, FA_EAVE], [FA_MID, 0, FA_RIDGE], [FA_MID, FA_D, FA_RIDGE], [0, FA_D, FA_EAVE]],
];

//. MEMBERS ,
// Order runs the way the building goes together: primary frame, then
// secondary, then the framed opening.
const FA_MEMBERS = [
  {
    key: 'rigid-column', label: 'Rigid Frame Column',
    blurb: "The main vertical of a clear-span frame. Bolted to the slab at the base plate, and it carries the whole roof load down to the foundation.",
    lines: [
      [[0, FA_BAY, 0], [0, FA_BAY, FA_EAVE]],
      [[FA_W, FA_BAY, 0], [FA_W, FA_BAY, FA_EAVE]],
    ],
  },
  {
    key: 'rigid-rafter', label: 'Rigid Frame Rafter',
    blurb: "The sloped top of the frame. It spans the building with nothing underneath it, which is what gives you a clear floor from wall to wall - 30' through 200'.",
    lines: [
      [[0, FA_BAY, FA_EAVE], [FA_MID, FA_BAY, FA_RIDGE]],
      [[FA_W, FA_BAY, FA_EAVE], [FA_MID, FA_BAY, FA_RIDGE]],
    ],
  },
  {
    key: 'endwall-rafter', label: 'Endwall Rafter',
    blurb: 'The sloped member on the end frame. Lighter than an interior rafter, because an endwall picks up roughly half the load one in the middle does.',
    lines: [
      [[0, 0, FA_EAVE], [FA_MID, 0, FA_RIDGE]],
      [[FA_W, 0, FA_EAVE], [FA_MID, 0, FA_RIDGE]],
    ],
  },
  {
    key: 'corner-column', label: 'Corner Column',
    blurb: 'The post at each corner of the building. Ties the endwall to the sidewall and takes girts coming in from both directions.',
    lines: [
      [[0, 0, 0], [0, 0, FA_EAVE]],
      [[FA_W, 0, 0], [FA_W, 0, FA_EAVE]],
    ],
  },
  {
    key: 'eave-strut', label: 'Eave Strut',
    blurb: 'Runs the length of the building at the eave, tying the frames together and giving roof and wall panels one common line to land on.',
    lines: [
      [[0, 0, FA_EAVE], [0, FA_D, FA_EAVE]],
      [[FA_W, 0, FA_EAVE], [FA_W, FA_D, FA_EAVE]],
    ],
  },
  {
    key: 'roof-purlin', label: 'Roof Purlin',
    blurb: 'Spans frame to frame to carry the roof panels. We add purlin strapping where it helps, which stops the purlins rotating and sagging over time.',
    lines: [faPurlin(60), faPurlin(120), faPurlin(240), faPurlin(300)],
  },
  {
    key: 'sidewall-girt', label: 'Sidewall Girt',
    blurb: 'Horizontal members up the sidewall. They carry the wall panels and hand wind load off to the columns.',
    lines: FA_GIRT.map(z => [[FA_W, 0, z], [FA_W, FA_D, z]]),
  },
  {
    key: 'endwall-girt', label: 'Endwall Girt',
    blurb: 'Same job as a sidewall girt, on the end of the building. Runs interrupted around a framed opening rather than through it.',
    // The upper girt runs clear; the lower one is broken by the opening.
    lines: [
      [[0, 0, FA_GIRT[1]], [FA_W, 0, FA_GIRT[1]]],
      [[0, 0, FA_GIRT[0]], [FA_DOOR.x0, 0, FA_GIRT[0]]],
      [[FA_DOOR.x1, 0, FA_GIRT[0]], [FA_W, 0, FA_GIRT[0]]],
    ],
  },
  {
    key: 'base-angle', label: 'Base Angle',
    blurb: 'Runs the perimeter at the slab. Panels fasten to it, and together with base trim it seals the bottom of the wall against water, dirt, and critters.',
    lines: [
      [[0, 0, 0], [FA_W, 0, 0]],
      [[FA_W, 0, 0], [FA_W, FA_D, 0]],
    ],
  },
  {
    key: 'door-header', label: 'Door Header',
    blurb: "The beam across the top of a framed opening. It carries the wall above so the door doesn't have to.",
    lines: [[[FA_DOOR.x0, 0, FA_DOOR.head], [FA_DOOR.x1, 0, FA_DOOR.head]]],
  },
  {
    key: 'door-jamb', label: 'Door Jamb',
    blurb: 'The vertical each side of a framed opening. Jamb and header cover trim finishes it off once the door is hung.',
    lines: [
      [[FA_DOOR.x0, 0, 0], [FA_DOOR.x0, 0, FA_DOOR.head]],
      [[FA_DOOR.x1, 0, 0], [FA_DOOR.x1, 0, FA_DOOR.head]],
    ],
  },
];

const faNum = (i) => String(i + 1).padStart(2, '0');

// Floating tag pinned to the midpoint of a member's first run. Width is
// estimated off the character count because SVG text has no layout box to
// measure before paint, and Geist Mono at 10px sits right around 6px/char.
const FaTag = ({ member, index }) => {
  const [p0, p1] = member.lines[0];
  const mx = (faX(p0[0], p0[1]) + faX(p1[0], p1[1])) / 2;
  const my = (faY(p0[1], p0[2]) + faY(p1[1], p1[2])) / 2;
  const text = `${faNum(index)} ${member.label.toUpperCase()}`;
  const w = text.length * 6.1 + 18;
  const h = 20;
  // Flip the tag to the left of the anchor when it would run off the right edge.
  const flip = mx + 22 + w > 700;
  const bx = flip ? mx - 22 - w : mx + 22;
  const by = my - 30;
  return (
    <g style={{ pointerEvents: 'none' }}>
      <line x1={mx} y1={my} x2={flip ? bx + w : bx} y2={by + h / 2}
        stroke="var(--accent)" strokeWidth="1" strokeDasharray="2 2" opacity="0.7" />
      <circle cx={mx} cy={my} r="2.5" fill="var(--accent)" />
      <rect x={bx} y={by} width={w} height={h} fill="var(--bg)" stroke="var(--accent)" strokeWidth="1" />
      <text x={bx + 9} y={by + 14}
        fontFamily="Geist Mono, ui-monospace, monospace" fontSize="10"
        letterSpacing="0.06em" fill="var(--accent)">{text}</text>
    </g>
  );
};

const FrameAnatomy = ({ initial = 'rigid-column' }) => {
  // `pinned` survives the pointer leaving the diagram, so a visitor can read a
  // long description without keeping the cursor on a 2px line. Hover wins while
  // it is happening; the pin is what we fall back to.
  const [pinned, setPinned] = React.useState(initial);
  const [hovered, setHovered] = React.useState(null);
  const activeKey = hovered || pinned;
  const activeIndex = FA_MEMBERS.findIndex(m => m.key === activeKey);
  const active = FA_MEMBERS[activeIndex];

  return (
    <section className="section" style={{ position: 'relative' }}>
      <div className="grid-bg" style={{
        position: 'absolute', inset: 0, opacity: 0.22,
        maskImage: 'radial-gradient(ellipse at center, black 25%, transparent 70%)',
        WebkitMaskImage: 'radial-gradient(ellipse at center, black 25%, transparent 70%)',
      }}></div>

      <div className="page" style={{ position: 'relative' }}>
        <SectionEyebrow id="FIG. 01" label="Anatomy of a Chambliss frame" />
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', flexWrap: 'wrap', gap: 24 }}>
          <h2 style={{ maxWidth: 620 }}>Frame anatomy.</h2>
          <span className="mono" style={{ fontSize: 10, color: 'var(--fg-faint)', letterSpacing: '0.08em' }}>
            CLEAR-SPAN RIGID FRAME · SCHEMATIC
          </span>
        </div>

        <p style={{ marginTop: 20, maxWidth: 620, fontSize: 16, color: 'var(--fg-dim)' }}>
          Pick any member to see where it sits and what it carries.
        </p>

        <div className="mob-stack" style={{
          marginTop: 48,
          display: 'grid', gridTemplateColumns: '1.55fr 1fr', gap: 0,
          border: '1px solid var(--border)', background: 'var(--bg-card)',
        }}>
          {/* DIAGRAM. aria-hidden because the legend beside it is the same
              information in text, and every member there is a real button - a
              screen reader gets the full list without crawling a wireframe. */}
          {/* Centred rather than top-aligned: the legend beside it is taller
              than the drawing's natural height, and stretching the grid cell
              would otherwise dump all the slack under the building. */}
          <div style={{
            borderRight: '1px solid var(--border)', padding: 16,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <svg viewBox="0 18 720 384" width="100%" aria-hidden="true"
              style={{ display: 'block', overflow: 'visible' }}>
              {FA_PLANES.map((pl, i) => (
                <polygon key={i} points={faPoly(pl)} fill="var(--fg)" opacity="0.035" />
              ))}

              {FA_GHOST.map((ln, i) => (
                <line key={i}
                  x1={faX(ln[0][0], ln[0][1])} y1={faY(ln[0][1], ln[0][2])}
                  x2={faX(ln[1][0], ln[1][1])} y2={faY(ln[1][1], ln[1][2])}
                  stroke="var(--border-strong)" strokeWidth="1" />
              ))}

              {FA_MEMBERS.map((m) => {
                const on = m.key === activeKey;
                return (
                  <g key={m.key}
                    onMouseEnter={() => setHovered(m.key)}
                    onMouseLeave={() => setHovered(null)}
                    onClick={() => setPinned(m.key)}
                    style={{ cursor: 'pointer' }}>
                    {/* fat invisible stroke so a 2px line is actually hoverable */}
                    {m.lines.map((ln, j) => (
                      <line key={`h${j}`}
                        x1={faX(ln[0][0], ln[0][1])} y1={faY(ln[0][1], ln[0][2])}
                        x2={faX(ln[1][0], ln[1][1])} y2={faY(ln[1][1], ln[1][2])}
                        stroke="transparent" strokeWidth="14" />
                    ))}
                    {m.lines.map((ln, j) => (
                      <line key={j}
                        x1={faX(ln[0][0], ln[0][1])} y1={faY(ln[0][1], ln[0][2])}
                        x2={faX(ln[1][0], ln[1][1])} y2={faY(ln[1][1], ln[1][2])}
                        stroke={on ? 'var(--accent)' : 'var(--fg-faint)'}
                        strokeWidth={on ? 3.5 : 1.6}
                        strokeLinecap="square"
                        opacity={on ? 1 : 0.68}
                        style={{ transition: 'stroke .18s, stroke-width .18s, opacity .18s' }} />
                    ))}
                  </g>
                );
              })}

              {active && <FaTag member={active} index={activeIndex} />}
            </svg>
          </div>

          {/* LEGEND */}
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <div className="mono" style={{
              fontSize: 10, letterSpacing: '0.08em', color: 'var(--fg-faint)',
              padding: '18px 20px', borderBottom: '1px solid var(--border)',
            }}>
              MEMBERS · {FA_MEMBERS.length}
            </div>

            <div style={{ flex: 1 }}>
              {FA_MEMBERS.map((m, i) => {
                const on = m.key === activeKey;
                return (
                  <button key={m.key}
                    onMouseEnter={() => setHovered(m.key)}
                    onMouseLeave={() => setHovered(null)}
                    onFocus={() => setHovered(m.key)}
                    onBlur={() => setHovered(null)}
                    onClick={() => setPinned(m.key)}
                    aria-pressed={m.key === pinned}
                    style={{
                      width: '100%', textAlign: 'left', background: on ? 'var(--bg-elev)' : 'transparent',
                      border: 'none', borderBottom: '1px solid var(--border)',
                      borderLeft: `2px solid ${on ? 'var(--accent)' : 'transparent'}`,
                      padding: '9px 20px', display: 'flex', alignItems: 'baseline', gap: 12,
                      color: on ? 'var(--fg)' : 'var(--fg-dim)',
                      transition: 'background .15s, color .15s, border-color .15s',
                    }}>
                    <span className="mono" style={{ fontSize: 10, color: on ? 'var(--accent)' : 'var(--fg-faint)' }}>
                      {faNum(i)}
                    </span>
                    <span style={{ fontSize: 13.5 }}>{m.label}</span>
                  </button>
                );
              })}
            </div>

            {/* Fixed-height so stepping through members does not shunt the
                page up and down under the cursor. */}
            <div style={{ padding: '18px 20px', background: 'var(--bg-elev)', minHeight: 132 }}>
              {active && (
                <>
                  <div className="mono" style={{ fontSize: 10, letterSpacing: '0.08em', color: 'var(--accent)', marginBottom: 8 }}>
                    {faNum(activeIndex)} / {active.label.toUpperCase()}
                  </div>
                  <p style={{ margin: 0, fontSize: 13.5, lineHeight: 1.55, color: 'var(--fg-dim)' }}>
                    {active.blurb}
                  </p>
                </>
              )}
            </div>
          </div>
        </div>

        <div className="mono" style={{
          marginTop: 14, fontSize: 10, color: 'var(--fg-faint)', letterSpacing: '0.08em',
        }}>
          SCHEMATIC · NOT TO SCALE · MEMBER SIZES SET BY ENGINEERING FOR YOUR LOADS
        </div>
      </div>
    </section>
  );
};

Object.assign(window, { FrameAnatomy });
