#include "io/CatalogLoader.h"
#include <cmath>


namespace MC {

CatalogLoader& GetCatalog() {
    static CatalogLoader instance;
    if (!instance.IsLoaded())
        instance.LoadBuiltIn();
    return instance;
}

// ── Helpers ───────────────────────────────────────────────────────────────────
static ConnectionPoint CP(double x, double y, double heading, int idx) {
    return ConnectionPoint(Vec2{x, y}, heading, idx);
}

static TrackPiece MakeStraight(TrackSeries series,
    const char* cat, const char* name, double length)
{
    TrackPiece p;
    p.series        = series;
    p.catalogNumber = cat;
    p.displayName   = name;
    p.type          = PieceType::Straight;
    p.lengthMm      = length;
    p.radiusMm      = 0;
    p.angleDeg      = 0;
    p.gridW = 1; p.gridH = 1;
    p.connections   = { CP(0, 0, 180, 0), CP(length, 0, 0, 1) };
    return p;
}

static TrackPiece MakeCurve(TrackSeries series,
    const char* cat, const char* name,
    double radius, double angleDeg)
{
    // exit x = R*sin(angle), exit y = R*(1-cos(angle))
    double aRad = angleDeg * M_PI / 180.0;
    double ex   = radius * std::sin(aRad);
    double ey   = radius * (1.0 - std::cos(aRad));
    double arcLen = radius * aRad;

    TrackPiece p;
    p.series        = series;
    p.catalogNumber = cat;
    p.displayName   = name;
    p.type          = PieceType::Curve;
    p.lengthMm      = arcLen;
    p.radiusMm      = radius;
    p.angleDeg      = angleDeg;
    p.gridW = 1; p.gridH = 1;
    p.connections   = { CP(0, 0, 180, 0), CP(ex, ey, angleDeg, 1) };
    return p;
}

static TrackPiece MakeTurnout(TrackSeries series,
    const char* cat, const char* name,
    PieceType type, double length, double radius, double angleDeg)
{
    double branchAngle = (type == PieceType::TurnoutLeft) ? -angleDeg : angleDeg;

    double aRad  = angleDeg * M_PI / 180.0;
    double ex    = std::min(radius * std::sin(aRad), length);

    // Derive the angle actually subtended by the clamped ex distance
    double cappedRad = std::asin(std::min(ex / radius, 1.0));
    double cappedDeg = cappedRad * 180.0 / M_PI;

    // ey and heading use the capped angle so the dot sits on the arc, not past it
    double ey_mag      = radius * (1.0 - std::cos(cappedRad));
    double ey          = (type == PieceType::TurnoutLeft) ? -ey_mag : ey_mag;
    double exitHeading = (type == PieceType::TurnoutLeft)
                         ? 360.0 - cappedDeg : cappedDeg;

    TrackPiece p;
    p.series        = series;
    p.catalogNumber = cat;
    p.displayName   = name;
    p.type          = type;
    p.lengthMm      = length;
    p.radiusMm      = radius;
    p.angleDeg      = branchAngle;
    p.gridW = 2; p.gridH = 1;
    p.connections   = {
        CP(0,      0,   180,         0),
        CP(length, 0,   0,           1),
        CP(ex,     ey,  exitHeading, 2)
    };
    return p;
}

static TrackPiece MakeCrossing(TrackSeries series,
    const char* cat, const char* name,
    double length, double radius, double angleDeg)
{
    double aRad = angleDeg * M_PI / 180.0;
    // Route 1 (horizontal): (0,0) to (length,0), crosses at centre (length/2, 0).
    // Route 2 crosses through the same centre at angleDeg, same length.
    double cx = length / 2.0;
    double half = length / 2.0;
    double nwx = cx - half * std::cos(aRad);
    double nwy =    - half * std::sin(aRad);
    double sex = cx + half * std::cos(aRad);
    double sey =      half * std::sin(aRad);
    TrackPiece p;
    p.series = series; p.catalogNumber = cat; p.displayName = name;
    p.type = PieceType::Crossing; p.lengthMm = length;
    p.radiusMm = radius; p.angleDeg = angleDeg;
    p.gridW = 2; p.gridH = 2;
    p.connections = {
        CP(0,      0,   180,          0),   // route 1 west
        CP(length, 0,   0,            1),   // route 1 east
        CP(nwx,    nwy, 180+angleDeg, 2),   // route 2 NW
        CP(sex,    sey, angleDeg,     3)    // route 2 SE
    };
    return p;
}

static TrackPiece MakeDoubleSlip(TrackSeries series,
    const char* cat, const char* name,
    double length, double radius, double angleDeg)
{
    double aRad = angleDeg * M_PI / 180.0;
    // Two straight routes crossing through centre (length/2, 0), like a crossing.
    // Curved slip routes (radius, angleDeg) connect the corners internally.
    double cx = length / 2.0;
    double half = length / 2.0;
    double nwx = cx - half * std::cos(aRad);
    double nwy =    - half * std::sin(aRad);
    double sex = cx + half * std::cos(aRad);
    double sey =      half * std::sin(aRad);
    TrackPiece p;
    p.series = series; p.catalogNumber = cat; p.displayName = name;
    p.type = PieceType::DoubleCrossover; p.lengthMm = length;
    p.radiusMm = radius; p.angleDeg = angleDeg;
    p.gridW = 2; p.gridH = 2;
    p.connections = {
        CP(0,      0,   180,          0),   // route 1 west
        CP(length, 0,   0,            1),   // route 1 east
        CP(nwx,    nwy, 180+angleDeg, 2),   // route 2 NW
        CP(sex,    sey, angleDeg,     3)    // route 2 SE
    };
    return p;
}

static TrackPiece MakeBumper(TrackSeries series,
    const char* cat, const char* name, double length)
{
    TrackPiece p;
    p.series = series; p.catalogNumber = cat; p.displayName = name;
    p.type = PieceType::Bumper; p.lengthMm = length;
    p.radiusMm = 0; p.angleDeg = 0;
    p.gridW = 1; p.gridH = 1;
    p.connections = { CP(0, 0, 180, 0) };
    return p;
}

// ── LoadBuiltIn ───────────────────────────────────────────────────────────────
void CatalogLoader::LoadBuiltIn() {
    m_pieces.clear();
    m_index.clear();

    auto Add = [&](TrackPiece p) {
        m_index[p.catalogNumber] = m_pieces.size();
        m_pieces.push_back(std::move(p));
    };

    // ── Märklin C-Track ───────────────────────────────────────────────────────
    const auto C = TrackSeries::MarlinC;

    // Straights
    Add(MakeStraight(C, "24188", "24188 - 188.3mm", 188.3));
    Add(MakeStraight(C, "24172", "24172 - 171.7mm", 171.7));
    Add(MakeStraight(C, "24360", "24360 - 360mm",   360.0));
    {
        TrackPiece p = MakeStraight(C, "74636", "74636 Arch Bridge 360mm", 360.0);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeStraight(C, "74620", "74620 Truss Bridge 180mm", 188.3);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(C, "74623", "74623 Curved Ramp R2 30deg", 437.5, 30.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(C, "74613", "74613 Curved Ramp R1 30deg", 360.0, 30.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }
    {
        TrackPiece p = MakeStraight(C, "74618", "74618 Straight Ramp 180mm (pair)", 180.0);
        p.isBridge = true; p.isRamp = true; p.pairPiece = true;
        Add(p);
    }
    Add(MakeStraight(C, "24229", "24229 - 229.3mm", 229.3));
    Add(MakeStraight(C, "24236", "24236 - 236.1mm", 236.1));
    Add(MakeStraight(C, "24094", "24094 - 94.2mm",   94.2));
    Add(MakeStraight(C, "24077", "24077 - 77.5mm",   77.5));
    Add(MakeStraight(C, "24071", "24071 - 70.8mm",   70.8));
    Add(MakeStraight(C, "24064", "24064 - 64.3mm",   64.3));

    // Curves (radius mm, 30° per piece)
    Add(MakeCurve(C, "24130", "24130 (30°) R1 - 360mm (14-3/16)",    360.0,  30.0));
    Add(MakeCurve(C, "24230", "24230 (30°) R2 - 437.5mm (17-1/4)",  437.5,  30.0));
    Add(MakeCurve(C, "24330", "24330 (30°) R3 - 515mm (20-1/4)",    515.0,  30.0));
    Add(MakeCurve(C, "24315", "24315 (15°) R3 - 515mm (20-1/4)",    515.0,  15.0));
    Add(MakeCurve(C, "24430", "24430 (30°) R4 - 579.3mm (22-13/16)",  579.3,  30.0));
    Add(MakeCurve(C, "24530", "24530 (30°) R5 - 643.6mm (25-11/32)",  643.6,  30.0));
    Add(MakeCurve(C, "24115", "24115 (15°) R1 - 360mm (14-3/16)",    360.0,  15.0));
    Add(MakeCurve(C, "24107", "24107 (7.5°) R1 - 360mm (14-3/16)",    360.0,  7.5));
    Add(MakeCurve(C, "24224", "24224 (24.3°) R2 - 437.5mm (17-1/4)",  437.5, 24.3));
    Add(MakeCurve(C, "24206", "24206 (5.7°) R2 - 437.5mm (17-1/4)",    437.5,  5.7));
    Add(MakeCurve(C, "24207", "24207 (7.5°) R2 - 437.5mm (17-1/4)",    437.5,  7.5));
    Add(MakeCurve(C, "24294", "24294 (15°) R2 - 437.5mm (17-1/4) Circuit", 437.5, 15.0));
    Add(MakeStraight(C, "24994", "24994 - 94.2mm Circuit", 94.2));
    Add(MakeCurve(C, "24215", "24215 (15°) R2 - 437.5mm (17-1/4)",    437.5, 15.0));

    Add(MakeCurve(C, "24912", "24912 (12.1°) R9 - 1114.6mm (43-7/8)", 1114.6, 12.1));

    // Turnouts
    // 24611 - Left turnout: straight 188.3mm + left curve R=360mm 30deg
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24611"; p.displayName = "24611 Left Turnout R2=437.5mm 24.3°";
        p.type = PieceType::TurnoutLeft; p.lengthMm = 188.3;
        p.radiusMm = 437.5; p.angleDeg = -24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,      180,    0),   // entry
            CP(188.3,  0,        0,    1),   // straight exit
            CP(180.04, -38.76, 335.7,  2),   // left branch exit R2 24.3°
        };
        Add(p);
    }

