{"product_id":"bottom-feeders-super-weight-hangers-for-axial®-scx24™","title":"Bottom Feeders - Super 1.8 Weight Hangers for Axial® SCX24™","description":"\u003cp\u003eThe Bottom Feeders super weight hangers are a class 3 must and provide incredible weighting and tuning control corner to corner. They are intended to be used with our RidgeBeast 1.8\" wheels.\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eAdjustable canting forward or back\u003c\/li\u003e\n\u003cli\u003eRemovable weight slugs. Each hanger also has 6 slots so you can mix and match weights to get your optimal weight balance and positioning. \u003c\/li\u003e\n\u003cli\u003eBearing supported two-piece nylon construction locks into the axle's knuckle\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eAxle Compatibility: The initial version is designed to fit our X15 axles front or rear. MEUS ISO version to come in the near future.\u003c\/p\u003e\n\u003cp\u003eWheel Compatibility: They are designed to only work with super 1.8\" wheels such as our RidgeBeast wheels. The hangers rely upon either our +6 or +7.5 MF6 Hubs to properly support the hangers via the bearing. We welcome wheel designers to design around our adjustable hubs to provide further options. Please reach out via email if you're interested.\u003c\/p\u003e\n\u003cp\u003eWeights:\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eBare Hanger w\/ Bearings and Nylon Hub: 11.5g\u003c\/li\u003e\n\u003cli\u003eAluminum: 0.5g Each (6- 3.1g)\u003c\/li\u003e\n\u003cli\u003eBrass: 1.9 g each (6- 11.2g)\u003c\/li\u003e\n\u003cli\u003eStainless Steel: 1.8g Each (6- 10.5g)\u003c\/li\u003e\n\u003cli\u003eTungsten: 3.1g Each (6- 18.7g)\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eUse our calculator below to help guide you to the best setup for your needs.\u003c\/p\u003e\n\u003cp\u003eIncludes:\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003e2x Nylon Hangers\u003c\/li\u003e\n\u003cli\u003e2x Nylon Axle Attachment\u003c\/li\u003e\n\u003cli\u003e4x +7.5 Nylon MF6 Hubs\n\u003cul\u003e\n\u003cli\u003e2x include lip for rear axle applications\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003c\/li\u003e\n\u003cli\u003e12x 8mm x 6mm Keel Weights in material of choice\u003c\/li\u003e\n\u003cli\u003e4x 20mm x 15mm x 4mm Bearings\u003c\/li\u003e\n\u003cli\u003eHardware\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eImportant Install Info\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eIncluded Nylon +7.5mm MF6 Hubs (No lip) Steering axle only\u003c\/li\u003e\n\u003cli\u003eIncluded Nylon +7.5mm MF6 Hubs (With Lip) Rear straight axle only\u003c\/li\u003e\n\u003cli\u003eWhen using with our metal MF6 hubs on the rear, you will used the included M2 countersunk screws to prevent the hangers from sliding too far inward over the axle.\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e \u003c\/p\u003e\n\u003cmeta charset=\"UTF-8\"\u003e\n\u003cstyle\u003e\n  .wh-page *, .wh-page *::before, .wh-page *::after { box-sizing: border-box; margin: 0; padding: 0; }\n  \/* Reset buttons\/inputs inside wh-page to prevent Dawn theme overrides *\/\n  .wh-page button,\n  .wh-page button:focus,\n  .wh-page button:active {\n    appearance: none;\n    -webkit-appearance: none;\n    font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif;\n    outline: none;\n    box-shadow: none;\n    letter-spacing: normal;\n    text-transform: none;\n  }\n  .wh-page input,\n  .wh-page select {\n    font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif;\n  }\n  .wh-page {\n    max-width: 900px;\n    margin: 0 auto;\n    padding: 48px 24px 80px;\n    font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif;\n    color: #1A1A2E;\n  }\n  \/* Hero *\/\n  .wh-hero {\n    margin-bottom: 24px;\n    border-radius: 10px;\n    overflow: hidden;\n    background: #1B2640;\n    padding: 24px 28px 22px;\n  }\n  .wh-hero__title {\n    font-size: 20px;\n    font-weight: 700;\n    color: #FFFFFF;\n    margin-bottom: 8px;\n  }\n  .wh-hero__desc {\n    font-size: 14px;\n    color: #A8B4CC;\n    line-height: 1.5;\n    margin-bottom: 12px;\n  }\n  .wh-hero__badges {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    align-items: center;\n  }\n  .wh-hero__badge {\n    font-size: 11px;\n    font-weight: 600;\n    color: #FFFFFF;\n    background: rgba(255,255,255,0.08);\n    border: 1px solid rgba(255,255,255,0.12);\n    border-radius: 4px;\n    padding: 4px 10px;\n    white-space: nowrap;\n  }\n  .wh-hero__badge--orange {\n    color: #E8761A;\n    background: rgba(232,118,26,0.12);\n    border-color: rgba(232,118,26,0.3);\n  }\n  \/* Path selector *\/\n  .wh-paths {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 14px;\n    margin-bottom: 24px;\n  }\n  .wh-path {\n    border: 1.5px solid #DDE1EA !important;\n    border-radius: 10px !important;\n    padding: 24px 24px 20px !important;\n    cursor: pointer;\n    background: #FFFFFF !important;\n    text-align: left !important;\n    transition: border-color 0.15s, background 0.15s;\n    display: block !important;\n    color: inherit !important;\n    width: 100% !important;\n    font-size: inherit !important;\n  }\n  .wh-path:hover { border-color: #1B2640; }\n  .wh-path.is-active {\n    border-color: #1B2640 !important;\n    background: #1B2640 !important;\n  }\n  .wh-path__title {\n    font-size: 16px;\n    font-weight: 600;\n    color: #1B2640;\n    margin-bottom: 6px;\n    display: block;\n    transition: color 0.15s;\n  }\n  .wh-path.is-active .wh-path__title { color: #FFFFFF; }\n  .wh-path__desc {\n    font-size: 13px;\n    color: #6B7280;\n    line-height: 1.5;\n    display: block;\n    transition: color 0.15s;\n  }\n  .wh-path.is-active .wh-path__desc { color: #A8B4CC; }\n  .wh-path__cta {\n    font-size: 12px;\n    font-weight: 700;\n    color: #E8761A;\n    margin-top: 14px;\n    display: block;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    transition: color 0.15s;\n  }\n  .wh-path.is-active .wh-path__cta { color: #f0a060; }\n  .wh-path-prompt {\n    font-size: 13px;\n    color: #9CA3AF;\n    text-align: center;\n    margin-bottom: 24px;\n  }\n  .wh-path-prompt.is-hidden { display: none; }\n  \/* Section wrapper *\/\n  .wh-section { display: none; }\n  .wh-section.is-visible { display: block; }\n  \/* Step labels *\/\n  .wh-step__label {\n    font-size: 11px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.5px;\n    color: #6B7280;\n    margin-bottom: 10px;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n  }\n  .wh-step__label::before {\n    content: attr(data-step);\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 20px;\n    height: 20px;\n    border-radius: 50%;\n    background: #1B2640;\n    color: #FFFFFF;\n    font-size: 11px;\n    font-weight: 700;\n    flex-shrink: 0;\n    letter-spacing: 0;\n  }\n  \/* Axle direction labels *\/\n  .wh-axle-label {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 2px;\n    color: #9CA3AF;\n    text-align: center;\n    margin: 6px 0;\n  }\n  \/* Center bias readout inside the corner grid *\/\n  .wh-grid-bias {\n    grid-column: 1 \/ -1;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 10px;\n    padding: 4px 0;\n  }\n  .wh-grid-bias__label {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 2px;\n    color: #9CA3AF;\n  }\n  .wh-grid-bias__val {\n    font-size: 13px;\n    font-weight: 700;\n    color: #1B2640;\n  }\n  .wh-grid-bias__val strong { color: #E8761A; font-weight: 700; }\n  \/* Corner grid *\/\n  .wh-corner-grid {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 10px;\n  }\n  .wh-corner-card {\n    border: 1.5px solid #DDE1EA;\n    border-radius: 8px;\n    padding: 14px 16px;\n    background: #FFFFFF;\n    transition: border-color 0.15s, opacity 0.15s;\n  }\n  .wh-corner-card.is-off .wh-corner-card__body { opacity: 0.35; pointer-events: none; }\n  .wh-corner-card__head {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    margin-bottom: 12px;\n    gap: 8px;\n  }\n  .wh-corner-card__name {\n    font-size: 13px;\n    font-weight: 700;\n    color: #1B2640;\n  }\n  .wh-corner-card__row {\n    margin-bottom: 10px;\n  }\n  .wh-corner-card__row-label {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    color: #9CA3AF;\n    display: block;\n    margin-bottom: 4px;\n  }\n  .wh-corner-card__total {\n    font-size: 13px;\n    font-weight: 700;\n    color: #E8761A;\n    border-top: 1px solid #EEF0F4;\n    padding-top: 10px;\n    margin-top: 2px;\n  }\n  \/* Selects \u0026 inputs *\/\n  .wh-select {\n    width: 100%;\n    font-size: 13px;\n    color: #1B2640;\n    background: #FFFFFF;\n    border: 1.5px solid #DDE1EA;\n    border-radius: 6px;\n    padding: 7px 10px;\n    cursor: pointer;\n    transition: border-color 0.15s;\n  }\n  .wh-select:hover, .wh-select:focus { border-color: #1B2640; outline: none; }\n  .wh-input {\n    width: 100%;\n    font-size: 15px;\n    font-weight: 600;\n    color: #1B2640;\n    background: #FFFFFF;\n    border: 1.5px solid #DDE1EA;\n    border-radius: 6px;\n    padding: 9px 12px;\n    transition: border-color 0.15s;\n  }\n  .wh-input:hover, .wh-input:focus { border-color: #1B2640; outline: none; }\n  .wh-input::placeholder { color: #C5CCDB; font-weight: 400; }\n  \/* Stepper *\/\n  .wh-stepper {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n  }\n  .wh-stepper__btn {\n    width: 30px !important;\n    height: 30px !important;\n    border: 1.5px solid #DDE1EA !important;\n    border-radius: 6px !important;\n    background: #FFFFFF !important;\n    color: #1B2640 !important;\n    font-size: 16px !important;\n    font-weight: 700 !important;\n    cursor: pointer;\n    display: inline-flex !important;\n    align-items: center;\n    justify-content: center;\n    transition: border-color 0.15s, background 0.15s;\n    padding: 0 !important;\n    line-height: 1 !important;\n    min-width: 0 !important;\n    min-height: 0 !important;\n  }\n  .wh-stepper__btn:hover { border-color: #1B2640 !important; }\n  .wh-stepper__btn:disabled { opacity: 0.3; cursor: default; border-color: #DDE1EA !important; }\n  .wh-stepper__val {\n    font-size: 15px;\n    font-weight: 700;\n    color: #1B2640;\n    min-width: 52px;\n    text-align: center;\n  }\n  .wh-stepper__val small {\n    display: block;\n    font-size: 10px;\n    font-weight: 600;\n    color: #9CA3AF;\n    letter-spacing: 0.5px;\n  }\n  .wh-stepper--sm .wh-stepper__btn {\n    width: 24px !important;\n    height: 24px !important;\n    font-size: 13px !important;\n    border-radius: 5px !important;\n  }\n  .wh-stepper--sm .wh-stepper__val { min-width: 22px; font-size: 13px; }\n  .wh-slugrow {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n    padding: 3px 0;\n  }\n  .wh-slugrow__name { font-size: 12px; font-weight: 600; color: #374151; }\n  .wh-slugrow__name small { color: #9CA3AF; font-weight: 500; font-size: 11px; }\n  .wh-alt-title {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.5px;\n    color: #9CA3AF;\n    margin: 16px 0 6px;\n  }\n  \/* Checkbox \/ toggle *\/\n  .wh-check {\n    display: inline-flex;\n    align-items: center;\n    gap: 8px;\n    cursor: pointer;\n    font-size: 13px;\n    color: #374151;\n    user-select: none;\n  }\n  .wh-check input {\n    appearance: none;\n    -webkit-appearance: none;\n    width: 34px;\n    height: 20px;\n    border-radius: 10px;\n    background: #DDE1EA;\n    position: relative;\n    cursor: pointer;\n    transition: background 0.15s;\n    flex-shrink: 0;\n    margin: 0;\n  }\n  .wh-check input::after {\n    content: '';\n    position: absolute;\n    top: 2px;\n    left: 2px;\n    width: 16px;\n    height: 16px;\n    border-radius: 50%;\n    background: #FFFFFF;\n    transition: left 0.15s;\n    box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n  }\n  .wh-check input:checked { background: #E8761A; }\n  .wh-check input:checked::after { left: 16px; }\n  .wh-check--sm { font-size: 12px; color: #6B7280; }\n  \/* Goal bias *\/\n  .wh-bias-row {\n    display: flex;\n    align-items: center;\n    gap: 16px;\n    flex-wrap: wrap;\n  }\n  .wh-bias-row input[type=\"range\"] {\n    flex: 1;\n    min-width: 160px;\n    accent-color: #E8761A;\n    cursor: pointer;\n  }\n  .wh-bias-num {\n    width: 84px;\n    text-align: center;\n  }\n  .wh-bias-split {\n    font-size: 12px;\n    font-weight: 600;\n    color: #6B7280;\n    white-space: nowrap;\n  }\n  \/* Blocks \u0026 results *\/\n  .wh-block { margin-bottom: 24px; }\n  .wh-results {\n    background: #F4F5F7;\n    border: 1px solid #DDE1EA;\n    border-radius: 10px;\n    padding: 20px 24px;\n    margin-bottom: 16px;\n  }\n  .wh-results__header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    margin-bottom: 16px;\n    flex-wrap: wrap;\n    gap: 10px;\n  }\n  .wh-results__title {\n    font-size: 16px;\n    font-weight: 600;\n    color: #1B2640;\n  }\n  .wh-results__badge {\n    font-size: 11px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    color: #E8761A;\n    background: #FFF0E6;\n    border: 1px solid #F5C4A0;\n    border-radius: 4px;\n    padding: 3px 10px;\n  }\n  .wh-table {\n    width: 100%;\n    border-collapse: collapse;\n  }\n  .wh-table th {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.5px;\n    color: #9CA3AF;\n    text-align: left;\n    padding: 0 0 7px;\n    border-bottom: 1px solid #DDE1EA;\n  }\n  .wh-table th:last-child { text-align: right; }\n  .wh-table td {\n    padding: 6px 0;\n    border-bottom: 1px solid #EEF0F4;\n    vertical-align: middle;\n    font-size: 13px;\n    color: #374151;\n  }\n  .wh-table tr:last-child td { border-bottom: none; }\n  .wh-table td:last-child {\n    text-align: right;\n    font-weight: 600;\n    color: #1B2640;\n    white-space: nowrap;\n  }\n  .wh-table tr.wh-table__total td {\n    border-top: 1px solid #DDE1EA;\n    font-weight: 700;\n    color: #1B2640;\n    font-size: 14px;\n    padding-top: 10px;\n  }\n  .wh-table tr.wh-table__total td:last-child { color: #E8761A; font-size: 16px; }\n  \/* Balance result corner tiles *\/\n  .wh-result-grid {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 10px;\n    margin-bottom: 4px;\n  }\n  .wh-result-tile {\n    background: #FFFFFF;\n    border: 1.5px solid #DDE1EA;\n    border-radius: 8px;\n    padding: 12px 16px;\n  }\n  .wh-result-tile.is-hot { border-color: #E8761A; background: #FFF8F3; }\n  .wh-result-tile__corner {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    color: #9CA3AF;\n    display: block;\n    margin-bottom: 4px;\n  }\n  .wh-result-tile__slugs {\n    font-size: 22px;\n    font-weight: 700;\n    color: #1B2640;\n    line-height: 1.1;\n  }\n  .wh-result-tile__slugs small {\n    font-size: 12px;\n    font-weight: 600;\n    color: #6B7280;\n  }\n  .wh-result-tile__detail {\n    font-size: 11px;\n    color: #9CA3AF;\n    display: block;\n    margin-top: 3px;\n  }\n  \/* What-to-buy box *\/\n  .wh-buy {\n    background: #FFFFFF;\n    border: 2px solid #E8761A;\n    border-radius: 10px;\n    overflow: hidden;\n    margin-bottom: 12px;\n  }\n  .wh-buy__head {\n    background: #1B2640;\n    color: #FFFFFF;\n    padding: 10px 20px;\n    font-size: 12px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.5px;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n  }\n  .wh-buy__head svg { flex-shrink: 0; }\n  .wh-buy__rows { padding: 4px 20px; }\n  .wh-buy__row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 12px;\n    padding: 10px 0;\n    border-bottom: 1px solid #EEF0F4;\n  }\n  .wh-buy__row:last-child { border-bottom: none; }\n  .wh-buy__row-name { font-size: 14px; font-weight: 600; color: #1B2640; }\n  .wh-buy__row-price { font-size: 14px; font-weight: 600; color: #1B2640; white-space: nowrap; }\n  .wh-buy__total {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 12px 20px;\n    background: #FFF8F3;\n    border-top: 1px solid #F5C4A0;\n    font-size: 14px;\n    font-weight: 700;\n    color: #1B2640;\n  }\n  .wh-buy__total-price { font-size: 20px; color: #E8761A; }\n  .wh-buy__foot {\n    font-size: 11px;\n    color: #6B7280;\n    line-height: 1.5;\n    padding: 10px 20px 12px;\n    border-top: 1px solid #EEF0F4;\n  }\n  \/* Notes \u0026 warnings *\/\n  .wh-note {\n    font-size: 12px;\n    color: #6B7280;\n    line-height: 1.6;\n    background: #F4F5F7;\n    border: 1px solid #DDE1EA;\n    border-radius: 8px;\n    padding: 12px 16px;\n  }\n  .wh-note strong { color: #1B2640; }\n  .wh-warn {\n    font-size: 12px;\n    font-weight: 600;\n    color: #B45309;\n    background: #FFF0E6;\n    border: 1px solid #F5C4A0;\n    border-radius: 8px;\n    padding: 12px 16px;\n    line-height: 1.6;\n    margin-bottom: 12px;\n  }\n  .wh-muted-msg {\n    font-size: 13px;\n    color: #9CA3AF;\n    text-align: center;\n    padding: 18px 0;\n  }\n  .wh-hardware-extra {\n    display: none;\n    margin-top: 12px;\n  }\n  .wh-hardware-extra.is-visible { display: block; }\n  .wh-inline-row {\n    display: flex;\n    align-items: center;\n    gap: 14px;\n    flex-wrap: wrap;\n    margin-top: 12px;\n  }\n  \/* Axle-pair cards (weight adder) *\/\n  .wh-axle-stack { display: flex; flex-direction: column; gap: 10px; }\n  .wh-axle-card__cols {\n    display: grid;\n    grid-template-columns: minmax(0, 1fr) 220px;\n    gap: 4px 28px;\n    align-items: start;\n  }\n  \/* Share button (lives inside the What to buy box) *\/\n  .wh-share {\n    background: none !important;\n    border: 1.5px solid #DDE1EA !important;\n    color: #6B7280 !important;\n    border-radius: 6px !important;\n    padding: 7px 14px !important;\n    font-size: 12px !important;\n    font-weight: 600 !important;\n    cursor: pointer;\n    transition: border-color 0.15s, color 0.15s;\n  }\n  .wh-share:hover { border-color: #E8761A !important; color: #E8761A !important; }\n  .wh-buy__share { padding: 0 20px 14px; }\n  \/* Add to cart *\/\n  .wh-buy__cart { padding: 12px 20px 6px; }\n  .wh-cart {\n    width: 100% !important;\n    display: inline-flex !important;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    background: #E8761A !important;\n    color: #FFFFFF !important;\n    border: none !important;\n    border-radius: 8px !important;\n    padding: 13px 20px !important;\n    font-size: 14px !important;\n    font-weight: 700 !important;\n    cursor: pointer;\n    transition: opacity 0.15s;\n  }\n  .wh-cart:hover { opacity: 0.9; }\n  .wh-cart:disabled { opacity: 0.6; cursor: default; }\n  \/* Alternative options inside the What to buy box *\/\n  .wh-buy__alts { border-top: 1px solid #EEF0F4; padding: 10px 20px 8px; }\n  .wh-buy__alts-title {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.5px;\n    color: #9CA3AF;\n    margin-bottom: 8px;\n  }\n  .wh-buy__alt {\n    display: flex !important;\n    align-items: center;\n    justify-content: space-between;\n    gap: 12px;\n    width: 100% !important;\n    background: #FFFFFF !important;\n    border: 1.5px solid #DDE1EA !important;\n    border-radius: 6px !important;\n    padding: 8px 12px !important;\n    margin-bottom: 6px;\n    cursor: pointer;\n    text-align: left !important;\n    transition: border-color 0.15s;\n  }\n  .wh-buy__alt:hover { border-color: #1B2640 !important; }\n  .wh-buy__alt-name { font-size: 12px; font-weight: 600; color: #1B2640; }\n  .wh-buy__alt-meta { font-size: 12px; font-weight: 600; color: #6B7280; white-space: nowrap; }\n  \/* Dark storefront background - text that sits directly on the page *\/\n  .wh-step__label { color: #A8B4CC; }\n  .wh-step__label::before { background: #E8761A; }\n  .wh-axle-label { color: #8E9CBB; }\n  .wh-grid-bias__label { color: #8E9CBB; }\n  .wh-grid-bias__val { color: #FFFFFF; }\n  .wh-path-prompt { color: #8E9CBB; }\n  .wh-bias-split { color: #A8B4CC; }\n  .wh-check--light { color: #C7CFDF; }\n  .wh-hero { border: 1px solid rgba(255,255,255,0.14); }\n  .wh-path.is-active { border-color: #E8761A !important; }\n  @media (max-width: 640px) {\n    .wh-paths { grid-template-columns: 1fr; }\n    .wh-page { padding: 32px 16px 60px; }\n    .wh-corner-card { padding: 12px; }\n    .wh-axle-card__cols { grid-template-columns: 1fr; }\n  }\n\u003c\/style\u003e\n\n\u003cdiv class=\"wh-page\"\u003e\n\n  \u003cdiv class=\"wh-hero\"\u003e\n    \u003cp class=\"wh-hero__title\"\u003eBottom Feeders Weight Calculator\u003c\/p\u003e\n    \u003cp class=\"wh-hero__desc\"\u003eDial in your SCX24's weight before you buy. See exactly how many grams a Bottom Feeders setup adds, or enter your corner weights and let the calculator tell you how many slugs to run in each corner to hit your target front bias.\u003c\/p\u003e\n    \u003cdiv class=\"wh-hero__badges\"\u003e\n      \u003cspan class=\"wh-hero__badge wh-hero__badge--orange\"\u003eFits Mofo RC X15 axles\u003c\/span\u003e\n      \u003cspan class=\"wh-hero__badge\"\u003e6 slugs per hanger\u003c\/span\u003e\n      \u003cspan class=\"wh-hero__badge\"\u003eSold in pairs\u003c\/span\u003e\n      \u003cspan class=\"wh-hero__badge\"\u003eNylon +7.5 hubs included\u003c\/span\u003e\n      \u003cspan class=\"wh-hero__badge\"\u003eMEUS ISO compatibility coming soon\u003c\/span\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003cdiv class=\"wh-paths\"\u003e\n    \u003cbutton type=\"button\" class=\"wh-path\" id=\"wh-path-adder\" data-section=\"adder\"\u003e\n      \u003cspan class=\"wh-path__title\"\u003eHow much weight am I adding?\u003c\/span\u003e\n      \u003cspan class=\"wh-path__desc\"\u003eBuild out a hanger setup: pick slug materials, counts, and hubs for each pair, and see total grams added.\u003c\/span\u003e\n      \u003cspan class=\"wh-path__cta\"\u003eBuild my setup →\u003c\/span\u003e\n    \u003c\/button\u003e\n    \u003cbutton type=\"button\" class=\"wh-path\" id=\"wh-path-balance\" data-section=\"balance\"\u003e\n      \u003cspan class=\"wh-path__title\"\u003eBalance my rig\u003c\/span\u003e\n      \u003cspan class=\"wh-path__desc\"\u003eEnter your corner weights and target front bias, and we'll recommend the slug material and counts for each corner.\u003c\/span\u003e\n      \u003cspan class=\"wh-path__cta\"\u003eBalance it →\u003c\/span\u003e\n    \u003c\/button\u003e\n  \u003c\/div\u003e\n  \u003cp class=\"wh-path-prompt\" id=\"wh-path-prompt\"\u003eSelect a calculator above to get started.\u003c\/p\u003e\n\n  \u003c!-- ============ SECTION: WEIGHT ADDER ============ --\u003e\n  \u003cdiv class=\"wh-section\" id=\"wh-section-adder\"\u003e\n    \u003cdiv class=\"wh-block\"\u003e\n      \u003cp class=\"wh-step__label\" data-step=\"1\"\u003eConfigure your hanger pairs\u003c\/p\u003e\n      \u003cp class=\"wh-axle-label\"\u003e▲ Front of vehicle\u003c\/p\u003e\n      \u003cdiv class=\"wh-axle-stack\" id=\"wh-adder-grid\"\u003e\u003c\/div\u003e\n      \u003cp class=\"wh-axle-label\"\u003eRear of vehicle ▼\u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"wh-block\"\u003e\n      \u003cp class=\"wh-step__label\" data-step=\"2\"\u003eTotal weight added\u003c\/p\u003e\n      \u003cdiv class=\"wh-results\"\u003e\n        \u003cdiv class=\"wh-results__header\"\u003e\n          \u003cp class=\"wh-results__title\"\u003eSetup summary\u003c\/p\u003e\n          \u003cspan class=\"wh-results__badge\" id=\"wh-adder-badge\"\u003e0.0g added\u003c\/span\u003e\n        \u003c\/div\u003e\n        \u003ctable class=\"wh-table\" id=\"wh-adder-table\"\u003e\u003c\/table\u003e\n      \u003c\/div\u003e\n      \u003cdiv id=\"wh-adder-buy\"\u003e\u003c\/div\u003e\n      \u003cdiv class=\"wh-note\" id=\"wh-adder-note\"\u003e\u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- ============ SECTION: BALANCE ============ --\u003e\n  \u003cdiv class=\"wh-section\" id=\"wh-section-balance\"\u003e\n    \u003cdiv class=\"wh-block\"\u003e\n      \u003cp class=\"wh-step__label\" data-step=\"1\"\u003eEnter your corner weights (grams)\u003c\/p\u003e\n      \u003cp class=\"wh-axle-label\"\u003e▲ Front of vehicle\u003c\/p\u003e\n      \u003cdiv class=\"wh-corner-grid\"\u003e\n        \u003cdiv class=\"wh-corner-card\"\u003e\n          \u003cdiv class=\"wh-corner-card__head\"\u003e\u003cspan class=\"wh-corner-card__name\"\u003eFront Left\u003c\/span\u003e\u003c\/div\u003e\n          \u003cinput class=\"wh-input\" type=\"number\" inputmode=\"decimal\" min=\"0\" step=\"0.1\" placeholder=\"e.g. 78.5\" id=\"wh-cw-fl\"\u003e\n        \u003c\/div\u003e\n        \u003cdiv class=\"wh-corner-card\"\u003e\n          \u003cdiv class=\"wh-corner-card__head\"\u003e\u003cspan class=\"wh-corner-card__name\"\u003eFront Right\u003c\/span\u003e\u003c\/div\u003e\n          \u003cinput class=\"wh-input\" type=\"number\" inputmode=\"decimal\" min=\"0\" step=\"0.1\" placeholder=\"e.g. 76.0\" id=\"wh-cw-fr\"\u003e\n        \u003c\/div\u003e\n        \u003cdiv class=\"wh-corner-card\"\u003e\n          \u003cdiv class=\"wh-corner-card__head\"\u003e\u003cspan class=\"wh-corner-card__name\"\u003eRear Left\u003c\/span\u003e\u003c\/div\u003e\n          \u003cinput class=\"wh-input\" type=\"number\" inputmode=\"decimal\" min=\"0\" step=\"0.1\" placeholder=\"e.g. 70.2\" id=\"wh-cw-rl\"\u003e\n        \u003c\/div\u003e\n        \u003cdiv class=\"wh-corner-card\"\u003e\n          \u003cdiv class=\"wh-corner-card__head\"\u003e\u003cspan class=\"wh-corner-card__name\"\u003eRear Right\u003c\/span\u003e\u003c\/div\u003e\n          \u003cinput class=\"wh-input\" type=\"number\" inputmode=\"decimal\" min=\"0\" step=\"0.1\" placeholder=\"e.g. 71.8\" id=\"wh-cw-rr\"\u003e\n        \u003c\/div\u003e\n      \u003c\/div\u003e\n      \u003cp class=\"wh-axle-label\"\u003eRear of vehicle ▼\u003c\/p\u003e\n      \u003cdiv class=\"wh-inline-row\"\u003e\n        \u003clabel class=\"wh-check wh-check--sm wh-check--light\"\u003e\n          \u003cinput type=\"checkbox\" id=\"wh-hw-installed\"\u003e\n          \u003cspan\u003eI already have Bottom Feeders. My corner weights were measured with the hangers \u0026amp; hubs installed (empty)\u003c\/span\u003e\n        \u003c\/label\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"wh-hardware-extra is-visible\" id=\"wh-hw-extra\"\u003e\n        \u003cdiv class=\"wh-note\" style=\"margin-bottom:10px;\"\u003eHanger hardware will be added to each corner before balancing: \u003cstrong\u003e11.5g hanger\u003c\/strong\u003e + your hub choice below, per corner.\u003c\/div\u003e\n        \u003cselect class=\"wh-select\" id=\"wh-hw-hub\" style=\"max-width:320px;\"\u003e\n          \u003coption value=\"nylon\"\u003eNylon +7.5 hub, 1.0g (included)\u003c\/option\u003e\n          \u003coption value=\"aluminum\"\u003eAluminum +6 hub, 2.4g\u003c\/option\u003e\n          \u003coption value=\"brass\"\u003eBrass +6 hub, 9.0g\u003c\/option\u003e\n        \u003c\/select\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"wh-block\"\u003e\n      \u003cp class=\"wh-step__label\" data-step=\"2\"\u003eSet your target front bias\u003c\/p\u003e\n      \u003cdiv class=\"wh-bias-row\"\u003e\n        \u003cinput type=\"range\" min=\"40\" max=\"75\" step=\"1\" value=\"60\" id=\"wh-bias-slider\"\u003e\n        \u003cinput class=\"wh-input wh-bias-num\" type=\"number\" min=\"1\" max=\"99\" step=\"1\" value=\"60\" id=\"wh-bias-num\"\u003e\n        \u003cspan class=\"wh-bias-split\" id=\"wh-bias-split\"\u003e60% front \/ 40% rear\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"wh-inline-row\"\u003e\n        \u003clabel class=\"wh-check wh-check--light\"\u003e\n          \u003cinput type=\"checkbox\" id=\"wh-lr-toggle\" checked\u003e\n          \u003cspan\u003eAlso balance left \/ right on each axle\u003c\/span\u003e\n        \u003c\/label\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"wh-block\"\u003e\n      \u003cp class=\"wh-step__label\" data-step=\"3\"\u003eYour recommended setup\u003c\/p\u003e\n      \u003cdiv class=\"wh-results\"\u003e\n        \u003cdiv class=\"wh-results__header\"\u003e\n          \u003cp class=\"wh-results__title\"\u003eRecommended setup\u003c\/p\u003e\n          \u003cspan class=\"wh-results__badge\" id=\"wh-bal-badge\"\u003e-\u003c\/span\u003e\n        \u003c\/div\u003e\n        \u003cdiv id=\"wh-bal-body\"\u003e\n          \u003cp class=\"wh-muted-msg\"\u003eEnter all four corner weights above to see your setup.\u003c\/p\u003e\n        \u003c\/div\u003e\n      \u003c\/div\u003e\n      \u003cdiv id=\"wh-bal-buy\"\u003e\u003c\/div\u003e\n      \u003cdiv id=\"wh-bal-notes\"\u003e\u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n\u003c\/div\u003e\n\n\u003cscript\u003e\n(function () {\n  'use strict';\n\n  \/* ================= DATA =================\n     HANGER_BASE is the assembled nylon hanger weight (g each).\n     Update this one number when the final assembled weight is confirmed. *\/\n  var HANGER_BASE = 11.5;\n\n  var HUBS = {\n    nylon:    { label: 'Nylon +7.5 (included)', w: 1.0 },\n    aluminum: { label: 'Aluminum +6',           w: 2.4 },\n    brass:    { label: 'Brass +6',              w: 9.0 }\n  };\n\n  \/* per = single slug weight; full6 = measured weight of a full hanger of 6.\n     Partial hangers use per-slug weight; a full 6 snaps to the measured value. *\/\n  var SLUGS = {\n    aluminum:  { label: 'Aluminum',        per: 0.6, full6: 3.1,  packs4: false },\n    stainless: { label: 'Stainless Steel', per: 1.7, full6: 10.5, packs4: true  },\n    brass:     { label: 'Brass',           per: 2.0, full6: 11.2, packs4: false },\n    tungsten:  { label: 'Tungsten',        per: 3.1, full6: 18.7, packs4: true  }\n  };\n\n  \/* Store prices - update here when pricing changes.\n     pairBare  = Bottom Feeders pair, no slugs (nylon hubs included)\n     pairLoaded = pair + full 12-pack of one material\n     pack12 \/ pack4 = slugs sold separately *\/\n  var PRICES = {\n    pairBare: 27.99,\n    pairLoaded: { aluminum: 34.99, stainless: 39.99, brass: 44.99, tungsten: 79.99 },\n    pack12:     { aluminum: 9.99,  stainless: 14.99, brass: 19.99, tungsten: 54.99 },\n    pack4:      { stainless: 6.49, tungsten: 19.99 }\n  };\n  function money(x) { return '$' + x.toFixed(2); }\n\n  \/* Shopify wiring: storefront + variant IDs for every SKU the calculator sells *\/\n  var STORE_URL = 'https:\/\/moforc.com';\n  var VARIANTS = {\n    pair_bare:      '46273818067001',\n    pair_aluminum:  '46260716273721',\n    pair_stainless: '46260716306489',\n    pair_brass:     '46260716339257',\n    pair_tungsten:  '46260716372025',\n    pack12_aluminum:  '46260720304185',\n    pack4_stainless:  '46273914470457',\n    pack12_stainless: '46260720336953',\n    pack12_brass:     '46260720369721',\n    pack4_tungsten:   '46260720402489',\n    pack12_tungsten:  '46260720566329'\n  };\n  function cartPermalink(items) {\n    return STORE_URL + '\/cart\/' + items.map(function (i) { return i.vid + ':' + i.qty; }).join(',');\n  }\n  \/* When embedded on the store itself, add via the cart API and land on \/cart.\n     Anywhere else (or if the API call fails), fall back to the checkout permalink. *\/\n  function addSetupToCart(items) {\n    var onStore = location.hostname \u0026\u0026 STORE_URL.indexOf(location.hostname) !== -1;\n    if (onStore \u0026\u0026 window.fetch) {\n      fetch('\/cart\/add.js', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\/json' },\n        body: JSON.stringify({ items: items.map(function (i) { return { id: parseInt(i.vid, 10), quantity: i.qty }; }) })\n      }).then(function (r) {\n        if (!r.ok) throw new Error('cart add failed');\n        window.location.href = '\/cart';\n      }).catch(function () {\n        window.location.href = cartPermalink(items);\n      });\n    } else {\n      window.location.href = cartPermalink(items);\n    }\n  }\n\n  \/* ---- Live prices ----\n     Pulls current variant prices from the storefront product endpoints so\n     admin price edits and sales flow into the calculator automatically.\n     Any failure (draft product, renamed handle, offline preview) is silent\n     and the built-in PRICES above stay in effect as the fallback. *\/\n  var PRODUCT_HANDLES = [\n    'bottom-feeders-super-weight-hangers-for-axial%C2%AE-scx24%E2%84%A2',\n    'keel-weight-slug-replacements-8mm-diameter-x-6mm-long'\n  ];\n  function applyLivePrice(key, price) {\n    if (!(price \u003e 0)) return;\n    if (key === 'pair_bare') PRICES.pairBare = price;\n    else if (key.indexOf('pair_') === 0) PRICES.pairLoaded[key.slice(5)] = price;\n    else if (key.indexOf('pack12_') === 0) PRICES.pack12[key.slice(7)] = price;\n    else if (key.indexOf('pack4_') === 0) PRICES.pack4[key.slice(6)] = price;\n  }\n  function loadLivePrices(done) {\n    if (!window.fetch) return;\n    var byId = {};\n    Object.keys(VARIANTS).forEach(function (k) { byId[VARIANTS[k]] = k; });\n    var pending = PRODUCT_HANDLES.length;\n    var changed = false;\n    PRODUCT_HANDLES.forEach(function (h) {\n      fetch(STORE_URL + '\/products\/' + h + '.js', { credentials: 'omit' })\n        .then(function (r) { return r.ok ? r.json() : null; })\n        .then(function (p) {\n          if (p \u0026\u0026 p.variants) {\n            p.variants.forEach(function (v) {\n              var key = byId[String(v.id)];\n              if (key) { applyLivePrice(key, v.price \/ 100); changed = true; }\n            });\n          }\n        })\n        .catch(function () {})\n        .then(function () { if (--pending === 0 \u0026\u0026 changed) done(); });\n    });\n  }\n\n  var CORNERS = [\n    { key: 'fl', name: 'Front Left'  },\n    { key: 'fr', name: 'Front Right' },\n    { key: 'rl', name: 'Rear Left'   },\n    { key: 'rr', name: 'Rear Right'  }\n  ];\n\n  function slugWeight(mat, n) {\n    if (n \u003c= 0) return 0;\n    if (n \u003e= 6) return SLUGS[mat].full6;\n    return n * SLUGS[mat].per;\n  }\n  function fmt(x) { return (Math.round(x * 10) \/ 10).toFixed(1); }\n  function esc(s) { return String(s).replace(\/\u0026\/g,'\u0026amp;').replace(\/\u003c\/g,'\u0026lt;'); }\n\n  \/* ================= PATH SWITCHING ================= *\/\n  var activeSection = null;\n  var pathButtons = document.querySelectorAll('.wh-path');\n  var prompt = document.getElementById('wh-path-prompt');\n  pathButtons.forEach(function (btn) {\n    btn.addEventListener('click', function () {\n      pathButtons.forEach(function (b) { b.classList.remove('is-active'); });\n      btn.classList.add('is-active');\n      prompt.classList.add('is-hidden');\n      document.querySelectorAll('.wh-section').forEach(function (s) { s.classList.remove('is-visible'); });\n      activeSection = btn.getAttribute('data-section');\n      document.getElementById('wh-section-' + activeSection).classList.add('is-visible');\n      syncHash();\n    });\n  });\n\n  \/* ================= MODE 1: WEIGHT ADDER ================= *\/\n  var MATS = ['aluminum', 'stainless', 'brass', 'tungsten'];\n\n  var AXLES = [\n    { key: 'front', name: 'Front pair' },\n    { key: 'rear',  name: 'Rear pair'  }\n  ];\n  \/* Stainless default - best value per gram. Counts are per hanger; the pair mirrors left\/right. *\/\n  var adderState = {\n    front: { on: true, hub: 'nylon', counts: { aluminum: 0, stainless: 6, brass: 0, tungsten: 0 } },\n    rear:  { on: true, hub: 'nylon', counts: { aluminum: 0, stainless: 6, brass: 0, tungsten: 0 } }\n  };\n\n  function cornerSlugCount(counts) {\n    return MATS.reduce(function (s, m) { return s + counts[m]; }, 0);\n  }\n  \/* Mixed loads sum per-slug weights; a uniform full 6 snaps to the measured value *\/\n  function cornerSlugWeight(counts) {\n    var used = MATS.filter(function (m) { return counts[m] \u003e 0; });\n    if (used.length === 1 \u0026\u0026 counts[used[0]] === 6) return SLUGS[used[0]].full6;\n    return used.reduce(function (s, m) { return s + counts[m] * SLUGS[m].per; }, 0);\n  }\n\n  function buildAdderGrid() {\n    var grid = document.getElementById('wh-adder-grid');\n    var html = '';\n    AXLES.forEach(function (ax) {\n      var st = adderState[ax.key];\n      html +=\n        '\u003cdiv class=\"wh-corner-card' + (st.on ? '' : ' is-off') + '\" id=\"wh-ac-' + ax.key + '\"\u003e' +\n          '\u003cdiv class=\"wh-corner-card__head\"\u003e' +\n            '\u003cspan class=\"wh-corner-card__name\"\u003e' + ax.name + ' \u003csmall style=\"font-weight:500;color:#9CA3AF;\"\u003e(mirrored left \u0026amp; right)\u003c\/small\u003e\u003c\/span\u003e' +\n            '\u003clabel class=\"wh-check wh-check--sm\"\u003e\u003cinput type=\"checkbox\" data-axle=\"' + ax.key + '\" data-role=\"on\"' + (st.on ? ' checked' : '') + '\u003e\u003cspan\u003eHangers\u003c\/span\u003e\u003c\/label\u003e' +\n          '\u003c\/div\u003e' +\n          '\u003cdiv class=\"wh-corner-card__body wh-axle-card__cols\"\u003e' +\n            '\u003cdiv\u003e' +\n              '\u003cspan class=\"wh-corner-card__row-label\" id=\"wh-ac-cap-' + ax.key + '\"\u003eSlugs: ' + cornerSlugCount(st.counts) + ' of 6 holes filled per hanger\u003c\/span\u003e' +\n              MATS.map(function (m) {\n                return '\u003cdiv class=\"wh-slugrow\"\u003e' +\n                  '\u003cspan class=\"wh-slugrow__name\"\u003e' + SLUGS[m].label + ' \u003csmall\u003e' + SLUGS[m].per + 'g\u003c\/small\u003e\u003c\/span\u003e' +\n                  '\u003cdiv class=\"wh-stepper wh-stepper--sm\"\u003e' +\n                    '\u003cbutton type=\"button\" class=\"wh-stepper__btn\" data-axle=\"' + ax.key + '\" data-mat=\"' + m + '\" data-role=\"minus\"\u003e−\u003c\/button\u003e' +\n                    '\u003cspan class=\"wh-stepper__val\" id=\"wh-ac-n-' + ax.key + '-' + m + '\"\u003e' + st.counts[m] + '\u003c\/span\u003e' +\n                    '\u003cbutton type=\"button\" class=\"wh-stepper__btn\" data-axle=\"' + ax.key + '\" data-mat=\"' + m + '\" data-role=\"plus\"\u003e+\u003c\/button\u003e' +\n                  '\u003c\/div\u003e' +\n                '\u003c\/div\u003e';\n              }).join('') +\n            '\u003c\/div\u003e' +\n            '\u003cdiv\u003e' +\n              '\u003cspan class=\"wh-corner-card__row-label\"\u003eHubs (both sides)\u003c\/span\u003e' +\n              '\u003cselect class=\"wh-select\" data-axle=\"' + ax.key + '\" data-role=\"hub\"\u003e' +\n                '\u003coption value=\"nylon\"' + (st.hub === 'nylon' ? ' selected' : '') + '\u003eNylon +7.5, 1.0g (included)\u003c\/option\u003e' +\n                '\u003coption value=\"aluminum\"' + (st.hub === 'aluminum' ? ' selected' : '') + '\u003eAluminum +6, 2.4g\u003c\/option\u003e' +\n                '\u003coption value=\"brass\"' + (st.hub === 'brass' ? ' selected' : '') + '\u003eBrass +6, 9.0g\u003c\/option\u003e' +\n              '\u003c\/select\u003e' +\n            '\u003c\/div\u003e' +\n          '\u003c\/div\u003e' +\n          '\u003cdiv class=\"wh-corner-card__total\" id=\"wh-ac-total-' + ax.key + '\"\u003e\u003c\/div\u003e' +\n        '\u003c\/div\u003e';\n      if (ax.key === 'front') {\n        html += '\u003cdiv class=\"wh-grid-bias\"\u003e' +\n          '\u003cspan class=\"wh-grid-bias__label\"\u003eAdded weight bias\u003c\/span\u003e' +\n          '\u003cspan class=\"wh-grid-bias__val\" id=\"wh-adder-bias\"\u003e-\u003c\/span\u003e' +\n        '\u003c\/div\u003e';\n      }\n    });\n    grid.innerHTML = html;\n\n    grid.addEventListener('change', function (e) {\n      var el = e.target;\n      var key = el.getAttribute('data-axle');\n      if (!key) return;\n      var role = el.getAttribute('data-role');\n      if (role === 'on') {\n        adderState[key].on = el.checked;\n        document.getElementById('wh-ac-' + key).classList.toggle('is-off', !el.checked);\n      } else if (role === 'hub') {\n        adderState[key].hub = el.value;\n      }\n      renderAdder();\n    });\n    grid.addEventListener('click', function (e) {\n      var el = e.target.closest('button[data-role]');\n      if (!el) return;\n      var key = el.getAttribute('data-axle');\n      var mat = el.getAttribute('data-mat');\n      if (!key || !mat) return;\n      var role = el.getAttribute('data-role');\n      var st = adderState[key];\n      var total = cornerSlugCount(st.counts);\n      if (role === 'minus' \u0026\u0026 st.counts[mat] \u003e 0) st.counts[mat]--;\n      if (role === 'plus' \u0026\u0026 total \u003c 6) st.counts[mat]++;\n      document.getElementById('wh-ac-n-' + key + '-' + mat).textContent = st.counts[mat];\n      document.getElementById('wh-ac-cap-' + key).textContent =\n        'Slugs: ' + cornerSlugCount(st.counts) + ' of 6 holes filled per hanger';\n      renderAdder();\n    });\n  }\n\n  function cornerAdded(st) {\n    if (!st.on) return 0;\n    return HANGER_BASE + HUBS[st.hub].w + cornerSlugWeight(st.counts);\n  }\n\n  \/* Totals for any hypothetical adder state (used to price the alternatives) *\/\n  function adderTotals(state) {\n    var front = 0, rear = 0, pairs = 0, slugTotals = {};\n    AXLES.forEach(function (ax) {\n      var st = state[ax.key];\n      if (!st.on) return;\n      pairs++;\n      var per = cornerAdded(st);\n      if (ax.key === 'front') front += 2 * per; else rear += 2 * per;\n      MATS.forEach(function (m) { if (st.counts[m]) slugTotals[m] = (slugTotals[m] || 0) + 2 * st.counts[m]; });\n    });\n    return { total: front + rear, pairs: pairs, slugTotals: slugTotals };\n  }\n\n  \/* Same setup, but every filled hole swapped to one material *\/\n  function uniformVariant(mat) {\n    var v = {};\n    AXLES.forEach(function (ax) {\n      var st = adderState[ax.key];\n      var counts = { aluminum: 0, stainless: 0, brass: 0, tungsten: 0 };\n      counts[mat] = Math.min(6, cornerSlugCount(st.counts));\n      v[ax.key] = { on: st.on, hub: st.hub, counts: counts };\n    });\n    return v;\n  }\n\n  var adderAlts = [];\n\n  function refreshAdderInputs() {\n    AXLES.forEach(function (ax) {\n      var st = adderState[ax.key];\n      document.getElementById('wh-ac-' + ax.key).classList.toggle('is-off', !st.on);\n      document.querySelector('input[data-axle=\"' + ax.key + '\"][data-role=\"on\"]').checked = st.on;\n      document.querySelector('select[data-axle=\"' + ax.key + '\"][data-role=\"hub\"]').value = st.hub;\n      MATS.forEach(function (m) {\n        document.getElementById('wh-ac-n-' + ax.key + '-' + m).textContent = st.counts[m];\n      });\n      document.getElementById('wh-ac-cap-' + ax.key).textContent =\n        'Slugs: ' + cornerSlugCount(st.counts) + ' of 6 holes filled per hanger';\n    });\n  }\n\n  function renderAdder() {\n    var front = 0, rear = 0, pairs = 0;\n    var hubTotals = {}, slugTotals = {}, slugGrams = {};\n    AXLES.forEach(function (ax) {\n      var st = adderState[ax.key];\n      var perCorner = cornerAdded(st);\n      var totalEl = document.getElementById('wh-ac-total-' + ax.key);\n      totalEl.textContent = st.on\n        ? '+' + fmt(2 * perCorner) + 'g this axle (+' + fmt(perCorner) + 'g per corner)'\n        : 'No hangers (+0.0g)';\n      if (!st.on) return;\n      pairs++;\n      if (ax.key === 'front') front += 2 * perCorner; else rear += 2 * perCorner;\n      hubTotals[st.hub] = (hubTotals[st.hub] || 0) + 2;\n      var used = MATS.filter(function (m) { return st.counts[m] \u003e 0; });\n      var snap = used.length === 1 \u0026\u0026 st.counts[used[0]] === 6;\n      used.forEach(function (m) {\n        slugTotals[m] = (slugTotals[m] || 0) + 2 * st.counts[m];\n        slugGrams[m] = (slugGrams[m] || 0) + 2 * (snap ? SLUGS[m].full6 : st.counts[m] * SLUGS[m].per);\n      });\n    });\n    var hangers = pairs * 2;\n    var total = front + rear;\n    document.getElementById('wh-adder-badge').textContent = fmt(total) + 'g added';\n    document.getElementById('wh-adder-bias').innerHTML = total \u003e 0\n      ? '\u003cstrong\u003e' + fmt((front \/ total) * 100) + '% front\u003c\/strong\u003e \/ ' + fmt((rear \/ total) * 100) + '% rear'\n      : '-';\n\n    var rows = '\u003ctr\u003e\u003cth\u003eItem\u003c\/th\u003e\u003cth\u003eWeight\u003c\/th\u003e\u003c\/tr\u003e';\n    rows += '\u003ctr\u003e\u003ctd\u003eHangers × ' + hangers + ' (' + fmt(HANGER_BASE) + 'g each)\u003c\/td\u003e\u003ctd\u003e' + fmt(hangers * HANGER_BASE) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    Object.keys(hubTotals).forEach(function (h) {\n      rows += '\u003ctr\u003e\u003ctd\u003e' + esc(HUBS[h].label) + ' hubs × ' + hubTotals[h] + '\u003c\/td\u003e\u003ctd\u003e' + fmt(hubTotals[h] * HUBS[h].w) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    });\n    MATS.forEach(function (m) {\n      if (!slugTotals[m]) return;\n      rows += '\u003ctr\u003e\u003ctd\u003e' + esc(SLUGS[m].label) + ' slugs × ' + slugTotals[m] + '\u003c\/td\u003e\u003ctd\u003e' + fmt(slugGrams[m]) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    });\n    rows += '\u003ctr\u003e\u003ctd\u003eFront axle \/ rear axle\u003c\/td\u003e\u003ctd\u003e' + fmt(front) + 'g \/ ' + fmt(rear) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    if (total \u003e 0) {\n      var fPct = (front \/ total) * 100;\n      rows += '\u003ctr\u003e\u003ctd\u003eFront\/rear bias of added weight\u003c\/td\u003e\u003ctd\u003e' + fmt(fPct) + '% front \/ ' + fmt(100 - fPct) + '% rear\u003c\/td\u003e\u003c\/tr\u003e';\n    }\n    rows += '\u003ctr class=\"wh-table__total\"\u003e\u003ctd\u003eTotal added to your rig\u003c\/td\u003e\u003ctd\u003e' + fmt(total) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    document.getElementById('wh-adder-table').innerHTML = rows;\n\n    var buyEl = document.getElementById('wh-adder-buy');\n    if (hangers \u003e 0) {\n      var plan = planPurchase(slugTotals, pairs);\n      var nonNylon = Object.keys(hubTotals).some(function (h) { return h !== 'nylon'; });\n      var foot = 'Cheapest combination for this setup.';\n      if (plan.spare \u003e 0) foot += ' Leaves ' + plan.spare + ' spare slug' + (plan.spare === 1 ? '' : 's') + ' for tuning.';\n      if (nonNylon) foot += ' Aluminum and brass hubs are sold separately and not included in the total.';\n\n      \/\/ Alternatives: same holes filled, one material - shown as deltas vs current\n      adderAlts = [];\n      var altBtns = [];\n      MATS.forEach(function (m) {\n        var v = uniformVariant(m);\n        var differs = AXLES.some(function (ax) {\n          return MATS.some(function (mm) { return v[ax.key].counts[mm] !== adderState[ax.key].counts[mm]; });\n        });\n        if (!differs) return;\n        var t = adderTotals(v);\n        if (t.total \u003c= 0) return;\n        var p = planPurchase(t.slugTotals, t.pairs);\n        var dg = t.total - total, dc = p.cost - plan.cost;\n        var gTxt = (dg \u003e= 0 ? '+' : '−') + fmt(Math.abs(dg)) + 'g';\n        var cTxt = dc \u003e 0.005 ? '+' + money(dc) : (dc \u003c -0.005 ? 'save ' + money(-dc) : 'same price');\n        adderAlts.push(v);\n        altBtns.push('\u003cbutton type=\"button\" class=\"wh-buy__alt\" data-alt=\"' + (adderAlts.length - 1) + '\"\u003e' +\n          '\u003cspan class=\"wh-buy__alt-name\"\u003eAll ' + SLUGS[m].label + '\u003c\/span\u003e' +\n          '\u003cspan class=\"wh-buy__alt-meta\"\u003e' + gTxt + ' · ' + cTxt + '\u003c\/span\u003e' +\n        '\u003c\/button\u003e');\n      });\n      var adderAltsHtml = altBtns.length\n        ? '\u003cdiv class=\"wh-buy__alts\"\u003e\u003cp class=\"wh-buy__alts-title\"\u003eOther options: tap to switch\u003c\/p\u003e' + altBtns.join('') + '\u003c\/div\u003e'\n        : '';\n      renderBuyBox(buyEl, plan, foot, adderAltsHtml);\n    } else {\n      buyEl.innerHTML = '';\n    }\n\n    var noteBits = ['Not every hole needs a slug, and you can mix materials freely within one hanger.',\n      'Bottom Feeders are sold in \u003cstrong\u003epairs\u003c\/strong\u003e, bare or loaded with a full 12-pack of one material, and always include \u003cstrong\u003enylon +7.5 hubs\u003c\/strong\u003e. Extra slugs come in \u003cstrong\u003epacks of 12\u003c\/strong\u003e (stainless \u0026amp; tungsten also in \u003cstrong\u003epacks of 4\u003c\/strong\u003e).',\n      'Hub weights shown are the full weight of the hub fitted. Slugs in the hangers ride lower than hub weight, so fill hanger holes first. Step up to aluminum or brass hubs only if your hangers are full and you still need more grams.'];\n    document.getElementById('wh-adder-note').innerHTML = noteBits.join(' ');\n    syncHash();\n  }\n\n  \/* ================= MODE 2: BALANCE ================= *\/\n  var balOverride = null; \/* signature of a user-tapped alternative option *\/\n  function balInputChanged() { balOverride = null; renderBalance(); }\n\n  \/* Minimal grams to add per corner to hit target front bias g (0-1),\n     optionally equalizing left\/right on each axle. Returns [fl, fr, rl, rr]. *\/\n  function solveBalance(c, g, lr) {\n    var F = c[0] + c[1], R = c[2] + c[3], W = F + R;\n    var df = lr ? Math.abs(c[0] - c[1]) : 0;\n    var dr = lr ? Math.abs(c[2] - c[3]) : 0;\n    var Af, Ar;\n    \/\/ bias error at the minimum L\/R-fix point (Af=df, Ar=dr)\n    var h = (F + df) - g * (W + df + dr);\n    if (Math.abs(h) \u003c 1e-9) {\n      Af = df; Ar = dr;\n    } else if (h \u003c 0) {\n      \/\/ front is light: rear gets only its L\/R fix, front carries the bias correction\n      Ar = dr;\n      Af = (g * (W + dr) - F) \/ (1 - g);\n    } else {\n      \/\/ front is heavy: front gets only its L\/R fix, rear carries the bias correction\n      Af = df;\n      Ar = ((F + df) - g * (W + df)) \/ g;\n    }\n    Af = Math.max(0, Af); Ar = Math.max(0, Ar);\n    var out = [0, 0, 0, 0];\n    if (lr) {\n      out[0] = Math.min(Af, Math.max(0, (Af + c[1] - c[0]) \/ 2));\n      out[1] = Af - out[0];\n      out[2] = Math.min(Ar, Math.max(0, (Ar + c[3] - c[2]) \/ 2));\n      out[3] = Ar - out[2];\n    } else {\n      out[0] = out[1] = Af \/ 2;\n      out[2] = out[3] = Ar \/ 2;\n    }\n    return out;\n  }\n\n  \/* Cheapest way to cover n slugs of material m with separate packs *\/\n  function cheapestPacks(m, n) {\n    if (n \u003c= 0) return { cost: 0, covered: 0, items: [] };\n    var p4price = PRICES.pack4[m] || null;\n    var best = null;\n    for (var p12 = 0; p12 \u003c= Math.ceil(n \/ 12); p12++) {\n      var maxP4 = p4price ? Math.ceil(n \/ 4) : 0;\n      for (var p4 = 0; p4 \u003c= maxP4; p4++) {\n        var covered = p12 * 12 + p4 * 4;\n        if (covered \u003c n) continue;\n        var cost = p12 * PRICES.pack12[m] + p4 * (p4price || 0);\n        if (!best || cost \u003c best.cost - 1e-9 ||\n            (Math.abs(cost - best.cost) \u003c 1e-9 \u0026\u0026 covered \u003c best.covered)) {\n          best = { cost: cost, covered: covered, p12: p12, p4: p4 };\n        }\n      }\n    }\n    best.items = [];\n    if (best.p12) best.items.push({ label: SLUGS[m].label + ' slug 12-pack', qty: best.p12, price: best.p12 * PRICES.pack12[m], vid: VARIANTS['pack12_' + m] });\n    if (best.p4) best.items.push({ label: SLUGS[m].label + ' slug 4-pack', qty: best.p4, price: best.p4 * PRICES.pack4[m], vid: VARIANTS['pack4_' + m] });\n    return best;\n  }\n\n  \/* Cheapest full purchase: `pairs` hanger pairs (each bare or loaded with a\n     12-pack of one material) plus separate packs to cover any remaining slugs. *\/\n  function planPurchase(slugNeeds, pairs) {\n    var choices = ['bare'].concat(MATS);\n    var combos = [];\n    if (pairs \u003c= 0) combos.push([]);\n    else if (pairs === 1) choices.forEach(function (a) { combos.push([a]); });\n    else {\n      for (var i = 0; i \u003c choices.length; i++)\n        for (var j = i; j \u003c choices.length; j++) combos.push([choices[i], choices[j]]);\n    }\n    var best = null;\n    combos.forEach(function (loadout) {\n      var cost = 0, supplied = {}, items = [], counts = {};\n      loadout.forEach(function (ch) { counts[ch] = (counts[ch] || 0) + 1; });\n      Object.keys(counts).forEach(function (ch) {\n        var q = counts[ch];\n        if (ch === 'bare') {\n          cost += q * PRICES.pairBare;\n          items.push({ label: 'Bottom Feeders pair (No Weights)', qty: q, price: q * PRICES.pairBare, vid: VARIANTS.pair_bare });\n        } else {\n          cost += q * PRICES.pairLoaded[ch];\n          supplied[ch] = (supplied[ch] || 0) + q * 12;\n          items.push({ label: 'Bottom Feeders pair loaded with ' + SLUGS[ch].label, qty: q, price: q * PRICES.pairLoaded[ch], vid: VARIANTS['pair_' + ch] });\n        }\n      });\n      var spare = 0;\n      MATS.forEach(function (m) {\n        var need = slugNeeds[m] || 0;\n        var have = supplied[m] || 0;\n        var p = cheapestPacks(m, Math.max(0, need - have));\n        cost += p.cost;\n        p.items.forEach(function (it) { items.push(it); });\n        spare += have + p.covered - need;\n      });\n      if (!best || cost \u003c best.cost - 1e-9 || (Math.abs(cost - best.cost) \u003c 1e-9 \u0026\u0026 spare \u003c best.spare)) {\n        best = { cost: cost, items: items, spare: spare };\n      }\n    });\n    return best;\n  }\n\n  var CART_ICON = '\u003csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e\u003ccircle cx=\"9\" cy=\"21\" r=\"1\"\u003e\u003c\/circle\u003e\u003ccircle cx=\"20\" cy=\"21\" r=\"1\"\u003e\u003c\/circle\u003e\u003cpath d=\"M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6\"\u003e\u003c\/path\u003e\u003c\/svg\u003e';\n\n  function renderBuyBox(el, plan, foot, altsHtml) {\n    if (!plan || !plan.items.length || plan.cost \u003c= 0) { el.innerHTML = ''; return; }\n    var rows = plan.items.map(function (it) {\n      return '\u003cdiv class=\"wh-buy__row\"\u003e' +\n        '\u003cspan class=\"wh-buy__row-name\"\u003e' + it.qty + ' × ' + esc(it.label) + '\u003c\/span\u003e' +\n        '\u003cspan class=\"wh-buy__row-price\"\u003e' + money(it.price) + '\u003c\/span\u003e' +\n      '\u003c\/div\u003e';\n    }).join('');\n    var cartStr = plan.items.map(function (it) { return it.vid + ':' + it.qty; }).join(',');\n    el.innerHTML = '\u003cdiv class=\"wh-buy\"\u003e' +\n      '\u003cdiv class=\"wh-buy__head\"\u003e' + CART_ICON + 'What to buy\u003c\/div\u003e' +\n      '\u003cdiv class=\"wh-buy__rows\"\u003e' + rows + '\u003c\/div\u003e' +\n      '\u003cdiv class=\"wh-buy__total\"\u003e\u003cspan\u003eTotal\u003c\/span\u003e\u003cspan class=\"wh-buy__total-price\"\u003e' + money(plan.cost) + '\u003c\/span\u003e\u003c\/div\u003e' +\n      '\u003cdiv class=\"wh-buy__cart\"\u003e\u003cbutton type=\"button\" class=\"wh-cart\" data-cart=\"' + cartStr + '\"\u003e' + CART_ICON + ' Add this setup to cart\u003c\/button\u003e\u003c\/div\u003e' +\n      (altsHtml || '') +\n      (foot ? '\u003cdiv class=\"wh-buy__foot\"\u003e' + foot + '\u003c\/div\u003e' : '') +\n      '\u003cdiv class=\"wh-buy__share\"\u003e\u003cbutton type=\"button\" class=\"wh-share\"\u003eCopy link to this setup\u003c\/button\u003e\u003c\/div\u003e' +\n    '\u003c\/div\u003e';\n  }\n\n  \/* ---- Recommendation engine ----\n     Fills each corner as close as possible to its ideal grams using a set of\n     1 or 2 slug materials (mixing allowed within a hanger, max 6 slugs), then\n     scores each candidate: gram accuracy first, then pack waste, pack count,\n     and a small penalty per extra material. *\/\n  function comboWeight(mats, counts) {\n    var active = [], slugs = 0;\n    for (var i = 0; i \u003c mats.length; i++) if (counts[i] \u003e 0) { active.push(i); slugs += counts[i]; }\n    if (active.length === 1 \u0026\u0026 counts[active[0]] === 6) return SLUGS[mats[active[0]]].full6;\n    var w = 0;\n    for (var j = 0; j \u003c mats.length; j++) w += counts[j] * SLUGS[mats[j]].per;\n    return w;\n  }\n\n  function bestFill(needed, mats) {\n    var best = null;\n    (function rec(idx, counts, used) {\n      if (idx === mats.length) {\n        var w = comboWeight(mats, counts);\n        var err = Math.abs(w - needed);\n        var nmat = counts.filter(function (n) { return n \u003e 0; }).length;\n        if (!best || err \u003c best.err - 1e-9 ||\n            (err \u003c best.err + 1e-9 \u0026\u0026 (used \u003c best.slugs || (used === best.slugs \u0026\u0026 nmat \u003c best.nmat)))) {\n          best = { counts: counts.slice(), w: w, err: err, slugs: used, nmat: nmat };\n        }\n        return;\n      }\n      for (var n = 0; n \u003c= 6 - used; n++) { counts.push(n); rec(idx + 1, counts, used + n); counts.pop(); }\n    })(0, [], 0);\n    return best;\n  }\n\n  function evaluateOption(mats, needed, pairsNeeded) {\n    var fills = needed.map(function (x) { return bestFill(x, mats); });\n    var errSum = 0, totals = {}, totalSlugs = 0, totalW = 0;\n    fills.forEach(function (f) {\n      errSum += f.err; totalW += f.w;\n      f.counts.forEach(function (n, i) {\n        if (n) { totals[mats[i]] = (totals[mats[i]] || 0) + n; totalSlugs += n; }\n      });\n    });\n    var usedMats = MATS.filter(function (m) { return totals[m]; });\n    var plan = planPurchase(totals, pairsNeeded);\n    \/* accuracy first, then real purchase price, then simplicity *\/\n    var score = errSum * 1.5 + plan.cost * 0.04 + Math.max(0, usedMats.length - 1) * 0.75;\n    return { mats: mats, fills: fills, errSum: errSum, totals: totals, usedMats: usedMats,\n             plan: plan, totalSlugs: totalSlugs, totalW: totalW, score: score };\n  }\n\n  function optionSignature(opt) {\n    return CORNERS.map(function (co, i) {\n      return MATS.map(function (m) {\n        var idx = opt.mats.indexOf(m);\n        return idx \u003e= 0 ? opt.fills[i].counts[idx] : 0;\n      }).join('.');\n    }).join('|');\n  }\n\n  function recommend(needed, pairsNeeded) {\n    var cands = [];\n    MATS.forEach(function (m) { cands.push([m]); });\n    for (var i = 0; i \u003c MATS.length; i++)\n      for (var j = i + 1; j \u003c MATS.length; j++) cands.push([MATS[i], MATS[j]]);\n    var opts = cands.map(function (ms) { return evaluateOption(ms, needed, pairsNeeded); });\n    \/* Options that fit within ~one small slug of the best fit are treated as\n       equally accurate - among those, CHEAPEST wins. Sloppier options rank after. *\/\n    var minErr = Math.min.apply(null, opts.map(function (o) { return o.errSum; }));\n    var TOL = 1.5;\n    opts.sort(function (a, b) {\n      var aOk = a.errSum \u003c= minErr + TOL, bOk = b.errSum \u003c= minErr + TOL;\n      if (aOk !== bOk) return aOk ? -1 : 1;\n      if (aOk) {\n        if (Math.abs(a.plan.cost - b.plan.cost) \u003e 0.005) return a.plan.cost - b.plan.cost;\n        if (Math.abs(a.errSum - b.errSum) \u003e 1e-9) return a.errSum - b.errSum;\n        return a.usedMats.length - b.usedMats.length;\n      }\n      return a.score - b.score;\n    });\n    return opts;\n  }\n\n  function readCorner(id) {\n    var v = document.getElementById(id).value;\n    if (v === '' || v === null) return null;\n    var n = parseFloat(v);\n    return (isFinite(n) \u0026\u0026 n \u003e 0) ? n : null;\n  }\n\n  function renderBalance() {\n    var body = document.getElementById('wh-bal-body');\n    var badge = document.getElementById('wh-bal-badge');\n    var notes = document.getElementById('wh-bal-notes');\n    var raw = [readCorner('wh-cw-fl'), readCorner('wh-cw-fr'), readCorner('wh-cw-rl'), readCorner('wh-cw-rr')];\n    if (raw.some(function (v) { return v === null; })) {\n      badge.textContent = '-';\n      body.innerHTML = '\u003cp class=\"wh-muted-msg\"\u003eEnter all four corner weights above to see your setup.\u003c\/p\u003e';\n      notes.innerHTML = '';\n      document.getElementById('wh-bal-buy').innerHTML = '';\n      return;\n    }\n\n    var installed = document.getElementById('wh-hw-installed').checked;\n    var hwHub = document.getElementById('wh-hw-hub').value;\n    var hwPerCorner = installed ? 0 : HANGER_BASE + HUBS[hwHub].w;\n\n    var g = parseFloat(document.getElementById('wh-bias-num').value) \/ 100;\n    if (!isFinite(g) || g \u003c= 0 || g \u003e= 1) g = 0.60;\n    var lr = document.getElementById('wh-lr-toggle').checked;\n\n    \/* Hanger layout scenarios - never sell more pairs than the balance needs.\n       The hanger hardware itself shifts balance, so each layout is re-solved\n       with hardware only on the corners that actually get hangers. *\/\n    var scenarios = installed\n      ? [{ mask: [1, 1, 1, 1], pairs: 0, where: null }]\n      : [\n          { mask: [1, 1, 1, 1], pairs: 2, where: 'all four corners' },\n          { mask: [1, 1, 0, 0], pairs: 1, where: 'the front axle' },\n          { mask: [0, 0, 1, 1], pairs: 1, where: 'the rear axle' }\n        ];\n    var pick = null;\n    scenarios.forEach(function (sc) {\n      var cs = raw.map(function (v, i) { return v + (sc.mask[i] ? hwPerCorner : 0); });\n      var addS = solveBalance(cs, g, lr);\n      var dropped = 0;\n      var needS = addS.map(function (x, i) { if (sc.mask[i]) return x; dropped += x; return 0; });\n      if (dropped \u003e 2.0) return; \/\/ this layout can't reach the target\n      var opts = recommend(needS, sc.pairs);\n      \/* scenarios compared in dollars, with ~$5 per gram of missed fit *\/\n      var scScore = opts[0].plan.cost + (opts[0].errSum + dropped) * 5;\n      if (!pick || scScore \u003c pick.scScore) pick = { sc: sc, c: cs, add: needS, options: opts, scScore: scScore };\n    });\n    var sc = pick.sc;\n    var c = pick.c;\n    var add = pick.add;\n    var options = pick.options;\n    var pairsNeeded = sc.pairs;\n    var win = options[0];\n    if (balOverride) {\n      for (var wi = 0; wi \u003c options.length; wi++) {\n        if (optionSignature(options[wi]) === balOverride) { win = options[wi]; break; }\n      }\n    }\n\n    var W0 = c[0] + c[1] + c[2] + c[3];\n    var curFront = (c[0] + c[1]) \/ W0;\n\n    var achievedAdd = win.fills.map(function (f) { return f.w; });\n    var newC = c.map(function (v, i) { return v + achievedAdd[i]; });\n    var newW = newC[0] + newC[1] + newC[2] + newC[3];\n    var newFront = (newC[0] + newC[1]) \/ newW;\n    var totalAdded = win.totalW;\n    var totalSlugs = win.totalSlugs;\n    var matNames = win.usedMats.map(function (m) { return SLUGS[m].label; }).join(' + ');\n\n    badge.textContent = totalSlugs \u003e 0\n      ? matNames + ' · ' + totalSlugs + ' slugs · +' + fmt(totalAdded) + 'g'\n      : 'On target';\n\n    function fillLabel(opt, i) {\n      var parts = [];\n      opt.mats.forEach(function (m, k) {\n        var n = opt.fills[i].counts[k];\n        if (n) parts.push(n + '× ' + SLUGS[m].label);\n      });\n      return parts.length ? parts.join(' + ') : 'No slugs needed';\n    }\n\n    var tiles = CORNERS.map(function (co, i) {\n      var n = win.fills[i].slugs;\n      var noHanger = !installed \u0026\u0026 !sc.mask[i];\n      return '\u003cdiv class=\"wh-result-tile' + (n \u003e 0 ? ' is-hot' : '') + '\"\u003e' +\n        '\u003cspan class=\"wh-result-tile__corner\"\u003e' + co.name + '\u003c\/span\u003e' +\n        '\u003cspan class=\"wh-result-tile__slugs\"\u003e' + n + ' \u003csmall\u003e' + (n === 1 ? 'slug' : 'slugs') + ' · +' + fmt(achievedAdd[i]) + 'g\u003c\/small\u003e\u003c\/span\u003e' +\n        '\u003cspan class=\"wh-result-tile__detail\"\u003e' + (noHanger ? 'No hanger needed here' : esc(fillLabel(win, i))) + '\u003c\/span\u003e' +\n        '\u003cspan class=\"wh-result-tile__detail\"\u003eCorner: ' + fmt(c[i]) + 'g → ' + fmt(newC[i]) + 'g\u003c\/span\u003e' +\n      '\u003c\/div\u003e';\n    });\n    var html = '\u003cdiv class=\"wh-result-grid\"\u003e' + tiles.join('') + '\u003c\/div\u003e';\n\n    var rows = '\u003ctr\u003e\u003cth\u003eResult\u003c\/th\u003e\u003cth\u003eValue\u003c\/th\u003e\u003c\/tr\u003e';\n    rows += '\u003ctr\u003e\u003ctd\u003eFront bias now\u003c\/td\u003e\u003ctd\u003e' + fmt(curFront * 100) + '%\u003c\/td\u003e\u003c\/tr\u003e';\n    rows += '\u003ctr\u003e\u003ctd\u003eFront bias with slugs fitted\u003c\/td\u003e\u003ctd\u003e' + fmt(newFront * 100) + '% (target ' + fmt(g * 100) + '%)\u003c\/td\u003e\u003c\/tr\u003e';\n    if (lr) {\n      rows += '\u003ctr\u003e\u003ctd\u003eLeft\/right offset after (front axle)\u003c\/td\u003e\u003ctd\u003e' + fmt(Math.abs(newC[0] - newC[1])) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n      rows += '\u003ctr\u003e\u003ctd\u003eLeft\/right offset after (rear axle)\u003c\/td\u003e\u003ctd\u003e' + fmt(Math.abs(newC[2] - newC[3])) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    }\n    rows += '\u003ctr\u003e\u003ctd\u003eRig weight after\u003c\/td\u003e\u003ctd\u003e' + fmt(newW) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    rows += '\u003ctr class=\"wh-table__total\"\u003e\u003ctd\u003eTotal slug weight added\u003c\/td\u003e\u003ctd\u003e+' + fmt(totalAdded) + 'g\u003c\/td\u003e\u003c\/tr\u003e';\n    html += '\u003ctable class=\"wh-table\" style=\"margin-top:14px;\"\u003e' + rows + '\u003c\/table\u003e';\n\n    body.innerHTML = html;\n\n    \/\/ Alternative options - rendered inside the What to buy box, tappable to switch\n    var altsHtml = '';\n    if (totalSlugs \u003e 0) {\n      var winSig = optionSignature(win);\n      var seen = {};\n      seen[winSig] = true;\n      var altBtns = [];\n      for (var oi = 0; oi \u003c options.length \u0026\u0026 altBtns.length \u003c 2; oi++) {\n        var o = options[oi];\n        var sig = optionSignature(o);\n        if (seen[sig] || o.totalSlugs === 0) continue;\n        seen[sig] = true;\n        var oFront = (c[0] + o.fills[0].w + c[1] + o.fills[1].w) \/ (W0 + o.totalW);\n        var name = o.usedMats.map(function (m) { return SLUGS[m].label; }).join(' + ');\n        altBtns.push('\u003cbutton type=\"button\" class=\"wh-buy__alt\" data-sig=\"' + sig + '\"\u003e' +\n          '\u003cspan class=\"wh-buy__alt-name\"\u003e' + esc(name) + ', ' + o.totalSlugs + ' slugs\u003c\/span\u003e' +\n          '\u003cspan class=\"wh-buy__alt-meta\"\u003e' + fmt(oFront * 100) + '% front · ' + money(o.plan.cost) + '\u003c\/span\u003e' +\n        '\u003c\/button\u003e');\n      }\n      if (altBtns.length) {\n        altsHtml = '\u003cdiv class=\"wh-buy__alts\"\u003e\u003cp class=\"wh-buy__alts-title\"\u003eOther options: tap to switch\u003c\/p\u003e' + altBtns.join('') + '\u003c\/div\u003e';\n      }\n    }\n\n    var noteHtml = '';\n    var rawTotal = raw[0] + raw[1] + raw[2] + raw[3];\n    if (rawTotal \u003c 150 || rawTotal \u003e 1000) {\n      noteHtml += '\u003cdiv class=\"wh-warn\"\u003eYour corners total ' + fmt(rawTotal) + 'g, which is unusual for an SCX24 (most sit between 250g and 600g all-up). ' +\n        'Double-check you\\'re entering \u003cstrong\u003egrams per corner\u003c\/strong\u003e, not ounces or the whole-rig weight.\u003c\/div\u003e';\n    }\n    var shortfall = [];\n    add.forEach(function (x, i) {\n      if (x - win.fills[i].w \u003e 1.0) shortfall.push(i);\n    });\n    if (shortfall.length) {\n      var names = shortfall.map(function (i) { return CORNERS[i].name; }).join(', ');\n      var needed = shortfall.map(function (i) { return fmt(add[i]) + 'g'; }).join(', ');\n      noteHtml += '\u003cdiv class=\"wh-warn\"\u003eA hanger maxes out at 6 slugs (18.7g of tungsten). ' + esc(names) +\n        ' ideally need' + (shortfall.length === 1 ? 's' : '') + ' more (' + needed +\n        '). Consider a brass hub (9g) on ' + (shortfall.length === 1 ? 'that corner' : 'those corners') + ' for extra grams.\u003c\/div\u003e';\n    }\n    if (totalSlugs === 0 \u0026\u0026 !shortfall.length) {\n      noteHtml += '\u003cdiv class=\"wh-note\" style=\"margin-bottom:12px;\"\u003e\u003cstrong\u003eYou\\'re already on target.\u003c\/strong\u003e Your current balance is within half a slug of your goal, so no slugs are needed.\u003c\/div\u003e';\n    }\n    var buyEl = document.getElementById('wh-bal-buy');\n    if (totalSlugs \u003e 0 || pairsNeeded \u003e 0) {\n      var foot = 'Cheapest combination that covers this setup' +\n        (installed ? '' : ' (hangers on ' + sc.where + ')') + '. Nothing extra.';\n      if (totalSlugs === 0) foot = 'You\\'re on target. These bare pairs get your Bottom Feeders fitted with no slugs needed.';\n      if (pairsNeeded === 1) foot += ' One pair is all this balance needs; you can always add a second pair later for more tuning range.';\n      if (win.plan.spare \u003e 0) foot += ' Leaves ' + win.plan.spare + ' spare slug' + (win.plan.spare === 1 ? '' : 's') + ' for future tuning.';\n      renderBuyBox(buyEl, win.plan, foot, altsHtml);\n    } else {\n      buyEl.innerHTML = '';\n    }\n\n    if (totalSlugs \u003e 0) {\n      noteHtml += '\u003cdiv class=\"wh-note\"\u003eRecommended: the cheapest option that still lands on your target. Options within about a slug of accuracy are ranked by price. ' +\n        '\u003cstrong\u003eTip:\u003c\/strong\u003e slugs in the hangers carry weight at the very bottom of the axle, so fill hanger holes before stepping up to heavier hubs.' +\n        (installed ? '' : ' Hanger hardware (+' + fmt(hwPerCorner) + 'g per corner) on ' + sc.where + ' was added to your entered weights before balancing.') +\n        '\u003c\/div\u003e';\n    }\n    notes.innerHTML = noteHtml;\n    syncHash();\n  }\n\n  \/* hardware-installed toggle *\/\n  document.getElementById('wh-hw-installed').addEventListener('change', function () {\n    document.getElementById('wh-hw-extra').classList.toggle('is-visible', !this.checked);\n    balInputChanged();\n  });\n  document.getElementById('wh-hw-hub').addEventListener('change', balInputChanged);\n  document.getElementById('wh-lr-toggle').addEventListener('change', balInputChanged);\n\n  \/* tap an alternative option in the What to buy box to switch to it *\/\n  document.getElementById('wh-bal-buy').addEventListener('click', function (e) {\n    var alt = e.target.closest('.wh-buy__alt');\n    if (!alt) return;\n    balOverride = alt.getAttribute('data-sig');\n    renderBalance();\n  });\n  document.getElementById('wh-adder-buy').addEventListener('click', function (e) {\n    var alt = e.target.closest('.wh-buy__alt');\n    if (!alt) return;\n    var v = adderAlts[alt.getAttribute('data-alt') | 0];\n    if (!v) return;\n    AXLES.forEach(function (ax) {\n      MATS.forEach(function (m) { adderState[ax.key].counts[m] = v[ax.key].counts[m]; });\n    });\n    refreshAdderInputs();\n    renderAdder();\n  });\n\n  \/* bias slider \u003c-\u003e number sync *\/\n  var biasSlider = document.getElementById('wh-bias-slider');\n  var biasNum = document.getElementById('wh-bias-num');\n  function syncBias(fromSlider) {\n    if (fromSlider) biasNum.value = biasSlider.value;\n    else {\n      var v = parseFloat(biasNum.value);\n      if (isFinite(v)) biasSlider.value = Math.max(40, Math.min(75, v));\n    }\n    var f = parseFloat(biasNum.value);\n    if (isFinite(f) \u0026\u0026 f \u003e 0 \u0026\u0026 f \u003c 100) {\n      document.getElementById('wh-bias-split').textContent = f + '% front \/ ' + (100 - f) + '% rear';\n    }\n    balInputChanged();\n  }\n  biasSlider.addEventListener('input', function () { syncBias(true); });\n  biasNum.addEventListener('input', function () { syncBias(false); });\n\n  \/* corner weight inputs *\/\n  ['wh-cw-fl', 'wh-cw-fr', 'wh-cw-rl', 'wh-cw-rr'].forEach(function (id) {\n    document.getElementById(id).addEventListener('input', balInputChanged);\n  });\n\n  \/* ================= SHAREABLE SETUP LINK ================= *\/\n  function currentState() {\n    return {\n      v: 1,\n      m: activeSection,\n      a: AXLES.map(function (ax) {\n        var s = adderState[ax.key];\n        return [s.on ? 1 : 0, s.hub, MATS.map(function (m) { return s.counts[m]; })];\n      }),\n      b: [\n        ['wh-cw-fl', 'wh-cw-fr', 'wh-cw-rl', 'wh-cw-rr'].map(function (id) { return document.getElementById(id).value; }),\n        document.getElementById('wh-bias-num').value,\n        document.getElementById('wh-lr-toggle').checked ? 1 : 0,\n        document.getElementById('wh-hw-installed').checked ? 1 : 0,\n        document.getElementById('wh-hw-hub').value\n      ]\n    };\n  }\n  function syncHash() {\n    try {\n      history.replaceState(null, '', location.pathname + location.search + '#wh=' + btoa(JSON.stringify(currentState())));\n    } catch (e) { \/* older browsers: sharing simply unavailable *\/ }\n  }\n  function restoreFromHash() {\n    try {\n      var mHash = location.hash.match(\/wh=([A-Za-z0-9+\/=]+)\/);\n      if (!mHash) return null;\n      var st = JSON.parse(atob(mHash[1]));\n      if (st.a) {\n        AXLES.forEach(function (ax, i) {\n          var row = st.a[i];\n          if (!row) return;\n          var s = adderState[ax.key];\n          s.on = !!row[0];\n          if (HUBS[row[1]]) s.hub = row[1];\n          var used = 0;\n          MATS.forEach(function (m, k) {\n            var n = Math.max(0, Math.min(6, (row[2] \u0026\u0026 row[2][k]) | 0));\n            n = Math.min(n, 6 - used);\n            s.counts[m] = n; used += n;\n          });\n        });\n      }\n      if (st.b) {\n        ['wh-cw-fl', 'wh-cw-fr', 'wh-cw-rl', 'wh-cw-rr'].forEach(function (id, i) {\n          if (st.b[0] \u0026\u0026 st.b[0][i] !== undefined) document.getElementById(id).value = st.b[0][i];\n        });\n        if (st.b[1]) {\n          document.getElementById('wh-bias-num').value = st.b[1];\n          document.getElementById('wh-bias-slider').value = Math.max(40, Math.min(75, parseFloat(st.b[1]) || 60));\n          var f = parseFloat(st.b[1]);\n          if (isFinite(f) \u0026\u0026 f \u003e 0 \u0026\u0026 f \u003c 100) {\n            document.getElementById('wh-bias-split').textContent = f + '% front \/ ' + (100 - f) + '% rear';\n          }\n        }\n        document.getElementById('wh-lr-toggle').checked = !!st.b[2];\n        var inst = !!st.b[3];\n        document.getElementById('wh-hw-installed').checked = inst;\n        document.getElementById('wh-hw-extra').classList.toggle('is-visible', !inst);\n        if (HUBS[st.b[4]]) document.getElementById('wh-hw-hub').value = st.b[4];\n      }\n      return st.m || null;\n    } catch (e) { return null; }\n  }\n\n  \/* add-to-cart buttons are re-rendered inside the buy boxes - delegate from the page root *\/\n  document.querySelector('.wh-page').addEventListener('click', function (e) {\n    var cartBtn = e.target.closest('.wh-cart');\n    if (cartBtn) {\n      var items = cartBtn.getAttribute('data-cart').split(',').map(function (s) {\n        var p = s.split(':');\n        return { vid: p[0], qty: parseInt(p[1], 10) || 1 };\n      });\n      cartBtn.disabled = true;\n      cartBtn.textContent = 'Adding…';\n      addSetupToCart(items);\n      return;\n    }\n    var btn = e.target.closest('.wh-share');\n    if (!btn) return;\n    syncHash();\n    var done = function () {\n      btn.textContent = 'Link copied!';\n      setTimeout(function () { btn.textContent = 'Copy link to this setup'; }, 1600);\n    };\n    if (navigator.clipboard \u0026\u0026 navigator.clipboard.writeText) {\n      navigator.clipboard.writeText(location.href).then(done, done);\n    } else { done(); }\n  });\n\n  \/* init *\/\n  var startSection = restoreFromHash();\n  buildAdderGrid();\n  renderAdder();\n  renderBalance();\n  loadLivePrices(function () { renderAdder(); renderBalance(); });\n  if (startSection) {\n    var startBtn = document.getElementById('wh-path-' + startSection);\n    if (startBtn) startBtn.click();\n  }\n})();\n\u003c\/script\u003e","brand":"Mofo Rc","offers":[{"title":"X15 Axles (1 Pair) \/ No Weights","offer_id":46273818067001,"sku":"934","price":27.99,"currency_code":"USD","in_stock":true},{"title":"X15 Axles (1 Pair) \/ Aluminum","offer_id":46260716273721,"sku":"934-AL","price":34.99,"currency_code":"USD","in_stock":true},{"title":"X15 Axles (1 Pair) \/ Stainless Steel","offer_id":46260716306489,"sku":"934-SS","price":39.99,"currency_code":"USD","in_stock":true},{"title":"X15 Axles (1 Pair) \/ Brass","offer_id":46260716339257,"sku":"934-BR","price":44.99,"currency_code":"USD","in_stock":true},{"title":"X15 Axles (1 Pair) \/ Tungsten","offer_id":46260716372025,"sku":"934-TN","price":79.99,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0574\/3922\/3865\/files\/mofo_rc__scx24_bottom_feeders.jpg?v=1785866772","url":"https:\/\/moforc.com\/products\/bottom-feeders-super-weight-hangers-for-axial%c2%ae-scx24%e2%84%a2","provider":"Mofo Rc","version":"1.0","type":"link"}