{
 "_canon": "W2_CATALOG_TREE_V1",
 "version": 3,
 "ui": {
  "categories": false,
  "viewArrows": false,
  "_viewArrowsNote": "The bundle renders four views (front/right/back/left) and pages them with two bare divs holding a chevron — no class, inline style only, so CSS cannot reach them. There is no swipe handler anywhere, so hiding the chevrons leaves the view pinned to front, which is the only one that carries a print. Flip this to true to bring them back."
 },
 "note": "Single source of truth for the ShopLink category tree. ui.categories=false collapses the header to the Wear blanks only: no category level, no back arrow. Flip it to true to bring the catalog back — nothing is deleted.",
 "categories": [
  {
   "id": "wear",
   "label": "Wear",
   "model": "signal",
   "live": true,
   "chips": {
    "type": "word"
   },
   "shoplink": "print",
   "gbt_rate": "legacy",
   "children": [
    {
     "id": "tshirt",
     "label": "Premium T-Shirt",
     "priceUsd": 19,
     "status": "live",
     "presets": [
      "oneLineCenter",
      "twoLineCenter",
      "leftChestLogo",
      "leftChestMascot",
      "centerMascot",
      "freeCenter",
      "freeLeftChest"
     ],
     "qr": true,
     "note": "Live product. The colours in `staged` have artwork ready but are NOT in the bundle (vn / mn), so they can be previewed and never bought: the CTA is blocked while one is selected. They become real purchases only after the bundle pack.",
     "products": [],
     "_w2fam": "W2FAM3",
     "shelf": true,
     "priceVnd": 590000
    },
    {
     "id": "polo",
     "label": "Polo",
     "priceUsd": 29,
     "status": "planned",
     "presets": [
      "leftChestLogo",
      "leftChestMascot"
     ],
     "qr": true,
     "note": "W2_POLO_PREVIEW_260807: six blanks, one shoot, cut into the T-SHIRT's coordinate frame with the tee's own transform — collar, shoulder and hem keep the relationship the camera recorded, so switching the tab does not jump. Left chest ONLY: the placket owns the middle, so no centred print and no `center` anchor in w2teegeo. status stays `planned` ON PURPOSE — that is what keeps lockCta() firing. Selling polo needs three things this repo does not own: back2 price_usd() must know `polo`, back2 must carry polo PRINT_SIZE_PRESETS, and Merchize must have a polo SKU. Only then add polo to BUYABLE in w2price.js. pendingNote deleted, not blanked: with mockups present the nomock path never runs, so the copy had no reader.",
     "products": [],
     "positions": [
      "leftChest"
     ],
     "colors": [
      "white",
      "grey",
      "navy",
      "black",
      "green",
      "cream"
     ],
     "inherits": "tshirt",
     "_w2polo": "W2POLO1",
     "_w2fam": "W2FAM3",
     "shelf": false,
     "mockups": {
      "white": "polo_white_front.png.webp",
      "grey": "polo_grey_front.png.webp",
      "navy": "polo_navy_front.png.webp",
      "black": "polo_black_front.png.webp",
      "green": "polo_green_front.png.webp",
      "cream": "polo_cream_front.png.webp"
     },
     "_w2mark": "W2_POLO_PREVIEW_260807"
    },
    {
     "id": "longsleeve",
     "label": "Long Sleeve",
     "priceUsd": 24,
     "status": "planned",
     "presets": [
      "oneLineCenter",
      "twoLineCenter",
      "leftChestLogo",
      "leftChestMascot",
      "centerMascot",
      "freeCenter",
      "freeLeftChest"
     ],
     "qr": true,
     "note": "W2LS1: rebuilt on the shared 1000x1188 canvas from the owner's renders. Native tone, shape-symmetric alpha, silhouette identical across colours. Print anchors and the heart band are measured and live in w2teegeo.js / w2railgeo.js, not in CSS percentages.",
     "products": [],
     "colors": [
      "white",
      "grey",
      "navy",
      "black"
     ],
     "inherits": "tshirt",
     "_w2ls": "W2LS1",
     "_w2fam": "W2FAM3",
     "views": [
      "front"
     ],
     "shelf": false
    },
    {
     "id": "sweatshirt",
     "label": "Sweatshirt",
     "priceUsd": 34,
     "status": "planned",
     "presets": [
      "oneLineCenter",
      "twoLineCenter",
      "leftChestLogo",
      "leftChestMascot",
      "centerMascot",
      "freeCenter",
      "freeLeftChest"
     ],
     "qr": true,
     "note": "W2SW1: rebuilt on the shared 1000x1188 canvas from the owner's renders. Native tone, shape-symmetric alpha, silhouette identical across colours. Anchors measured, they live in w2teegeo.js / w2railgeo.js.",
     "products": [],
     "colors": [
      "white",
      "grey",
      "navy",
      "black"
     ],
     "inherits": "hoodie",
     "_w2sweat": "W2SWEAT1",
     "_w2fam": "W2FAM3",
     "views": [
      "front"
     ],
     "shelf": false
    },
    {
     "id": "hoodie",
     "label": "Hoodie",
     "priceUsd": 39,
     "status": "live",
     "presets": [
      "oneLineCenter",
      "twoLineCenter",
      "leftChestLogo",
      "leftChestMascot",
      "centerMascot",
      "freeCenter",
      "freeLeftChest"
     ],
     "qr": true,
     "note": "Live product, artwork fully replaced 2026-07-29 with the oversized cut. All five colours share one geometry: shoulder 250, width 751, axis on the canvas centre 444. White, black, grey and navy are real bundle colours and replace the live files. Cream is staged only — mn.hoodie has no cream, so the CTA is blocked while it is selected.",
     "products": [],
     "_w2fam": "W2FAM3",
     "shelf": false
    },
    {
     "id": "cap",
     "label": "Premium Cap",
     "priceUsd": 24,
     "status": "live",
     "presets": [
      "capLogo",
      "capMascot",
      "capFreeText"
     ],
     "qr": false,
     "note": "W2HEAD2: geometry from headwearCanon. Only crownTop / crownH / brimLine are family-specific; canvas, axisX, crownW and the print anchor are shared and must not be restated here.",
     "products": [],
     "colors": [
      "white",
      "grey",
      "navy",
      "black",
      "green",
      "cream"
     ],
     "views": [
      "front"
     ],
     "mockups": {
      "white": "cap_white_front.png",
      "grey": "cap_grey_front.png",
      "navy": "cap_navy_front.png",
      "black": "cap_black_front.png"
     },
     "geometry": {
      "$ref": "headwearCanon",
      "crownTop": 148,
      "crownH": 547,
      "brimLine": 695
     },
     "shelf": true,
     "priceVnd": 590000
    },
    {
     "id": "snapback",
     "label": "Snapback",
     "priceUsd": 26,
     "status": "planned",
     "presets": [
      "capLogo",
      "capMascot",
      "capFreeText"
     ],
     "qr": false,
     "note": "W2HEAD2: geometry from headwearCanon. Only crownTop / crownH / brimLine are family-specific; canvas, axisX, crownW and the print anchor are shared and must not be restated here.",
     "products": [],
     "colors": [
      "white",
      "grey",
      "navy",
      "black"
     ],
     "views": [
      "front"
     ],
     "geometry": {
      "$ref": "headwearCanon",
      "crownTop": 162,
      "crownH": 546,
      "brimLine": 708
     },
     "inherits": "cap",
     "shelf": false
    },
    {
     "id": "bucket",
     "label": "Bucket",
     "priceUsd": 24,
     "status": "planned",
     "presets": [
      "capLogo",
      "capMascot",
      "capFreeText"
     ],
     "qr": false,
     "note": "W2HEAD2: geometry from headwearCanon. Only crownTop / crownH / brimLine are family-specific; canvas, axisX, crownW and the print anchor are shared and must not be restated here.",
     "products": [],
     "colors": [
      "white",
      "grey",
      "navy",
      "black"
     ],
     "views": [
      "front"
     ],
     "geometry": {
      "$ref": "headwearCanon",
      "crownTop": 213,
      "crownH": 463,
      "brimLine": 676
     },
     "inherits": "cap",
     "shelf": false
    },
    {
     "id": "bag",
     "label": "Bag",
     "priceUsd": 24,
     "status": "live",
     "presets": [
      "bagLogo",
      "bagMascot",
      "bagFreeText"
     ],
     "qr": true,
     "note": "Largest print area of the range.",
     "products": [],
     "shelf": false
    }
   ],
   "_w2mark": "P46_SHELF_FOUR_V1"
  },
  {
   "id": "coffee",
   "label": "Coffee",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "sticker",
   "gbt_rate": null,
   "children": [
    {
     "id": "beans",
     "label": "Whole Bean",
     "products": [
      {
       "id": "cf-dalat",
       "name": "Da Lat Arabica",
       "priceUsd": 14,
       "desc": "High-altitude arabica from Lam Dong. Soft acidity, cocoa finish. Roasted to order.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true,
       "photo": "cf-dalat.jpg"
      },
      {
       "id": "cf-bmt",
       "name": "Buon Ma Thuot Robusta",
       "priceUsd": 11,
       "desc": "The classic Vietnamese robusta. Heavy body, low acidity, built for phin brewing.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true
      },
      {
       "id": "cf-peaberry",
       "name": "Peaberry Reserve",
       "priceUsd": 18,
       "desc": "Single-bean selection, hand-sorted. Small lot, sweeter and denser than standard grade.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "ground",
     "label": "Ground",
     "products": [
      {
       "id": "cf-phin",
       "name": "Phin Blend",
       "priceUsd": 10,
       "desc": "Coarse grind tuned for the traditional drip filter. Robusta-led with an arabica lift.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true
      },
      {
       "id": "cf-espresso",
       "name": "Espresso Dark",
       "priceUsd": 12,
       "desc": "Dark roast, fine grind. Holds up against condensed milk without going bitter.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "instant",
     "label": "Instant",
     "products": [
      {
       "id": "cf-3in1",
       "name": "3-in-1 Sticks",
       "priceUsd": 9,
       "desc": "Coffee, creamer and sugar in one sachet. Twenty sticks per box.",
       "variants": [
        "20 sticks"
       ],
       "stub": true
      },
      {
       "id": "cf-coconut",
       "name": "Coconut Latte Mix",
       "priceUsd": 12,
       "desc": "Instant base with real coconut milk powder. Just add hot water.",
       "variants": [
        "10 sachets"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "gear",
     "label": "Brew Gear",
     "products": [
      {
       "id": "cf-filter",
       "name": "Classic Phin Filter",
       "priceUsd": 8,
       "desc": "Stainless steel drip filter, single cup. The whole Vietnamese coffee ritual in one object.",
       "variants": [],
       "stub": true
      },
      {
       "id": "cf-set",
       "name": "Phin + Mug Set",
       "priceUsd": 19,
       "desc": "Filter, glass mug and a starter bag of ground coffee, boxed as a gift.",
       "variants": [],
       "stub": true
      }
     ]
    }
   ]
  },
  {
   "id": "tea",
   "label": "Tea",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "sticker",
   "gbt_rate": null,
   "children": [
    {
     "id": "green",
     "label": "Green & Oolong",
     "products": [
      {
       "id": "te-thainguyen",
       "name": "Thai Nguyen Green",
       "priceUsd": 12,
       "desc": "Vietnam's benchmark green tea. Grassy, slightly sweet, forgiving of hot water.",
       "variants": [
        "100 g",
        "200 g"
       ],
       "stub": true
      },
      {
       "id": "te-mocchau",
       "name": "Moc Chau Oolong",
       "priceUsd": 16,
       "desc": "Lightly oxidised highland oolong. Floral on the first steep, creamy on the third.",
       "variants": [
        "100 g",
        "200 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "floral",
     "label": "Lotus & Floral",
     "products": [
      {
       "id": "te-lotus",
       "name": "Hue Lotus Tea",
       "priceUsd": 22,
       "desc": "Green tea scented inside lotus flowers overnight. Labour-heavy, unmistakable.",
       "variants": [
        "50 g"
       ],
       "stub": true
      },
      {
       "id": "te-jasmine",
       "name": "Jasmine Silver Bud",
       "priceUsd": 15,
       "desc": "Silver-tip buds layered with fresh jasmine over several nights.",
       "variants": [
        "100 g",
        "200 g"
       ],
       "stub": true,
       "photo": "te-jasmine.jpg"
      }
     ]
    },
    {
     "id": "herbal",
     "label": "Herbal",
     "products": [
      {
       "id": "te-moringa",
       "name": "Moringa Leaf",
       "priceUsd": 9,
       "desc": "Dried moringa, caffeine free. Earthy and mineral, drinks well iced.",
       "variants": [
        "100 g",
        "200 g"
       ],
       "stub": true
      },
      {
       "id": "te-artichoke",
       "name": "Artichoke Tea",
       "priceUsd": 10,
       "desc": "Da Lat artichoke, a Vietnamese household staple. Mild, faintly sweet.",
       "variants": [
        "60 g"
       ],
       "stub": true
      },
      {
       "id": "te-ginseng",
       "name": "Ginseng Root Slices",
       "priceUsd": 24,
       "desc": "Sliced Ngoc Linh-style root for brewing. Sold as food, not as a supplement.",
       "variants": [
        "50 g"
       ],
       "stub": true
      }
     ]
    }
   ]
  },
  {
   "id": "spice",
   "label": "Spice",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "sticker",
   "gbt_rate": null,
   "children": [
    {
     "id": "pepper",
     "label": "Pepper",
     "products": [
      {
       "id": "sp-black",
       "name": "Phu Quoc Black Pepper",
       "priceUsd": 9,
       "desc": "Island-grown, sun-dried whole peppercorns. Sharper and more floral than commodity pepper.",
       "variants": [
        "100 g",
        "200 g"
       ],
       "stub": true
      },
      {
       "id": "sp-red",
       "name": "Red Peppercorn",
       "priceUsd": 12,
       "desc": "Vine-ripened before drying. Fruitier, milder heat, rarer harvest.",
       "variants": [
        "100 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "bark",
     "label": "Bark & Seed",
     "products": [
      {
       "id": "sp-cassia",
       "name": "Yen Bai Cassia Sticks",
       "priceUsd": 8,
       "desc": "Vietnamese cassia, the highest oil content of any cinnamon. Sold in rolled quills.",
       "variants": [
        "80 g"
       ],
       "stub": true
      },
      {
       "id": "sp-anise",
       "name": "Star Anise",
       "priceUsd": 7,
       "desc": "Whole stars from Lang Son. The backbone of pho broth.",
       "variants": [
        "60 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "salt",
     "label": "Salt & Chili",
     "products": [
      {
       "id": "sp-shrimp",
       "name": "Tay Ninh Shrimp Salt",
       "priceUsd": 6,
       "desc": "Salt, dried shrimp and chili, ground together. Eaten with fruit, not just savoury food.",
       "variants": [
        "90 g"
       ],
       "stub": true
      },
      {
       "id": "sp-chili",
       "name": "Bird's Eye Chili Flakes",
       "priceUsd": 6,
       "desc": "Sun-dried and crushed. Hot, with a clean finish.",
       "variants": [
        "50 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "sauce",
     "label": "Sauce",
     "products": [
      {
       "id": "sp-fishsauce",
       "name": "Phu Quoc Fish Sauce",
       "priceUsd": 14,
       "desc": "Geographically protected, aged in wooden barrels. Glass bottle, sealed for transit.",
       "variants": [
        "250 ml"
       ],
       "stub": true
      }
     ]
    }
   ]
  },
  {
   "id": "snack",
   "label": "Snack",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "sticker",
   "gbt_rate": null,
   "children": [
    {
     "id": "nuts",
     "label": "Nuts",
     "products": [
      {
       "id": "sn-cashew",
       "name": "Binh Phuoc Cashews",
       "priceUsd": 13,
       "desc": "Raw-roasted whole cashews from Vietnam's main growing province.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true
      },
      {
       "id": "sn-salted",
       "name": "Salted Cashews",
       "priceUsd": 13,
       "desc": "Roasted with sea salt only. Nothing else in the bag.",
       "variants": [
        "250 g",
        "500 g"
       ],
       "stub": true
      },
      {
       "id": "sn-honey",
       "name": "Honey Cashews",
       "priceUsd": 14,
       "desc": "Glazed with wildflower honey, baked until the shell sets.",
       "variants": [
        "200 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "dried",
     "label": "Dried Fruit",
     "products": [
      {
       "id": "sn-mango",
       "name": "Dried Mango",
       "priceUsd": 8,
       "desc": "Soft-dried Cat Hoa Loc mango. No added sugar.",
       "variants": [
        "200 g"
       ],
       "stub": true
      },
      {
       "id": "sn-jackfruit",
       "name": "Jackfruit Chips",
       "priceUsd": 9,
       "desc": "Vacuum-fried, keeps the fruit colour and snaps cleanly.",
       "variants": [
        "150 g"
       ],
       "stub": true
      },
      {
       "id": "sn-coconut",
       "name": "Coconut Chips",
       "priceUsd": 7,
       "desc": "Toasted Ben Tre coconut ribbons, lightly salted.",
       "variants": [
        "150 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "sweets",
     "label": "Sweets",
     "products": [
      {
       "id": "sn-sesame",
       "name": "Hue Sesame Candy",
       "priceUsd": 6,
       "desc": "Thin brittle of sesame and malt sugar, cut into cards.",
       "variants": [
        "200 g"
       ],
       "stub": true
      }
     ]
    }
   ]
  },
  {
   "id": "bamboo",
   "label": "Bamboo",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "engrave",
   "gbt_rate": null,
   "children": [
    {
     "id": "drink",
     "label": "Drinkware",
     "products": [
      {
       "id": "bm-straw",
       "name": "Straw Set of 6",
       "priceUsd": 9,
       "desc": "Reusable bamboo straws with a cleaning brush, in a cotton pouch.",
       "variants": [],
       "stub": true
      },
      {
       "id": "bm-tumbler",
       "name": "Bamboo Tumbler",
       "priceUsd": 16,
       "desc": "Single-wall tumbler with a sealed lid. ShopLink engraved on the base.",
       "variants": [],
       "stub": true
      }
     ]
    },
    {
     "id": "table",
     "label": "Table",
     "products": [
      {
       "id": "bm-cutlery",
       "name": "Cutlery Set",
       "priceUsd": 12,
       "desc": "Fork, spoon, knife and chopsticks in a roll-up case.",
       "variants": [],
       "stub": true
      },
      {
       "id": "bm-coaster",
       "name": "Coaster Set of 4",
       "priceUsd": 11,
       "desc": "Cross-cut bamboo discs, oiled. Engraving sits on the underside.",
       "variants": [],
       "stub": true
      },
      {
       "id": "bm-bowl",
       "name": "Serving Bowl",
       "priceUsd": 18,
       "desc": "Laminated bamboo, food-safe finish, dishwasher tolerant.",
       "variants": [
        "M",
        "L"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "daily",
     "label": "Daily",
     "products": [
      {
       "id": "bm-brush",
       "name": "Toothbrush Pair",
       "priceUsd": 7,
       "desc": "Bamboo handles, soft bristles, compostable packaging.",
       "variants": [],
       "stub": true
      },
      {
       "id": "bm-comb",
       "name": "Comb",
       "priceUsd": 6,
       "desc": "Wide-tooth bamboo comb, hand-sanded.",
       "variants": [],
       "stub": true
      }
     ]
    },
    {
     "id": "light",
     "label": "Light",
     "products": [
      {
       "id": "bm-lamp",
       "name": "Woven Table Lamp",
       "priceUsd": 34,
       "desc": "Hand-woven shade on a bamboo base. E27 fitting, bulb not included.",
       "variants": [],
       "stub": true
      }
     ]
    }
   ]
  },
  {
   "id": "craft",
   "label": "Craft",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "sticker",
   "gbt_rate": null,
   "children": [
    {
     "id": "ceramic",
     "label": "Ceramics",
     "products": [
      {
       "id": "cr-teaset",
       "name": "Bat Trang Tea Set",
       "priceUsd": 46,
       "desc": "Pot and four cups from the Bat Trang village kilns. Hand-glazed, small variations expected.",
       "variants": [],
       "stub": true
      },
      {
       "id": "cr-bowl",
       "name": "Rice-Pattern Bowl Pair",
       "priceUsd": 22,
       "desc": "Translucent rice-grain porcelain, a Bat Trang signature.",
       "variants": [],
       "stub": true
      }
     ]
    },
    {
     "id": "lacquer",
     "label": "Lacquer",
     "products": [
      {
       "id": "cr-tray",
       "name": "Lacquer Tray",
       "priceUsd": 28,
       "desc": "Layered lacquer over wood, hand-polished. Eggshell inlay on the rim.",
       "variants": [],
       "stub": true
      },
      {
       "id": "cr-coaster",
       "name": "Lacquer Coaster Set",
       "priceUsd": 19,
       "desc": "Four coasters, mother-of-pearl detail, boxed.",
       "variants": [],
       "stub": true
      }
     ]
    },
    {
     "id": "textile",
     "label": "Textile",
     "products": [
      {
       "id": "cr-scarf",
       "name": "Hoi An Silk Scarf",
       "priceUsd": 32,
       "desc": "Mulberry silk, hand-hemmed. Woven and dyed in Quang Nam.",
       "variants": [],
       "stub": true
      },
      {
       "id": "cr-pouch",
       "name": "Embroidered Pouch",
       "priceUsd": 14,
       "desc": "Hand-embroidered cotton pouch from northern highland workshops.",
       "variants": [],
       "stub": true
      }
     ]
    },
    {
     "id": "incense",
     "label": "Incense",
     "products": [
      {
       "id": "cr-incense",
       "name": "Quang Phu Cau Incense",
       "priceUsd": 8,
       "desc": "Sticks from the incense village outside Hanoi. Agarwood blend, no synthetic fragrance.",
       "variants": [
        "40 sticks"
       ],
       "stub": true
      },
      {
       "id": "cr-holder",
       "name": "Ceramic Incense Holder",
       "priceUsd": 12,
       "desc": "Glazed disc holder, catches ash cleanly.",
       "variants": [],
       "stub": true
      }
     ]
    }
   ]
  },
  {
   "id": "beauty",
   "label": "Beauty",
   "model": "catalog",
   "live": false,
   "chips": {
    "type": "item"
   },
   "shoplink": "sticker",
   "gbt_rate": null,
   "children": [
    {
     "id": "soap",
     "label": "Soap & Bath",
     "products": [
      {
       "id": "bt-coffee",
       "name": "Coffee Scrub Soap",
       "priceUsd": 8,
       "desc": "Cold-process bar with spent coffee grounds. Anhydrous, long shelf life.",
       "variants": [
        "100 g"
       ],
       "stub": true
      },
      {
       "id": "bt-lemongrass",
       "name": "Lemongrass Soap",
       "priceUsd": 7,
       "desc": "Lemongrass and coconut oil bar, cured four weeks.",
       "variants": [
        "100 g"
       ],
       "stub": true
      },
      {
       "id": "bt-salt",
       "name": "Bath Salt Jar",
       "priceUsd": 12,
       "desc": "Sea salt with dried lemongrass and star anise. Glass jar.",
       "variants": [
        "300 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "balm",
     "label": "Balm & Butter",
     "products": [
      {
       "id": "bt-butter",
       "name": "Shea Body Butter Bar",
       "priceUsd": 14,
       "desc": "Solid butter bar, melts on skin. No water, so no preservative system needed.",
       "variants": [
        "60 g"
       ],
       "stub": true
      },
      {
       "id": "bt-lip",
       "name": "Coconut Lip Balm",
       "priceUsd": 6,
       "desc": "Coconut oil and beeswax in an aluminium tin.",
       "variants": [
        "10 g"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "oil",
     "label": "Oil",
     "products": [
      {
       "id": "bt-lemongrass-oil",
       "name": "Lemongrass Essential Oil",
       "priceUsd": 11,
       "desc": "Steam-distilled from Vietnamese lemongrass. Amber dropper bottle.",
       "variants": [
        "10 ml"
       ],
       "stub": true
      },
      {
       "id": "bt-anise-oil",
       "name": "Star Anise Oil",
       "priceUsd": 13,
       "desc": "Distilled from Lang Son star anise. Warm, sweet, long-lasting.",
       "variants": [
        "10 ml"
       ],
       "stub": true
      }
     ]
    },
    {
     "id": "set",
     "label": "Set",
     "products": [
      {
       "id": "bt-travel",
       "name": "Travel Solid Set",
       "priceUsd": 29,
       "desc": "Soap, butter bar and lip balm in a bamboo tin. Nothing liquid, nothing to leak.",
       "variants": [],
       "stub": true
      }
     ]
    }
   ]
  }
 ],
 "palette": {
  "navy": {
   "hex": "#0D1B4B",
   "name": "Navy",
   "dark": true,
   "border": false,
   "whiteHeart": true,
   "note": "Already in the bundle palette for tshirt/hoodie, so garments get the swatch from React. Cap-family has no navy, so it is injected like cream."
  },
  "grey": {
   "hex": "#888888",
   "name": "Grey",
   "dark": true,
   "border": false,
   "whiteHeart": true,
   "note": "In the bundle palette for tshirt/hoodie, so garments get the swatch from React; the cap family has none, so it is injected."
  },
  "white": {
   "hex": "#FFFFFF",
   "name": "White",
   "dark": false,
   "border": true,
   "whiteHeart": false
  },
  "black": {
   "hex": "#111111",
   "name": "Black",
   "dark": true,
   "border": false,
   "whiteHeart": true
  },
  "green": {
   "hex": "#18352E",
   "name": "Forest",
   "dark": true,
   "border": false,
   "whiteHeart": true
  },
  "cream": {
   "hex": "#D9D1BE",
   "name": "Cream",
   "dark": false,
   "border": false,
   "whiteHeart": false
  },
  "_w2mark": "W2_POLO_COLOUR_260807",
  "_note": "W2_POLO_COLOUR_260807: the full swatch table. React renders each swatch with its hex as an inline background and exposes the key nowhere in the DOM, so this is the only place w2cat can resolve a selected swatch back to a colour key. Keep it in step with the bundle's own colour table; nearHex() tolerates a sum-of-channels difference of 40."
 },
 "headwearCanon": {
  "_mark": "W2CLEAN3",
  "canvas": {
   "w": 1180,
   "h": 1133
  },
  "axisX": 590.0,
  "crownW": 820,
  "print": {
   "cx": 590,
   "cy": 500
  },
  "colors": [
   "white",
   "grey",
   "navy",
   "black"
  ],
  "views": [
   "front"
  ],
  "note": "Four colours, full stop: white / grey / navy / black. The white blank IS the warm off-white of the reference renders — there is no separate cream and no pure-white synthetic. Every blank is mirror-exact about axisX = canvas.w/2 (verified byte for byte), the crown face is crownW px wide in all three families, and print.cx/cy is the same point in all three, so one preset lands identically and the logo does not move when the product is switched. Runtime placement is measured by w2headgeo.js. Front only. NOTE: the blanks are served behind TWO cache tokens — the bundle's and w2cat.js MVER — and both must be bumped by any pack that replaces a blank.",
  "shadow": "Generated from the object's own footprint. The reference halo is removed and the edge is rebuilt from the opaque core.",
  "inherits": "A planned blank must name the React product whose constructor it borrows (w2cat.js targetKind -> forwardToReact). This is a binding, NOT geometry: without it React is never pinned and the blank inherits the print sizes, marginTop and data-product of whatever product happened to be selected before it. Do not delete it as a duplicate."
 }
}