    // 24612 - Right turnout: straight 188.3mm + right curve R=360mm 30deg
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24612"; p.displayName = "24612 Right Turnout R2=437.5mm 24.3°";
        p.type = PieceType::TurnoutRight; p.lengthMm = 188.3;
        p.radiusMm = 437.5; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,     180,   0),   // entry
            CP(188.3,  0,       0,   1),   // straight exit
            CP(180.04, 38.76, 24.3,  2),   // right branch exit R2 24.3°
        };
        Add(p);
    }

    // 24630 - Triple switch: entry + straight + left curve + right curve
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24630"; p.displayName = "24630 Three-Way Turnout 2x24.3°";
        p.type = PieceType::TurnoutLeft; p.lengthMm = 188.3;
        p.radiusMm = 437.5; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(0,      0,       180,    0),   // entry
            CP(188.3,  0,         0,    1),   // straight exit
            CP(180.04, 38.76,   24.3,   2),   // left branch R2 24.3°
            CP(180.04, -38.76, 335.7,   3),   // right branch R2 24.3°
        };
        Add(p);
    }





    // 24671 - Left Curved Turnout
    // Route 1: 24130 curve left (R=360, 30deg) from entry
    // Route 2: 77.5mm straight + 24130 curve left (R=360, 30deg)
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24671";
        p.displayName = "24671 Left Curved Turnout (R360)";
        p.type = PieceType::TurnoutY; p.lengthMm = 257.5;
        p.radiusMm = 360; p.angleDeg = -30; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,     0,     180, 0),  // entry
            CP(180.0, -48.2, 330, 1),  // route 1: curves left on screen
            CP(257.5, -48.2, 330, 2),  // route 2: 77.5mm + curves left on screen
        };
        Add(p);
    }

    // 24672 - Right Curved Turnout (mirror of 24671)
    // Route 1: 24130 curve right from entry
    // Route 2: 77.5mm straight + 24130 curve right
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24672";
        p.displayName = "24672 Right Curved Turnout (R360)";
        p.type = PieceType::TurnoutY; p.lengthMm = 257.5;
        p.radiusMm = 360; p.angleDeg = 30; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,     0,    180, 0),  // entry
            CP(180.0, 48.2, 30,  1),  // route 1: 24130 right curve exit
            CP(257.5, 48.2, 30,  2),  // route 2: 77.5mm + 24130 right curve exit
        };
        Add(p);
    }





    // 24740 - Wide-Angle Crossing (236.1mm, 12.1deg)
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24740";
        p.displayName = "24740 Wide-Angle Crossing 12.1° 236.1mm";
        p.type = PieceType::Crossing; p.lengthMm = 236.1;
        p.radiusMm = 0; p.angleDeg = 12.1; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,      180,         0),  // route 1 entry
            CP(236.1,  0,      0,           1),  // route 1 exit
            CP(2.62,   -24.75, 180+12.1,    2),  // route 2 entry
            CP(233.48, 24.75,  12.1,        3),  // route 2 exit
        };
        Add(p);
    }

    // 24649 - C-Track Crossing (103.3mm, 48.6deg)
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24649";
        p.displayName = "24649 Crossing 48.6° 103.3mm";
        p.type = PieceType::Crossing; p.lengthMm = 103.3;
        p.radiusMm = 0; p.angleDeg = 48.6; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,      180,         0),
            CP(103.3,  0,      0,           1),
            CP(17.49,  -38.74, 180+48.6,    2),
            CP(85.81,  38.74,  48.6,        3),
        };
        Add(p);
    }

    // 24640 - C-Track Crossing (188.3mm, 24.3deg)
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24640";
        p.displayName = "24640 Crossing 24.3° 188.3mm";
        p.type = PieceType::Crossing; p.lengthMm = 188.3;
        p.radiusMm = 0; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,      180,         0),
            CP(188.3,  0,      0,           1),
            CP(8.34,   -38.74, 180+24.3,    2),
            CP(179.96, 38.74,  24.3,        3),
        };
        Add(p);
    }

    // 24624 - Double Slip Switch (Doppelkreuzungsweiche) 24.3° R≈437mm
    // Four routes: straight-through on both axes, plus two diagonal slip arcs.
    // Slip arc radius = 437.4mm (≈R2), geometrically derived from connection points.
    // Slip arc_a: conn[0]→conn[3], centre=(0,+437.4), sweep=+24.3°
    // Slip arc_b: conn[2]→conn[1], centre=(188.3,-437.4), sweep=-24.3°
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24624";
        p.displayName = "24624 Double Slip Switch 24.3°";
        p.type = PieceType::DoubleCrossover; p.lengthMm = 188.3;
        p.radiusMm = 437.4; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(0,      0,       180,       0),  // route1 entry  (W)
            CP(188.3,  0,         0,       1),  // route1 exit   (E)
            CP(8.34,  -38.74,  204.3,      2),  // route2 entry  (SW)
            CP(179.96, 38.74,   24.3,      3),  // route2 exit   (NE)
        };
        Add(p);
    }

    // 24720 - Double Slip Switch slim/high-speed 12.1° R≈1114mm
    // Same topology as 24624 but shallower angle and wider-radius slip paths.
    // Slip arc radius = 1114.6mm (≈R6), same as slim turnouts 24711/24712.
    // Slip arc_a: conn[0]→conn[3], centre=(0,+1114.6), sweep=+12.1°
    // Slip arc_b: conn[2]→conn[1], centre=(236.1,-1114.6), sweep=-12.1°
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24720";
        p.displayName = "24720 Double Slip Switch slim 12.1°";
        p.type = PieceType::DoubleCrossover; p.lengthMm = 236.1;
        p.radiusMm = 1114.6; p.angleDeg = 12.1; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(0,      0,       180,       0),  // route1 entry  (W)
            CP(236.1,  0,         0,       1),  // route1 exit   (E)
            CP(2.62,  -24.75,  192.1,      2),  // route2 entry  (SW)
            CP(233.48, 24.75,   12.1,      3),  // route2 exit   (NE)
        };
        Add(p);
    }

    // 24711 - Slim Straight Left Turnout (R=1114.6mm, 12.1deg, length 236.1mm)
    // Branch connector x is set to 233.4847mm (< straight 236.1mm) so that
    // the crossover geometry 24711-B + 24071 + 24229 == 24711-C + 24071 + 24711-C
    // closes exactly. Derived by solving the alignment constraint algebraically.
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24711";
        p.displayName = "24711 Slim Left Turnout (R1114.6, 236.1mm)";
        p.type = PieceType::TurnoutLeft; p.lengthMm = 236.1;
        p.radiusMm = 1114.6; p.angleDeg = -12.1; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,       0,       180,     0),
            CP(236.1,   0,       0,       1),
            CP(233.485, -24.729, 347.908, 2),
        };
        Add(p);
    }

    // 24712 - Slim Straight Right Turnout (R=1114.6mm, 12.1deg, length 236.1mm)
    // Mirror of 24711; branch y is positive (diverges right/up).
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24712";
        p.displayName = "24712 Slim Right Turnout (R1114.6, 236.1mm)";
        p.type = PieceType::TurnoutRight; p.lengthMm = 236.1;
        p.radiusMm = 1114.6; p.angleDeg = -12.1; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,       0,      180,    0),
            CP(236.1,   0,      0,      1),
            CP(233.485, 24.729, 12.092, 2),
        };
        Add(p);
    }

    // 24771 - Wide Radius Slim Left Curved Turnout (R=515mm, 30deg)
    // Route 1: R=515mm 30deg curve left
    // Route 2: 64mm straight + R=515mm 30deg curve left
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24771";
        p.displayName = "24771 Slim Left Curved Turnout (R515)";
        p.type = PieceType::TurnoutY; p.lengthMm = 321.5;
        p.radiusMm = 515; p.angleDeg = -30; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,     0,     180, 0),
            CP(257.5, -69.0, 330, 1),  // route 1: R=515 30deg left
            CP(321.5, -69.0, 330, 2),  // route 2: 64mm + R=515 30deg left
        };
        Add(p);
    }

    // 24772 - Wide Radius Slim Right Curved Turnout (R=515mm, 30deg)
    // Mirror of 24771
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24772";
        p.displayName = "24772 Slim Right Curved Turnout (R515)";
        p.type = PieceType::TurnoutY; p.lengthMm = 321.5;
        p.radiusMm = 515; p.angleDeg = 30; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,     0,    180, 0),
            CP(257.5, 69.0, 30,  1),  // route 1: R=515 30deg right
            CP(321.5, 69.0, 30,  2),  // route 2: 64mm + R=515 30deg right
        };
        Add(p);
    }

    // Bumper
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24977"; p.displayName = "24977 Bumper Stop";
        p.type = PieceType::Bumper; p.lengthMm = 48.3;
        p.radiusMm = 0; p.angleDeg = 0; p.gridW = 1; p.gridH = 1;
        p.connections = { CP(0, 0, 180, 0) };
        Add(p);
    }

    // 24978 - Bumper Stop with Light
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "24978"; p.displayName = "24978 Bumper Stop with Light";
        p.type = PieceType::Bumper; p.lengthMm = 48.3;
        p.radiusMm = 0; p.angleDeg = 0; p.gridW = 1; p.gridH = 1;
        p.connections = { CP(0, 0, 180, 0) };
        Add(p);
    }

    // Uncoupler
    Add(MakeStraight(C, "24997", "24997 Uncoupler 94.2mm", 94.2));

    // ── Adapter tracks ────────────────────────────────────────────────────────
    {
        // 24922: C-Track to K-Track adapter, 180mm
        // conn[0] = C-Track end, conn[1] = K-Track end
        TrackPiece p = MakeStraight(C, "24922", "24922 Adapter C-Track to K-Track 180mm", 180.0);
        p.connSeries = { TrackSeries::MarlinC, TrackSeries::MarlinK };
        Add(p);
    }
    {
        // 24951: C-Track to M-Track adapter, 180mm.
        // conn[0] = C-Track end, conn[1] = M-Track end.
        TrackPiece p = MakeStraight(C, "24951", "24951 Adapter C-Track to M-Track 180mm", 180.0);
        p.connSeries = { TrackSeries::MarlinC, TrackSeries::MarlinM };
        Add(p);
    }

    // ── Turntable ─────────────────────────────────────────────────────────────
    // 74861: pit diameter 278mm (r=139mm), deck 263mm, spokes at 12° intervals.
    // Ships with 6 of 74871 spoke-extension pieces pre-installed (auto-spawned
    // when this turntable is placed; see CanvasPanel placement logic). The
    // 74862 is mechanically identical and was the prior catalog number for
    // this piece; we now standardize on 74861.
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "74861";
        p.displayName = "74861 Turntable (pit 278mm)";
        p.type = PieceType::Turntable;
        p.lengthMm  = 378.0;  // full footprint diameter
        p.radiusMm  = 139.0;  // pit radius
        p.angleDeg  = 12.0;   // spoke grid angle
        p.gridW = 4; p.gridH = 4;

        // 30 spoke connection points around the pit edge at 12° increments
        // Each spoke exits radially outward from pit centre
        // Index 0 = entry spoke at 0° (east), indices 1-29 clockwise
        const double r = 139.0;
        for (int i = 0; i < 30; ++i) {
            double angleDeg = i * 12.0;
            double rad = angleDeg * M_PI / 180.0;
            double cx = r * std::cos(rad);
            double cy = r * std::sin(rad);
            // heading: pointing outward from centre
            p.connections.push_back(CP(cx, cy, angleDeg, i));
        }
        Add(p);
    }

    // 74871 Spoke Extension: 57.5mm-long stub that mounts on a 74861 turntable
    // spoke. Inner end sits flush with the pit rim; outer end has a
    // connector for the user's layout to continue. The piece is sold in
    // packs of 3; the tally rolls extras up into pack count (see
    // RefreshTally / OnBuyTrack).
    //
    // Geometry: a plain 57.5mm straight with a single connector at its
    // outer end. The inner end (conn[0]) is the one that snaps to a
    // turntable spoke; the outer end is conn[1].
    {
        TrackPiece p;
        p.series = C; p.catalogNumber = "74871";
        p.displayName = "74871 Turntable Spoke Extension 57.5mm";
        p.type = PieceType::Straight;
        p.lengthMm = 57.5;
        p.radiusMm = 0.0;
        p.angleDeg = 0.0;
        p.gridW = 1; p.gridH = 1;
        p.connections = {
            CP( 0.0, 0.0, 180.0, 0),   // inner: snaps to turntable spoke
            CP(57.5, 0.0,   0.0, 1),   // outer: connects to layout track
        };
        Add(p);
    }

    // ── TRIX HO C-Track ───────────────────────────────────────────────────────
    // Source: https://www.trix.de/en/products/trix-h0/tracks
    // Geometry identical to Märklin C-Track. Numbers 62xxx mirror 24xxx.
    const auto T = TrackSeries::TrixC;

    // Straights
    Add(MakeStraight(T, "62064", "62064 Straight 64.3mm / 2-9/16\"",    64.3));
    Add(MakeStraight(T, "62071", "62071 Straight 70.8mm / 2-13/16\"",   70.8));
    Add(MakeStraight(T, "62077", "62077 Straight 77.5mm / 3-1/16\"",    77.5));
    Add(MakeStraight(T, "62094", "62094 Straight 94.2mm / 3-3/4\"",     94.2));
    Add(MakeStraight(T, "62172", "62172 Straight 171.7mm / 6-3/4\"",   171.7));
    Add(MakeStraight(T, "62188", "62188 Straight 188.3mm / 7-3/8\"",   188.3));
    Add(MakeStraight(T, "62229", "62229 Straight 229.3mm / 9\"",        229.3));
    Add(MakeStraight(T, "62236", "62236 Straight 236.1mm / 9-5/16\"",  236.1));
    Add(MakeStraight(T, "62360", "62360 Straight 360mm / 14-3/16\"",   360.0));

    // Curves R1 = 360mm
    Add(MakeCurve(T, "62107", "62107 (7.5deg) R1 - 360mm",   360.0,  7.5));
    Add(MakeCurve(T, "62115", "62115 (15deg) R1 - 360mm",    360.0, 15.0));
    Add(MakeCurve(T, "62130", "62130 (30deg) R1 - 360mm",    360.0, 30.0));

    // Curves R2 = 437.5mm
    Add(MakeCurve(T, "62206", "62206 (5.7deg) R2 - 437.5mm",  437.5,  5.7));
    Add(MakeCurve(T, "62207", "62207 (7.5deg) R2 - 437.5mm",  437.5,  7.5));
    Add(MakeCurve(T, "62215", "62215 (15deg) R2 - 437.5mm",   437.5, 15.0));
    Add(MakeCurve(T, "62224", "62224 (24.3deg) R2 - 437.5mm", 437.5, 24.3));
    Add(MakeCurve(T, "62230", "62230 (30deg) R2 - 437.5mm",   437.5, 30.0));

    // Curves R3 = 515mm
    Add(MakeCurve(T, "62315", "62315 (15deg) R3 - 515mm",     515.0, 15.0));
    Add(MakeCurve(T, "62330", "62330 (30deg) R3 - 515mm",     515.0, 30.0));

    // Curves R9 = 1114.6mm
    Add(MakeCurve(T, "62912", "62912 (12.1deg) R9 - 1114.6mm", 1114.6, 12.1));

    // Switches — identical geometry to Märklin equivalents
    {
        TrackPiece p;
        p.series = T; p.catalogNumber = "62611"; p.displayName = "62611 Left Turnout R2=437.5mm 24.3deg";
        p.type = PieceType::TurnoutLeft; p.lengthMm = 188.3;
        p.radiusMm = 437.5; p.angleDeg = -24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,      180,    0),
            CP(188.3,  0,        0,    1),
            CP(180.04, -38.76, 335.7,  2),
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = T; p.catalogNumber = "62612"; p.displayName = "62612 Right Turnout R2=437.5mm 24.3deg";
        p.type = PieceType::TurnoutRight; p.lengthMm = 188.3;
        p.radiusMm = 437.5; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,      0,     180,   0),
            CP(188.3,  0,       0,   1),
            CP(180.04, 38.76, 24.3,  2),
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = T; p.catalogNumber = "62671"; p.displayName = "62671 Left Curved Turnout";
        p.type = PieceType::TurnoutY; p.lengthMm = 257.5;
        p.radiusMm = 437.5; p.angleDeg = -24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,     0,      180,    0),   // entry
            CP(180.0, -38.76, 335.7,  1),   // route 1: curves left
            CP(257.5, -38.76, 335.7,  2),   // route 2: straight + curves left
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = T; p.catalogNumber = "62672"; p.displayName = "62672 Right Curved Turnout";
        p.type = PieceType::TurnoutY; p.lengthMm = 257.5;
        p.radiusMm = 437.5; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(0,     0,     180,   0),   // entry
            CP(180.0, 38.76, 24.3,  1),   // route 1: curves right
            CP(257.5, 38.76, 24.3,  2),   // route 2: straight + curves right
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = T; p.catalogNumber = "62630"; p.displayName = "62630 Three-Way Turnout 2x24.3deg";
        p.type = PieceType::TurnoutLeft; p.lengthMm = 188.3;
        p.radiusMm = 437.5; p.angleDeg = 24.3; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(0,      0,       180,    0),
            CP(188.3,  0,         0,    1),
            CP(180.04, 38.76,   24.3,   2),
            CP(180.04, -38.76, 335.7,   3),
        };
        Add(p);
    }

    // Double slip
    {
        TrackPiece p;
        auto ref = Find("24624");
        if (ref) {
            p = *ref;
            p.catalogNumber = "62624";
            p.displayName   = "62624 Double Slip Switch 24.3deg";
            p.series = T;
        }
        Add(p);
    }

    // Bumper and uncoupler
    {
        TrackPiece p;
        p.series = T; p.catalogNumber = "62977"; p.displayName = "62977 Track End with Bumper 80.5mm";
        p.type = PieceType::Bumper; p.lengthMm = 80.5;
        p.radiusMm = 0; p.angleDeg = 0; p.gridW = 1; p.gridH = 1;
        p.connections = { CP(0, 0, 180, 0) };
        Add(p);
    }
    Add(MakeStraight(T, "62997", "62997 Uncoupler Track 94.2mm", 94.2));

    // TRIX bridges — same geometry as Märklin C-Track, different deck track
    {
        TrackPiece p = MakeStraight(T, "62636", "62636 Arch Bridge 360mm", 360.0);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeStraight(T, "62620", "62620 Truss Bridge 188mm", 188.3);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(T, "62623", "62623 Curved Ramp R2 30deg", 437.5, 30.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(T, "62613", "62613 Curved Ramp R1 30deg", 360.0, 30.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }

    // ── Märklin Z-Track (Mini-Club) ───────────────────────────────────────────
    // Source: https://www.maerklin.de/en/products/z-scale/tracks
    // Radii: R1=145mm, R2=195mm, R3=220mm, R4=490mm (turnout branch)
    const auto Z = TrackSeries::MarlinZ;

    // Straights
    Add(MakeStraight(Z, "8500",  "8500 Straight 110mm / 4-3/8\"",         110.0));
    Add(MakeStraight(Z, "8501",  "8501 Straight 55mm / 2-3/16\" (half)",   55.0));
    Add(MakeStraight(Z, "8502",  "8502 Straight 27.5mm / 1-1/8\" (qtr)",   27.5));
    Add(MakeStraight(Z, "8503",  "8503 Straight 55mm / 2-3/16\"",           55.0));
    Add(MakeStraight(Z, "8504",  "8504 Straight 25mm / 1\"",                25.0));
    Add(MakeStraight(Z, "8505",  "8505 Straight 220mm / 8-13/16\"",        220.0));
    Add(MakeStraight(Z, "8506",  "8506 Straight 112.8mm / 4-7/16\"",       112.8));
    Add(MakeStraight(Z, "8507",  "8507 Straight 112.8mm / 4-7/16\"",       112.8));
    Add(MakeStraight(Z, "8587",  "8587 Uncoupler 55mm / 2-3/16\"",          55.0));
    Add(MakeStraight(Z, "8588",  "8588 Isolating Track 55mm / 2-3/16\"",    55.0));
    Add(MakeStraight(Z, "8590",  "8590 Feeder Track 110mm / 4-3/8\"",      110.0));
    Add(MakeStraight(Z, "8598",  "8598 Isolating Track 55mm / 2-3/16\"",    55.0));

    // Curves R1 = 145mm
    Add(MakeCurve(Z, "8510", "8510 Curve R1=145mm 45deg",  145.0, 45.0));
    Add(MakeCurve(Z, "8511", "8511 Curve R1=145mm 30deg",  145.0, 30.0));

    // Curves R2 = 195mm
    Add(MakeCurve(Z, "8520", "8520 Curve R2=195mm 45deg",          195.0, 45.0));
    Add(MakeCurve(Z, "8521", "8521 Curve R2=195mm 30deg",          195.0, 30.0));
    Add(MakeCurve(Z, "8529", "8529 Circuit Curve R2=195mm 45deg",  195.0, 45.0));

    // Curves R3 = 220mm
    Add(MakeCurve(Z, "8530", "8530 Curve R3=220mm 45deg",          220.0, 45.0));
    Add(MakeCurve(Z, "8531", "8531 Curve R3=220mm 30deg",          220.0, 30.0));
    Add(MakeCurve(Z, "8539", "8539 Circuit Curve R3=220mm 45deg",  220.0, 45.0));

    // Turnout branch curve (R4=490mm, 13deg — same as branch on 8562/8563/8565/8566)
    Add(MakeCurve(Z, "8591", "8591 Turnout Branch Curve R4=490mm 13deg", 490.0, 13.0));

    // Straight turnouts: 110mm, R=490mm, 13deg
    Add(MakeTurnout(Z, "8562", "8562 Electric Left Turnout 110mm 13deg",
                    PieceType::TurnoutLeft,  110.0, 490.0, 13.0));
    Add(MakeTurnout(Z, "8563", "8563 Electric Right Turnout 110mm 13deg",
                    PieceType::TurnoutRight, 110.0, 490.0, 13.0));
    Add(MakeTurnout(Z, "8565", "8565 Manual Left Turnout 110mm 13deg",
                    PieceType::TurnoutLeft,  110.0, 490.0, 13.0));
    Add(MakeTurnout(Z, "8566", "8566 Manual Right Turnout 110mm 13deg",
                    PieceType::TurnoutRight, 110.0, 490.0, 13.0));

    // Curved turnouts 8568/8569: 25mm stub + two parallel curve branches same direction
    // conn[0] = entry, conn[1] = inner arc exit (arc starts at point 0)
    // conn[2] = outer arc exit (arc starts at point 25mm along stub)
    {
        TrackPiece p;
        p.series = Z; p.catalogNumber = "8568";
        p.displayName = "8568 Electric Left Curved Turnout R=195mm 30deg";
        p.type = PieceType::TurnoutCurved; p.lengthMm = 25.0;
        p.radiusMm = 195.0; p.angleDeg = -30.0; p.gridW = 2; p.gridH = 1;
        const double r = 195.0, a = 30.0 * M_PI / 180.0;
        // Inner arc exits: starts at (0,0), curves left
        double ix = r * std::sin(a),        iy = -(r - r * std::cos(a));
        // Outer arc exits: starts at (25,0), same curve
        double ox = 25.0 + r * std::sin(a), oy = -(r - r * std::cos(a));
        p.connections = {
            CP(0,  0,   180,   0),
            CP(ix, iy,  330.0, 1),   // inner arc exit
            CP(ox, oy,  330.0, 2),   // outer arc exit
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = Z; p.catalogNumber = "8569";
        p.displayName = "8569 Electric Right Curved Turnout R=195mm 30deg";
        p.type = PieceType::TurnoutCurved; p.lengthMm = 25.0;
        p.radiusMm = 195.0; p.angleDeg = 30.0; p.gridW = 2; p.gridH = 1;
        const double r = 195.0, a = 30.0 * M_PI / 180.0;
        double ix = r * std::sin(a),        iy =  (r - r * std::cos(a));
        double ox = 25.0 + r * std::sin(a), oy =  (r - r * std::cos(a));
        p.connections = {
            CP(0,  0,   180,  0),
            CP(ix, iy,   30.0, 1),   // inner arc exit
            CP(ox, oy,   30.0, 2),   // outer arc exit
        };
        Add(p);
    }

    // Crossing 8559: two straight tracks crossing at 13deg, length 112.8mm
    {
        TrackPiece p;
        p.series = Z; p.catalogNumber = "8559";
        p.displayName = "8559 Crossing 112.8mm 13deg";
        p.type = PieceType::Crossing; p.lengthMm = 112.8;
        p.radiusMm = 0; p.angleDeg = 13.0; p.gridW = 2; p.gridH = 2;
        // Route 1: straight W→E
        // Route 2: straight at 13deg, centered on piece midpoint
        double aRad = 13.0 * M_PI / 180.0;
        double half = 112.8 / 2.0;
        double dx = half * std::cos(aRad);
        double dy = half * std::sin(aRad);
        double cx = 112.8 / 2.0, cy = 0.0;  // piece centre
        p.connections = {
            CP(0,       0,    180,       0),   // route1 W entry
            CP(112.8,   0,      0,       1),   // route1 E exit
            CP(cx-dx, -(cy+dy), 180+13, 2),   // route2 SW entry
            CP(cx+dx,   cy+dy,     13,  3),   // route2 NE exit
        };
        Add(p);
    }

    // Double slip 8560: 112.8mm, 13deg
    // R=501.44mm so R*sin(13°)=112.8mm — both arcs span full length like 24624
    {
        TrackPiece p;
        p.series = Z; p.catalogNumber = "8560";
        p.displayName = "8560 Double Slip Switch 13°";
        p.type = PieceType::DoubleCrossover; p.lengthMm = 112.8;
        p.radiusMm = 501.4424; p.angleDeg = 13.0; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(0,      0,        180,    0),  // top arc left    (W)
            CP(112.8,  0,          0,    1),  // bottom arc right (E)
            CP(0.0,   -12.8519,  193.0,  2),  // bottom arc left  (SW)
            CP(112.8,  12.8519,   13.0,  3),  // top arc right    (NE)
        };
        Add(p);
    }

    // Bumper (clip-on, 15mm)
    {
        TrackPiece p;
        p.series = Z; p.catalogNumber = "8991";
        p.displayName = "8991 Track Bumper 15mm";
        p.type = PieceType::Bumper; p.lengthMm = 15.0;
        p.radiusMm = 0; p.angleDeg = 0; p.gridW = 1; p.gridH = 1;
        p.connections = { CP(0, 0, 180, 0) };
        Add(p);
    }

    // Z-Track bridges
    {
        TrackPiece p = MakeStraight(Z, "8975", "8975 Arch Bridge 220mm", 220.0);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeStraight(Z, "89759", "89759 Girder Bridge 110mm", 110.0);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeStraight(Z, "8976", "8976 Straight Ramp 110mm", 110.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(Z, "8977", "8977 Curved Ramp R1=145mm 45deg", 145.0, 45.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }

    // All 47 products scraped April 2026.
    // Radii: R1=360mm, R2=424.6mm, R3=515mm, R4=573.6mm, R5=636mm
    const auto K = TrackSeries::MarlinK;

    // ── Straights ─────────────────────────────────────────────────────────────
    Add(MakeStraight(K, "2200", "2200 Straight Track Length 180 mm / 7-3/32\"",    180.0));
    Add(MakeStraight(K, "2201", "2201 Straight Track Length 90 mm / 3-9/16\"",      90.0));
    Add(MakeStraight(K, "2202", "2202 Straight Track Length 45 mm / 1-3/4\"",       45.0));
    Add(MakeStraight(K, "2203", "2203 Straight Track Length 30 mm / 1-3/16\"",      30.0));
    Add(MakeStraight(K, "2204", "2204 Straight Track Length 22.5 mm / 7/8\"",       22.5));
    Add(MakeStraight(K, "2205", "2205 Flex Track Length 900 mm / 35-7/16\"",       900.0));
    Add(MakeStraight(K, "2206", "2206 Straight Track Length 168.9 mm / 6-5/8\"",   168.9));
    Add(MakeStraight(K, "2207", "2207 Straight Track Length 156 mm / 6-1/8\"",     156.0));
    Add(MakeStraight(K, "2208", "2208 Straight Track Length 35.1 mm / 1-3/8\"",     35.1));
    Add(MakeStraight(K, "2209", "2209 Straight Track Length 217.9 mm / 8-9/16\"",  217.9));
    Add(MakeStraight(K, "2293", "2293 Straight Track Length 41.3 mm / 1-5/8\"",     41.3));
    Add(MakeStraight(K, "2290", "2290 Straight Feeder Track Length 180 mm / 7-3/32\"", 180.0));
    Add(MakeStraight(K, "2291", "2291 Adapter Track for M Track Length 180 mm / 7-3/32\"", 180.0));
    Add(MakeStraight(K, "2292", "2292 Straight Feeder Track Length 180 mm / 7-3/32\"", 180.0));
    Add(MakeStraight(K, "2295", "2295 Contact Track Set Length 2 x 90 mm / 3-9/16\"", 90.0));
    Add(MakeStraight(K, "2297", "2297 Straight Uncoupler Track Length 90 mm / 3-9/16\"", 90.0));
    Add(MakeStraight(K, "2299", "2299 Straight Circuit Track Length 90 mm / 3-9/16\"", 90.0));

    // ── Curves R1 = 360 mm ────────────────────────────────────────────────────
    Add(MakeCurve(K, "2210", "2210 Curved Track R0=295.4mm 45deg",  295.4, 45.0));
    Add(MakeCurve(K, "2229", "2229 Curved Circuit Track Radius 360 mm",   360.0, 45.0));

    // ── Curves R2 = 424.6 mm ──────────────────────────────────────────────────
    Add(MakeCurve(K, "2221", "2221 Curved Track R1=360mm 30deg", 360.0, 30.0));
    Add(MakeCurve(K, "2223", "2223 Curved Track Radius 15° (R2=424.6 mm / 16-3/4\")", 424.6, 15.0));
    Add(MakeCurve(K, "2224", "2224 Curved Track 7.5deg (R1=360 mm)", 360.0, 7.5));
    Add(MakeCurve(K, "2239", "2239 Curved Circuit Track Radius 424.6 mm / 16-3/4\"",  424.6, 30.0));

    // ── Curves R3 = 515 mm ────────────────────────────────────────────────────
    Add(MakeCurve(K, "2231", "2231 Curved Track R2=424.6mm 30deg", 424.6, 30.0));
    Add(MakeCurve(K, "2232", "2232 Curved Track R2=424.6mm 22.5deg", 424.6, 22.5));
    Add(MakeCurve(K, "2233", "2233 Curved Track 15deg (R2=424.6 mm)", 424.6, 15.0));
    Add(MakeCurve(K, "2234", "2234 Curved Track Radius 7° (R3=515 mm)",  515.0,  7.5));
    Add(MakeCurve(K, "2235", "2235 Curved Track 3.45deg (R2=424.6 mm)", 424.6, 3.45));

    // ── Curves R4 = 573.6 mm ──────────────────────────────────────────────────
    Add(MakeCurve(K, "2241", "2241 Curved Track 30deg (R3=577.9 mm)", 577.9, 30.0));
    Add(MakeCurve(K, "2251", "2251 Curved Track 15deg (R4=618.5 mm)", 618.5, 15.0));

    // ── Curves R5 = 636 mm ────────────────────────────────────────────────────
    // 2274: complementary curve for the 22715/22716 turnouts. It mirrors the
    // turnout -- same radius (R5 = 902.4mm) and the same angle (14 deg 26'),
    // curving the opposite way, so it brings the diverged branch back level.
    //
    // The published list gives the angle but not the radius. 902.4mm is the
    // turnout's own radius and is what the K-Track turntable plan requires:
    // the old 636mm left the branch 56.6mm short of the main, so it could not
    // reach the turntable stub.
    Add(MakeCurve(K, "2274", "2274 Curved Track 14deg 26' (R=902.4 mm)", 902.4, 14.4333));

    // ── Crossings ─────────────────────────────────────────────────────────────
    // 2257: Crossing 22°30' 225mm
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2257";
        p.displayName = "2257 Crossing Length 225 mm / 8-7/8\"";
        p.type = PieceType::Crossing; p.lengthMm = 225.0;
        p.radiusMm = 0; p.angleDeg = 22.5; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(  0.00,   0.00, 180,    0),
            CP(225.00,   0.00,   0,    1),
            CP(  8.56, -43.05, 202.5,  2),
            CP(216.44,  43.05,  22.5,  3),
        };
        Add(p);
    }
    // 2258: Crossing 45° 90mm
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2258";
        p.displayName = "2258 Crossing Length 90 mm / 3-9/16\"";
        p.type = PieceType::Crossing; p.lengthMm = 90.0;
        p.radiusMm = 0; p.angleDeg = 45.0; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP( 0.00,   0.00, 180,  0),
            CP(90.00,   0.00,   0,  1),
            CP(13.18, -31.82, 225,  2),
            CP(76.82,  31.82,  45,  3),
        };
        Add(p);
    }
    // 2259: Crossing 22°30' 168.9mm
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2259";
        p.displayName = "2259 Crossing Length 168.9 mm / 6-5/8\"";
        p.type = PieceType::Crossing; p.lengthMm = 168.9;
        p.radiusMm = 0; p.angleDeg = 22.5; p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(  0.00,   0.00, 180,    0),
            CP(168.90,   0.00,   0,    1),
            CP(  6.43, -32.32, 202.5,  2),
            CP(162.47,  32.32,  22.5,  3),
        };
        Add(p);
    }

    // ── Turnouts ──────────────────────────────────────────────────────────────
    Add(MakeTurnout(K, "2262", "2262 Left Turnout Length 168.9 mm / 6-5/8\"",
                    PieceType::TurnoutLeft,  168.9, 424.6, 22.5));
    Add(MakeTurnout(K, "2263", "2263 Right Turnout Length 168.9 mm / 6-5/8\"",
                    PieceType::TurnoutRight, 168.9, 424.6, 22.5));
    Add(MakeTurnout(K, "2265", "2265 Left Turnout Length 168.9 mm / 6-5/8\" (manual)",
                    PieceType::TurnoutLeft,  168.9, 424.6, 22.5));
    Add(MakeTurnout(K, "2266", "2266 Right Turnout Length 168.9 mm / 6-5/8\" (manual)",
                    PieceType::TurnoutRight, 168.9, 424.6, 22.5));
    // 2269 - Right Curved Turnout (this block holds the geometry that
    // renders as right-handed; the catalog number for the right-handed
    // piece is 2269 per Marklin numbering)
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2269";
        p.displayName = "2269 Right Curved Turnout Outer Curve 30° Spacing 64.6 mm / 2-1/2\"";
        p.type = PieceType::TurnoutCurved;
        p.lengthMm = 64.6;   // stub length
        p.radiusMm = 360.0;
        p.angleDeg = 30.0;
        p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,   0.0,  180.0, 0),  // entry
            CP(180.0,  48.23,  30.0, 1),  // inner arc exit
            CP(244.6,  48.23,  30.0, 2),  // outer: stub + same arc
        };
        Add(p);
    }
    // 2268 - Left Curved Turnout (this block holds the geometry that
    // renders as left-handed; the catalog number for the left-handed
    // piece is 2268 per Marklin numbering)
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2268";
        p.displayName = "2268 Left Curved Turnout Outer Curve 30° Spacing 64.6 mm / 2-1/2\"";
        p.type = PieceType::TurnoutCurved;
        p.lengthMm = 64.6;
        p.radiusMm = 360.0;
        p.angleDeg = -30.0;
        p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,   0.0,  180.0, 0),  // entry
            CP(180.0, -48.23, 330.0, 1),  // inner arc exit
            CP(244.6, -48.23, 330.0, 2),  // outer: stub + same arc
        };
        Add(p);
    }
    // 2270 - Symmetrical Three-Way Turnout
    // Length 168.9mm, R=424.6mm, branches 2x22.5deg (same as 2232/2262/2263)
    // ex=R*sin(22.5°)=162.49mm, ey=R*(1-cos(22.5°))=32.32mm
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2270";
        p.displayName = "2270 Symmetrical Three-Way Turnout Length 168.9 mm / Radius 424.6 mm / 16-3/4\"";
        p.type = PieceType::TurnoutLeft; p.lengthMm = 168.9;
        p.radiusMm = 424.6; p.angleDeg = 22.5; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,    0.0,   180.0,  0),  // entry
            CP(168.9,    0.0,     0.0,  1),  // straight exit
            CP(162.49,  32.32,   22.5,  2),  // left branch exit
            CP(162.49, -32.32,  337.5,  3),  // right branch exit
        };
        Add(p);
    }
    // Branch angle is 14 deg 26' (= 14.4333). Deriving it from the stated
    // geometry confirms this: asin(225 / 902.4) = 14.438 deg. The old 14.3 was
    // a rounding, and it left the complementary 2274 curve unable to bring the
    // branch back to parallel.
    Add(MakeTurnout(K, "22715", "22715 Left Turnout Length 225 mm / 8-7/8\" / Radius 902.4 mm / 35-1/2\"",
                    PieceType::TurnoutLeft,  225.0, 902.4, 14.4333));
    Add(MakeTurnout(K, "22716", "22716 Right Turnout Length 225 mm / 8-7/8\" / Radius 902.4 mm / 35-1/2\"",
                    PieceType::TurnoutRight, 225.0, 902.4, 14.4333));

    // ── Double Slip Switches ──────────────────────────────────────────────────
    // 2260: Double Slip Switch 22°30', curve same as 2232 (R=424.6mm), length 168.9mm
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2260";
        p.displayName = "2260 Double Slip Switch Length 168.9 mm / 6-5/8\"";
        p.type = PieceType::DoubleCrossover; p.lengthMm = 168.9;
        p.radiusMm = 424.6; p.angleDeg = 22.5; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.00,   0.00,  180.0, 0),   // top arc left
            CP(168.90,   0.00,    0.0, 1),   // bottom arc right
            CP(  6.41, -32.32,  202.5, 2),   // bottom arc left
            CP(162.49,  32.32,   22.5, 3),   // top arc right
        };
        Add(p);
    }
    // 2275: Double Slip Switch 225mm / R902.4mm / 14.3°
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "2275";
        p.displayName = "2275 Double Slip Switch Length 225 mm / 8-7/8\" / Radius 902.4 mm / 35-1/2\"";
        p.type = PieceType::DoubleCrossover; p.lengthMm = 225.0;
        p.radiusMm = 902.4; p.angleDeg = 14.3; p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.00,   0.00, 180,    0),
            CP(225.00,   0.00,   0,    1),
            CP(  3.49, -27.79, 194.3,  2),
            CP(221.51,  27.79,  14.3,  3),
        };
        Add(p);
    }

    // ── Bumpers ───────────────────────────────────────────────────────────────
    Add(MakeBumper(K, "7389", "7389 Track Bumper", 60.0));
    Add(MakeBumper(K, "7391", "7391 Track Bumper", 40.0));

    // K-Track bridges
    {
        TrackPiece p = MakeStraight(K, "7263", "7263 Arch Bridge 360mm", 360.0);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeStraight(K, "7262", "7262 Truss Bridge 180mm", 180.0);
        p.isBridge = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(K, "7569", "7569 Curved Ramp R2 30deg", 424.6, 30.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }
    {
        TrackPiece p = MakeCurve(K, "7267", "7267 Curved Ramp R1 45deg", 360.0, 45.0);
        p.isBridge = true; p.isRamp = true;
        Add(p);
    }

    // ── K-Track Turntable ─────────────────────────────────────────────────────
    // 7286: Standard DB 27m design. External diameter 386mm (pit r=193mm),
    // deck 310mm. Spokes at 7.5° intervals — expandable to 48 spoke tracks
    // via the 7287 extension kit. Ships with 6 K-Track spoke tracks.
    // Discontinued (not sold; absent from ShopifyVariants).
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "7286";
        p.displayName = "7286 Turntable (pit 386mm)";
        p.type = PieceType::Turntable;
        p.lengthMm  = 386.0;  // full footprint diameter
        p.radiusMm  = 193.0;  // pit radius
        p.angleDeg  = 7.5;    // spoke grid angle
        p.gridW = 5; p.gridH = 5;

        // 48 spoke connection points around the pit edge at 7.5° increments.
        // Each spoke exits radially outward from pit centre.
        const double r = 193.0;
        for (int i = 0; i < 48; ++i) {
            double angleDeg = i * 7.5;
            double rad = angleDeg * M_PI / 180.0;
            p.connections.push_back(CP(r * std::cos(rad),
                                       r * std::sin(rad),
                                       angleDeg, i));
        }
        Add(p);
    }

    // 7287 Turntable Extension Kit: 3 K-Track spoke tracks + 3 dummy tracks.
    // Mountable at any point on the 7286 perimeter. Same 57.5mm spoke geometry
    // as the C-Track 74871. Discontinued (not sold).
    {
        TrackPiece p;
        p.series = K; p.catalogNumber = "7287";
        p.displayName = "7287 Turntable Spoke Extension 57.5mm";
        p.type = PieceType::Straight;
        p.lengthMm = 57.5;
        p.radiusMm = 0.0;
        p.angleDeg = 0.0;
        p.gridW = 1; p.gridH = 1;
        p.connections = {
            CP( 0.0, 0.0, 180.0, 0),   // inner: snaps to turntable spoke
            CP(57.5, 0.0,   0.0, 1),   // outer: connects to layout track
        };
        Add(p);
    }

    // ── Märklin Gauge 1 (Scale I, 1:32, 45mm gauge) ──────────────────────────
    // Source: https://www.maerklin.de/en/products/gauge-1/tracks
    // Radii: R1=1020, R2=1176, R3=1394, R4=1550, R5=1715, R6=2321, R7=2461mm
    // Angles: 22.5° (standard), 15° (shallow), 10° (large radius)
    const auto G1 = TrackSeries::Gauge1;

    // Straights
    Add(MakeStraight(G1, "59051", "59051 Straight 59.5mm / 2-5/16\"",    59.5));
    Add(MakeStraight(G1, "59052", "59052 Straight 79mm / 3-1/8\"",        79.0));
    Add(MakeStraight(G1, "59053", "59053 Straight 100mm / 3-15/16\"",    100.0));
    Add(MakeStraight(G1, "59054", "59054 Straight 116mm / 4-9/16\"",     116.0));
    Add(MakeStraight(G1, "59055", "59055 Straight 150mm / 5-7/8\"",      150.0));
    Add(MakeStraight(G1, "59056", "59056 Straight 152.2mm (diagonal)",   152.2));
    Add(MakeStraight(G1, "59057", "59057 Straight 200mm / 7-7/8\"",      200.0));
    Add(MakeStraight(G1, "59058", "59058 Straight 300mm / 11-13/16\"",   300.0));
    Add(MakeStraight(G1, "59059", "59059 Straight 600mm / 23-5/8\"",     600.0));
    Add(MakeStraight(G1, "59061", "59061 Straight 900mm / 35-7/16\"",    900.0));

    // Curves R1 = 1020mm, 22.5°
    Add(MakeCurve(G1, "59035", "59035 Curve R1=1020mm 22.5°",  1020.0, 22.5));

    // Curves R2 = 1176mm, 22.5°
    Add(MakeCurve(G1, "59072", "59072 Curve R2=1176mm 22.5°",  1176.0, 22.5));

    // Curves R3 = 1394mm (22.5° and 15°)
    Add(MakeCurve(G1, "59073", "59073 Curve R3=1394mm 22.5°",  1394.0, 22.5));
    Add(MakeCurve(G1, "59075", "59075 Curve R3=1394mm 15°",    1394.0, 15.0));

    // Curves R4 = 1550mm (15° and 22.5°)
    Add(MakeCurve(G1, "59074", "59074 Curve R4=1550mm 15°",    1550.0, 15.0));
    Add(MakeCurve(G1, "59076", "59076 Curve R4=1550mm 22.5°",  1550.0, 22.5));

    // Curves R5 = 1715mm, 22.5°
    Add(MakeCurve(G1, "59077", "59077 Curve R5=1715mm 22.5°",  1715.0, 22.5));

    // Curves R6 = 2321mm, 10°
    Add(MakeCurve(G1, "59078", "59078 Curve R6=2321mm 10°",    2321.0, 10.0));

    // Curves R7 = 2461mm, 10°
    Add(MakeCurve(G1, "59071", "59071 Curve R7=2461mm 10°",    2461.0, 10.0));

    // Turnout end piece: R=1394mm, 15° (matches branch of standard turnouts)
    Add(MakeCurve(G1, "59092", "59092 Turnout End Piece R1394mm 15°", 1394.0, 15.0));

    // Standard turnouts: R=1394mm, branch 15°, main track = R*sin(15°) = 360.8mm
    Add(MakeTurnout(G1, "59087", "59087 Right Turnout R1394mm 15°",
                    PieceType::TurnoutRight, 360.8, 1394.0, 15.0));
    Add(MakeTurnout(G1, "59088", "59088 Left Turnout R1394mm 15°",
                    PieceType::TurnoutLeft,  360.8, 1394.0, 15.0));

    // Turnout sets: slim/high-speed, R=2321mm, 10°
    // Main track length = R*sin(10°) = 2321*0.1736 = 402.9mm
    Add(MakeTurnout(G1, "59083", "59083 Right Slim Turnout Set R2321mm 10°",
                    PieceType::TurnoutRight, 402.9, 2321.0, 10.0));
    Add(MakeTurnout(G1, "59084", "59084 Left Slim Turnout Set R2321mm 10°",
                    PieceType::TurnoutLeft,  402.9, 2321.0, 10.0));

    // Curved turnouts: inner R=1394mm, outer R=1550mm, 22.5°
    // Both routes curve in the same direction (TurnoutCurved type)
    // conn[0]=entry, conn[1]=outer arc exit (R1550), conn[2]=inner arc exit (R1394)
    {
        TrackPiece p;
        p.series = G1; p.catalogNumber = "59085";
        p.displayName = "59085 Left Curved Turnout R1394/1550mm 22.5°";
        p.type = PieceType::TurnoutCurved;
        p.lengthMm = 59.5; p.radiusMm = 1550.0; p.angleDeg = -22.5;
        p.gridW = 2; p.gridH = 1;
        // outer (R1550, 22.5°): ex=593.2, ey=118.0  inner (R1394, 22.5°): ex=533.5, ey=106.1
        p.connections = {
            CP(  0.0,   0.0, 180.0, 0),  // entry
            CP(593.2, 118.0,  22.5, 1),  // outer arc exit (R1550)
            CP(533.5, 106.1,  22.5, 2),  // inner arc exit (R1394)
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = G1; p.catalogNumber = "59086";
        p.displayName = "59086 Right Curved Turnout R1394/1550mm 22.5°";
        p.type = PieceType::TurnoutCurved;
        p.lengthMm = 59.5; p.radiusMm = 1550.0; p.angleDeg = 22.5;
        p.gridW = 2; p.gridH = 1;
        p.connections = {
            CP(  0.0,    0.0,  180.0, 0),  // entry
            CP(593.2, -118.0,  337.5, 1),  // outer arc exit (R1550)
            CP(533.5, -106.1,  337.5, 2),  // inner arc exit (R1394)
        };
        Add(p);
    }

    // Three-way turnout: R=1394mm, 15° branches left and right
    {
        TrackPiece p;
        p.series = G1; p.catalogNumber = "59091";
        p.displayName = "59091 Three-Way Turnout R1394mm 15°";
        p.type = PieceType::TurnoutLeft;
        p.lengthMm = 360.8; p.radiusMm = 1394.0; p.angleDeg = 15.0;
        p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,   0.0,  180.0, 0),  // entry
            CP(360.8,   0.0,    0.0, 1),  // straight exit
            CP(360.8,  47.5,   15.0, 2),  // left branch exit
            CP(360.8, -47.5,  345.0, 3),  // right branch exit
        };
        Add(p);
    }

    // Double slip switch: R=1394mm, 22.5°
    // ex=R*sin(22.5°)=533.5, ey=R*(1-cos(22.5°))=106.1
    {
        TrackPiece p;
        p.series = G1; p.catalogNumber = "59093";
        p.displayName = "59093 Double Slip Switch R1394mm 22.5°";
        p.type = PieceType::DoubleCrossover;
        p.lengthMm = 533.5; p.radiusMm = 1394.0; p.angleDeg = 22.5;
        p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,   0.0,  180.0, 0),  // route1 entry  (W)
            CP(533.5,   0.0,    0.0, 1),  // route1 exit   (E)
            CP(  0.0, 106.1,  202.5, 2),  // route2 entry  (SW)
            CP(533.5, 106.1,   22.5, 3),  // route2 exit   (NE)
        };
        Add(p);
    }

    // Double crossover: same topology as double slip
    {
        TrackPiece p;
        p.series = G1; p.catalogNumber = "59094";
        p.displayName = "59094 Double Crossover R1394mm 22.5°";
        p.type = PieceType::DoubleCrossover;
        p.lengthMm = 533.5; p.radiusMm = 1394.0; p.angleDeg = 22.5;
        p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,   0.0,  180.0, 0),
            CP(533.5,   0.0,    0.0, 1),
            CP(  0.0, 106.1,  202.5, 2),
            CP(533.5, 106.1,   22.5, 3),
        };
        Add(p);
    }

    // Bumpers
    Add(MakeBumper(G1, "5602",  "5602 Track Bumper",           100.0));
    Add(MakeBumper(G1, "59099", "59099 Old-Timer Track Bumper", 100.0));

    // ── Märklin M-Track (HO 1:87, vintage tinplate, discontinued) ─────────────
    // Three curve radii: R0=286mm, R1=360mm, R2=437.4mm. All full sections=30°.
    // Straight reference: 5106=180mm. Turnout branch angles: R1=30°, R2=24.17°.
    const auto M = TrackSeries::MarlinM;

    // Straights
    Add(MakeStraight(M, "5106", "5106 Straight 180mm",    180.0));
    Add(MakeStraight(M, "5107", "5107 Straight 90mm",      90.0));
    Add(MakeStraight(M, "5129", "5129 Straight 70mm",      70.0));
    Add(MakeStraight(M, "5108", "5108 Straight 45mm",      45.0));
    Add(MakeStraight(M, "5109", "5109 Straight 33.8mm",    33.8));
    Add(MakeStraight(M, "5110", "5110 Straight 22.5mm",    22.5));
    Add(MakeStraight(M, "5210", "5210 Straight 16mm",      16.0));
    Add(MakeStraight(M, "5208", "5208 Straight 8mm",        8.0));
    Add(MakeStraight(M, "5105", "5105 Contact Track 180mm",180.0));
    Add(MakeStraight(M, "5145", "5145 Contact Track 90mm",  90.0));

    // Uncoupler track (straight, electric)
    {
        TrackPiece p = MakeStraight(M, "5112", "5112 Uncoupler Track 90mm", 90.0);
        p.type = PieceType::Uncoupler;
        Add(p);
    }

    // Curves R0 (286mm, 30°)
    // exit: x=143.0, y=38.3
    Add(MakeCurve(M, "5120", "5120 Curve R0=286mm 30°",   286.0, 30.0));

    // Curves R1 (360mm)
    // full (30°): exit x=180.0, y=48.2
    // half (15°): exit x=93.2,  y=12.3
    // quarter (7.5°): exit x=47.0, y=3.1
    Add(MakeCurve(M, "5100", "5100 Curve R1=360mm 30°",   360.0, 30.0));
    Add(MakeCurve(M, "5101", "5101 Curve R1=360mm 15°",   360.0, 15.0));
    Add(MakeCurve(M, "5102", "5102 Curve R1=360mm 7.5°",  360.0,  7.5));

    // Curves R2 (437.4mm)
    // full (30°): exit x=218.7, y=58.6
    // half (15°): exit x=113.2, y=14.9
    // comp 5206 (24.17°): exit x=179.1, y=38.3
    // comp ext 5205 (5.83°): exit x=44.4, y=2.3
    Add(MakeCurve(M, "5200", "5200 Curve R2=437.4mm 30°",    437.4, 30.0));
    Add(MakeCurve(M, "5201", "5201 Curve R2=437.4mm 15°",    437.4, 15.0));
    Add(MakeCurve(M, "5206", "5206 Curve R2 Compensation 24.17°", 437.4, 24.17));
    Add(MakeCurve(M, "5205", "5205 Curve R2 Comp Extension 5.83°", 437.4, 5.83));
    // Lean compensation curve: 8mm arc at 1.1° (R≈417mm). Corrects the slight
    // track lean caused by the 5206/5207 angle mismatch (24.17° vs 23.27°) —
    // a known M-Track geometry quirk. A copy of the 5208 (8mm) with 1.1° curve.
    Add(MakeCurve(M, "5208C", "5208C Lean Compensation Curve 1.1°", 416.7, 1.1));

    // R1 Turnouts (branch R1=360mm, 30°, main=180mm)
    Add(MakeTurnout(M, "5117", "5117 Left Turnout R1 30° Electric",
                    PieceType::TurnoutLeft,  180.0, 360.0, 30.0));
    Add(MakeTurnout(M, "5118", "5118 Left Turnout R1 30°",
                    PieceType::TurnoutLeft,  180.0, 360.0, 30.0));
    Add(MakeTurnout(M, "5119", "5119 Right Turnout R1 30°",
                    PieceType::TurnoutRight, 180.0, 360.0, 30.0));
    Add(MakeTurnout(M, "5121", "5121 Left Turnout R1 30° Manual",
                    PieceType::TurnoutLeft,  180.0, 360.0, 30.0));
    Add(MakeTurnout(M, "5122", "5122 Left Turnout R1 30° Manual",
                    PieceType::TurnoutLeft,  180.0, 360.0, 30.0));
    Add(MakeTurnout(M, "5123", "5123 Right Turnout R1 30° Manual",
                    PieceType::TurnoutRight, 180.0, 360.0, 30.0));
    Add(MakeTurnout(M, "3600", "3600 Turnout R1 30°",
                    PieceType::TurnoutLeft,  180.0, 360.0, 30.0));

    // R1 3/4 Turnout 5137: main=180mm, branch exits at 22.5° (3/4 of 30°)
    Add(MakeTurnout(M, "5137", "5137 Turnout R1 3/4 Electric",
                    PieceType::TurnoutLeft,  180.0, 360.0, 22.5));

    // R2 Turnouts (branch R2=437.4mm, 24.17°, main=179.1mm)
    Add(MakeTurnout(M, "5202", "5202 Turnout R2 24.17° Electric",
                    PieceType::TurnoutLeft,  179.1, 437.4, 24.17));
    Add(MakeTurnout(M, "5203", "5203 Left Turnout R2 24.17° Electric",
                    PieceType::TurnoutLeft,  179.1, 437.4, 24.17));
    Add(MakeTurnout(M, "5204", "5204 Right Turnout R2 24.17° Electric",
                    PieceType::TurnoutRight, 179.1, 437.4, 24.17));
    Add(MakeTurnout(M, "5221", "5221 Turnout R2 24.17° Manual",
                    PieceType::TurnoutLeft,  179.1, 437.4, 24.17));

    // Curved Turnouts 5142/5143: both branches R=360mm, 30°. Same structure as 8568/8569.
    // conn[0]=entry, conn[1]=inner arc exit (from entry),
    // conn[2]=outer arc exit (from 77.4mm stub end). Both arcs R=360mm.
    // The 77.4mm stub creates the parallel track spacing.
    // 5142 curves right (angleDeg +30), 5143 curves left (angleDeg -30).
    {
        TrackPiece p;
        p.series = M; p.catalogNumber = "5142";
        p.displayName = "5142 Right Curved Turnout R360 30deg";
        p.type = PieceType::TurnoutCurved; p.lengthMm = 77.4;
        p.radiusMm = 360.0; p.angleDeg = 30.0; p.gridW = 2; p.gridH = 1;
        const double r = 360.0, a = 30.0 * M_PI / 180.0;
        double ix = r * std::sin(a),        iy =  (r - r * std::cos(a));  // inner from entry
        double ox = 77.4 + r * std::sin(a), oy =  (r - r * std::cos(a));  // outer from stub
        p.connections = {
            CP(0,  0,   180,  0),
            CP(ix, iy,   30.0, 1),   // inner arc exit
            CP(ox, oy,   30.0, 2),   // outer arc exit
        };
        Add(p);
    }
    {
        TrackPiece p;
        p.series = M; p.catalogNumber = "5143";
        p.displayName = "5143 Left Curved Turnout R360 30deg";
        p.type = PieceType::TurnoutCurved; p.lengthMm = 77.4;
        p.radiusMm = 360.0; p.angleDeg = -30.0; p.gridW = 2; p.gridH = 1;
        const double r = 360.0, a = 30.0 * M_PI / 180.0;
        double ix = r * std::sin(a),        iy = -(r - r * std::cos(a));  // inner from entry
        double ox = 77.4 + r * std::sin(a), oy = -(r - r * std::cos(a));  // outer from stub
        p.connections = {
            CP(0,  0,   180,   0),
            CP(ix, iy,  330.0, 1),   // inner arc exit
            CP(ox, oy,  330.0, 2),   // outer arc exit
        };
        Add(p);
    }

    // Standard Crossing 5114 (30°, 193mm)
    Add(MakeCrossing(M, "5114", "5114 Crossing 30° 193mm", 193.0, 360.0, 30.0));

    // 5211: 48.5° crossing, ~98mm
    Add(MakeCrossing(M, "5211", "5211 Crossing 48.5° 98mm", 98.0, 360.0, 48.5));

    // Special Crossings 5207/5215 (23°16' = 23.267°, 180mm)
    Add(MakeCrossing(M, "5215", "5215 Crossing 23.27° 180mm", 180.0, 437.4, 23.267));

    // Double Slip 5207 (kruiswissel): 23°16' = 23.267°, 180mm
    Add(MakeDoubleSlip(M, "5207", "5207 Double Slip 23.27° Electric",
                       180.0, 437.4, 23.267));

    // Double Slip 5128: 2x R1 360mm 30° curves + 2x 193mm straights crossing at 30°
    Add(MakeDoubleSlip(M, "5128", "5128 Double Slip R360 30° Electric",
                       193.0, 360.0, 30.0));

    // Three-way Turnout 5214 (2x R2 24.17° + 180mm straight)
    {
        TrackPiece p;
        p.series = M; p.catalogNumber = "5214";
        p.displayName = "5214 Three-Way Turnout R2 24.17°";
        p.type = PieceType::TurnoutY;
        p.lengthMm = 179.1; p.radiusMm = 437.4; p.angleDeg = 24.17;
        p.gridW = 2; p.gridH = 2;
        p.connections = {
            CP(  0.0,   0.0,  180.0, 0),   // entry
            CP(179.1,   0.0,    0.0, 1),   // straight exit
            CP(179.1,  38.3,   24.17,2),   // left branch (R2 24.17°)
            CP(179.1, -38.3,  335.83,3),   // right branch (R2 24.17°)
        };
        Add(p);
    }

    // Bumpers
    Add(MakeBumper(M, "5190", "5190 Track Bumper",        90.0));
    Add(MakeBumper(M, "5191", "5191 Track Bumper Remote",  90.0));

    m_loaded = true;
}

// ── Lookup ────────────────────────────────────────────────────────────────────
bool CatalogLoader::Load(const char*) {
    LoadBuiltIn();
    return true;
}

const TrackPiece* CatalogLoader::Find(const std::string& cat) const {
    auto it = m_index.find(cat);
    return (it != m_index.end()) ? &m_pieces[it->second] : nullptr;
}

const TrackPiece* CatalogLoader::GetByIndex(int i) const {
    return (i >= 0 && i < (int)m_pieces.size()) ? &m_pieces[i] : nullptr;
}

int CatalogLoader::Count() const { return (int)m_pieces.size(); }

std::vector<const TrackPiece*> CatalogLoader::GetSeries(TrackSeries series) const {
    std::vector<const TrackPiece*> result;
    for (const auto& p : m_pieces)
        if (p.series == series) result.push_back(&p);
    return result;
}

std::vector<const TrackPiece*> CatalogLoader::GetByType(PieceType type) const {
    std::vector<const TrackPiece*> result;
    for (const auto& p : m_pieces)
        if (p.type == type) result.push_back(&p);
    return result;
}

} // namespace MC
