Single-use to

reusable, decoded.

An operational map of packaging replacements under EU Regulation 2025/40. Filter by deadline, PPWR status, CapEx band, and complexity to find which transitions to prioritise.

<div id="rotion-navigator-root"></div>

<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=JetBrains+Mono:wght@400;500;600&family=Inter:wght@300;400;500;600;700&display=swap');

#rotion-navigator-root {
  --bg: #f5f2ec;
  --fg: #1a1a1a;
  --accent: #d4ff3a;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-display: 'Fraunces', Georgia, serif;
  --font-mono: 'JetBrains Mono', 'Courier New', monospace;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  width: 100%;
  box-sizing: border-box;
}

#rotion-navigator-root *,
#rotion-navigator-root *::before,
#rotion-navigator-root *::after {
  box-sizing: border-box;
}

.rn-container { max-width: 1600px; margin: 0 auto; padding: 0 32px; }

.rn-horizon-bar {
  background: linear-gradient(to bottom, rgba(26,26,26,0.02), transparent);
  padding: 24px 0;
}
.rn-horizon-inner {
  display: flex; align-items: center; flex-wrap: wrap; gap: 16px;
}
.rn-label {
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(26,26,26,0.5);
}
.rn-label-medium { color: rgba(26,26,26,0.6); }

.rn-segmented {
  display: inline-flex; align-items: center;
  background: rgba(26,26,26,0.05); padding: 4px;
}
.rn-segmented button {
  padding: 8px 20px; font-size: 12px; font-weight: 500;
  background: transparent; color: rgba(26,26,26,0.7);
  border: none; cursor: pointer; font-family: var(--font-body);
  transition: all 0.15s;
}
.rn-segmented button.active {
  background: var(--fg); color: var(--accent);
}

.rn-horizon-caption {
  font-size: 12px; color: rgba(26,26,26,0.6);
  font-style: italic; font-family: var(--font-display);
}

.rn-controls {
  border-top: 1px solid rgba(26,26,26,0.2);
  border-bottom: 1px solid rgba(26,26,26,0.2);
  padding: 20px 0;
}
.rn-controls-inner {
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
}

.rn-search-wrapper {
  position: relative; flex: 1; min-width: 260px; max-width: 380px;
}
.rn-search-wrapper svg.search-icon {
  position: absolute; left: 12px; top: 50%;
  transform: translateY(-50%); color: rgba(26,26,26,0.4);
  pointer-events: none;
}
.rn-search-wrapper input {
  width: 100%; background: transparent;
  border: 1px solid rgba(26,26,26,0.3);
  padding: 10px 40px 10px 40px; font-size: 14px;
  color: var(--fg); font-family: var(--font-body);
}
.rn-search-wrapper input:focus { outline: none; border-color: var(--fg); }
.rn-search-clear {
  position: absolute; right: 12px; top: 50%;
  transform: translateY(-50%); background: transparent; border: none;
  cursor: pointer; color: rgba(26,26,26,0.4);
  font-size: 18px; line-height: 1; padding: 0;
}
.rn-search-clear:hover { color: var(--fg); }

.rn-filter-group { display: flex; align-items: center; gap: 8px; }
.rn-filter-btn {
  padding: 6px 12px; font-size: 12px; font-weight: 500;
  background: transparent; color: var(--fg); border: none;
  cursor: pointer; font-family: var(--font-body); transition: all 0.15s;
}
.rn-filter-btn:hover:not(.active) { background: rgba(26,26,26,0.05); }
.rn-filter-btn.active { background: var(--fg); color: var(--accent); }

.rn-count {
  margin-left: auto; font-family: var(--font-mono);
  font-size: 11px; color: rgba(26,26,26,0.6);
}
.rn-count-total { opacity: 0.5; }

.rn-table-wrap { padding: 32px 0; overflow-x: auto; }
.rn-table { width: 100%; border-collapse: collapse; min-width: 900px; }
.rn-table thead tr { border-bottom: 2px solid var(--fg); }
.rn-table th {
  text-align: left; padding: 12px 12px 12px 0;
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(26,26,26,0.5); font-weight: 400;
}
.rn-sort-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: none; padding: 0;
  cursor: pointer; font-family: inherit; font-size: inherit;
  letter-spacing: inherit; text-transform: inherit;
  color: rgba(26,26,26,0.5); font-weight: 400;
}
.rn-sort-btn.active { color: var(--fg); font-weight: 600; }
.rn-sort-arrow { font-size: 8px; }

.rn-table tbody tr.rn-data-row {
  border-bottom: 1px solid rgba(26,26,26,0.1);
  cursor: pointer; transition: background 0.15s;
}
.rn-data-row:hover { background: rgba(212,255,58,0.08); }
.rn-data-row.expanded { background: rgba(26,26,26,0.05); }
.rn-table td {
  padding: 16px 12px 16px 0; vertical-align: top;
}

.rn-row-num-wrap { display: flex; align-items: center; gap: 8px; }
.rn-row-num { font-family: var(--font-mono); font-size: 10px; color: rgba(26,26,26,0.4); }
.rn-chevron { font-size: 12px; color: rgba(26,26,26,0.4); }

.rn-category {
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: rgba(26,26,26,0.6); line-height: 1.3; max-width: 130px;
}
.rn-single-use {
  font-size: 13px; line-height: 1.4;
  color: rgba(26,26,26,0.7); max-width: 320px;
  text-decoration: line-through;
  text-decoration-color: rgba(26,26,26,0.3);
}
.rn-reusable {
  font-size: 13px; line-height: 1.4; font-weight: 500; max-width: 320px;
}

.rn-status-badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; font-size: 11px; font-weight: 500;
  letter-spacing: 0.03em; text-transform: uppercase;
  font-family: var(--font-body); white-space: nowrap;
}
.rn-status-badge.mandatory { background: var(--fg); color: var(--accent); }
.rn-status-badge.recommended {
  background: transparent; color: var(--fg); border: 1px solid var(--fg);
}
.rn-status-dot { width: 6px; height: 6px; border-radius: 50%; }
.rn-status-badge.mandatory .rn-status-dot { background: var(--accent); }
.rn-status-badge.recommended .rn-status-dot { background: var(--fg); }

.rn-num-display {
  font-family: var(--font-display); font-size: 20px;
  font-weight: 500; line-height: 1;
}

.rn-capex-wrap { display: flex; align-items: center; gap: 6px; }
.rn-capex-symbol {
  font-family: var(--font-display); font-size: 18px;
  font-weight: 500; line-height: 1; letter-spacing: -0.02em;
}
.rn-capex-tier {
  font-family: var(--font-mono); font-size: 10px;
  color: rgba(26,26,26,0.4); letter-spacing: 0.05em;
}

.rn-complexity-wrap { display: flex; align-items: center; gap: 8px; }
.rn-complexity-bars { display: flex; gap: 2px; }
.rn-complexity-bar { height: 16px; width: 6px; background: rgba(26,26,26,0.15); }
.rn-complexity-bar.filled { background: var(--fg); }
.rn-complexity-label { font-size: 12px; font-weight: 500; color: var(--fg); }

.rn-expanded-row { background: var(--fg); color: var(--bg); }
.rn-expanded-content {
  padding: 32px 24px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 32px;
  animation: rnSlideDown 0.2s ease-out;
}
@keyframes rnSlideDown {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
.rn-exp-label {
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(245,242,236,0.5); margin-bottom: 12px;
}
.rn-exp-label.accent { color: var(--accent); margin-bottom: 4px; }
.rn-exp-text {
  font-size: 13px; line-height: 1.6;
  color: rgba(245,242,236,0.9); margin: 0;
}
.rn-exp-text.muted { color: rgba(245,242,236,0.8); font-size: 12px; }
.rn-exp-divider {
  border-top: 1px solid rgba(245,242,236,0.15);
  padding-top: 12px; margin-top: 12px;
}
.rn-exp-divider.big { padding-top: 16px; margin-top: 16px; }

.rn-capex-big {
  font-family: var(--font-display); font-size: 32px;
  font-weight: 500; line-height: 1;
}
.rn-capex-range {
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(245,242,236,0.5); margin-top: 4px;
}
.rn-payback-num {
  font-family: var(--font-display); font-size: 28px;
  font-weight: 500; line-height: 1;
}
.rn-payback-unit {
  font-size: 14px; font-family: var(--font-mono);
  font-weight: 400; color: rgba(245,242,236,0.5);
  letter-spacing: 0.05em;
}

.rn-empty {
  padding: 80px 0; text-align: center;
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(26,26,26,0.4);
}

.rn-legend {
  padding: 0 0 32px;
  border-top: 1px solid rgba(26,26,26,0.1);
  padding-top: 24px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 32px;
}
.rn-legend-item > div + div { margin-top: 8px; }
.rn-legend-row { display: flex; align-items: center; gap: 12px; }
.rn-legend-text { font-size: 12px; color: rgba(26,26,26,0.7); }
.rn-legend-symbol {
  font-family: var(--font-display); font-size: 15px;
  font-weight: 500; color: var(--fg); margin-right: 10px;
}

.rn-methodology {
  border-top: 1px solid rgba(26,26,26,0.1);
  padding: 24px 0 32px;
}
.rn-methodology p {
  font-size: 12px; line-height: 1.7;
  color: rgba(26,26,26,0.6); max-width: 960px; margin: 0;
}
</style>

<script>
(function() {
  'use strict';

  var data = [
    { id: 1, category: "Transport & Logistics", singleUse: "Cardboard shipping boxes (B2B transport)", reusable: "Foldable/nestable plastic crates or totes", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "40% transport reuse target by 2030, 70% by 2040", ppwrDetail: "Art. 29 transport packaging reuse target: 40% by 2030, 70% by 2040 (indicative). Applies to B2B shipments between sites or partner enterprises (100% target).", roi: "8-14", complexity: "Low", capex: "€€", capexNote: "Per-crate unit cost moderate; scales with fleet size. Tracking layer adds per-asset cost.", complexityNote: "Drop-in replacement. Only process change is return logistics. Compatible with existing conveyors, WMS, forklifts.", sector: "Cross-sector" },
    { id: 2, category: "Transport & Logistics", singleUse: "Stretch wrap / pallet film (LDPE)", reusable: "Reusable pallet covers (Bearhug, Return2Sender, elastic nets)", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Supports 2030 transport reuse and 2030 waste reduction targets", ppwrDetail: "Counts toward Art. 29 transport reuse target. Reduces plastic waste footprint under Art. 43 waste reduction targets (-5% by 2030, -15% by 2040).", roi: "6-12", complexity: "Low", capex: "€", capexNote: "Low per-unit cost. No equipment changes. Scales incrementally with pallet volume.", complexityNote: "Minimal operational change. Requires stock of covers and return loop. No equipment changes.", sector: "Cross-sector" },
    { id: 3, category: "Transport & Logistics", singleUse: "Single-use wooden pallets (one-way)", reusable: "Pooled standardised pallets (EUR/EPAL, CHEP-style)", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Supports Art. 29 B2B transport target 100% by 2030", ppwrDetail: "Already mature pooling model; reinforced by Art. 29 B2B transport targets. EPR eco-modulation will penalise single-use alternatives.", roi: "4-10", complexity: "Low", capex: "€", capexNote: "Operational expense via pooling service; no ownership CapEx.", complexityNote: "Mature pooling ecosystem exists. Main change is service contract vs. purchasing.", sector: "Cross-sector" },
    { id: 4, category: "Transport & Logistics", singleUse: "Big bags / FIBCs for bulk goods", reusable: "Reusable palletboxes or reusable IBCs", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 transport reuse 40% by 2030; 70% by 2040", ppwrDetail: "Falls under Art. 29 transport reuse targets where applicable. Bulk contact packaging has conditional exemptions depending on product (food/chemical).", roi: "18-30", complexity: "High", capex: "€€€", capexNote: "Palletbox unit cost €100 to €800+ plus filling/discharge stations, dust management, cleaning infrastructure. Significant fleet investment.", complexityNote: "Unloading paradigm shift: from cut-and-dump to tilt/vacuum extraction. Often requires new filling and discharge stations, dust management, cleaning infrastructure.", sector: "Chemicals, food ingredients, agriculture" },
    { id: 5, category: "Transport & Logistics", singleUse: "Corrugated trays for grouped transport", reusable: "Returnable Plastic Containers (RPCs), stackable trays", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Grouped packaging (non-cardboard) 10% reuse by 2030, 25% by 2040", ppwrDetail: "Art. 29 grouped packaging (non-cardboard) reuse target: 10% by 2030, 25% by 2040. Cardboard exempt but RPC systems count toward overall 40% transport target.", roi: "10-18", complexity: "Medium", capex: "€€", capexNote: "RPC unit cost plus pooling/wash infrastructure. Usually accessed via service model (EPS, IFCO).", complexityNote: "Needs pooling infrastructure, wash cycles, reverse logistics. Widely adopted in FMCG/produce (EPS, IFCO model).", sector: "FMCG, produce, grocery" },
    { id: 6, category: "Fresh Produce & Retail", singleUse: "Plastic trays, nets, bags for fresh fruit & veg <1.5kg", reusable: "Reusable retail crates, open display in bulk bins", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Hard EU-wide ban from 1 Jan 2030", ppwrDetail: "Art. 25 + Annex V: EU-wide BAN from 1 Jan 2030 on single-use plastic packaging for unprocessed fresh produce under 1.5kg (limited safety exceptions).", roi: "12-20", complexity: "Medium", capex: "€€", capexNote: "Shelf and merchandising redesign, crate fleet, checkout process changes. Store-level rollout cost.", complexityNote: "Shelf and merchandising redesign. Hygiene and shrinkage concerns. Checkout process may need SKU identifiers on crates, not items.", sector: "Grocery retail, produce suppliers" },
    { id: 7, category: "Fresh Produce & Retail", singleUse: "Cardboard fruit crates (one-way)", reusable: "Foldable plastic fruit/veg crates (pooling)", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Supports Art. 29 transport reuse 40% by 2030", ppwrDetail: "Counts toward Art. 29 transport reuse. Well-established via EPS, Euro Pool, IFCO. Eco-modulation of EPR fees will favour reusables.", roi: "8-14", complexity: "Low", capex: "€", capexNote: "Typically accessed via pooling service (OpEx not CapEx). Pay-per-trip model.", complexityNote: "Mature pooling ecosystem. Retailer-supplier coordination needed but industry standard exists.", sector: "Grocery retail, produce" },
    { id: 8, category: "Fresh Produce & Retail", singleUse: "Single-use plastic packaging for mushrooms, berries, soft fruits", reusable: "Reusable deposit-return trays (Repasys-style)", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Under Art. 25 <1.5kg produce ban from 2030", ppwrDetail: "Under the <1.5kg fresh produce ban (Art. 25 + Annex V) from 2030. Reusable deposit systems are the primary compliant replacement.", roi: "14-24", complexity: "Medium", capex: "€€", capexNote: "Deposit system infrastructure, return points, consumer-facing tracking. Proven at retailer scale in Belgian pilots.", complexityNote: "Requires deposit system, consumer education, return points at retail. Proven in Belgian pilots.", sector: "Grocery retail" },
    { id: 9, category: "HORECA & Takeaway", singleUse: "Single-use plastic cups, plates, cutlery (dine-in)", reusable: "Reusable tableware, washable cups, branded mugs", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Hard EU-wide ban from 1 Jan 2030", ppwrDetail: "Art. 25 + Annex V: EU-wide BAN from 1 Jan 2030 for on-site HORECA consumption. Includes cups, plates, trays, service-ware.", roi: "6-12", complexity: "Low", capex: "€", capexNote: "Tableware restock (typically already exists in most venues). Dishwasher capacity may need upgrading.", complexityNote: "Requires dishwashing capacity. Cost savings usually rapid given per-unit price differential.", sector: "HORECA, events" },
    { id: 10, category: "HORECA & Takeaway", singleUse: "Takeaway cups and containers (coffee, food-to-go)", reusable: "Reusable takeaway container systems (Vytal, Recup, Billie Cup)", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "2028 offer obligation; 10% reuse by 2030; 40% indicative by 2040", ppwrDetail: "Art. 29: from 2028 operators must offer consumer-owned container option and reusable/refill option. 10% reusable target by 2030 (endeavour), scaling toward 40% by 2040.", roi: "18-30", complexity: "High", capex: "€€", capexNote: "Deposit infrastructure, cleaning logistics, multi-venue interoperability platform, consumer-facing app.", complexityNote: "Needs deposit system, cleaning logistics, consumer adoption, multi-venue interoperability. Tracking essential.", sector: "HORECA, cafes, QSR" },
    { id: 11, category: "HORECA & Takeaway", singleUse: "Single-serve sachets (sauce, sugar, creamer, seasoning)", reusable: "Bulk dispensers, refillable condiment stations", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Hard EU-wide ban from 1 Jan 2030", ppwrDetail: "Art. 25 + Annex V: BAN from 1 Jan 2030 on single-use plastic sachets in HORECA for condiments, sauces, sugar, seasoning, creamer.", roi: "8-16", complexity: "Medium", capex: "€", capexNote: "Dispenser units per venue. Low per-station cost but scales with venue count.", complexityNote: "Hygiene compliance, dispenser maintenance, portion control. Staff training required.", sector: "HORECA, QSR" },
    { id: 12, category: "HORECA & Takeaway", singleUse: "Hotel miniature toiletries (shampoo, shower gel, body lotion)", reusable: "Wall-mounted refillable dispensers", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Hard EU-wide ban from 1 Jan 2030", ppwrDetail: "Art. 25 + Annex V: BAN from 1 Jan 2030 on single-use plastic packaging for cosmetics/hygiene in the accommodation sector.", roi: "12-20", complexity: "Medium", capex: "€€", capexNote: "Dispenser installation across all rooms, refilling operations, brand-compliant design. Scales with room count.", complexityNote: "Dispenser installation across rooms, refilling operations, brand experience redesign.", sector: "Hospitality, accommodation" },
    { id: 13, category: "Beverage", singleUse: "Single-use PET bottles, aluminium cans (beverages)", reusable: "Refillable glass or PET bottles (deposit-return)", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "10% reuse by 2030, 40% by 2040; 90% collection SUP bottles/cans by 2029", ppwrDetail: "Art. 29 beverage reuse target: 10% by 2030, 40% by 2040 (with exceptions for wine, spirits, milk). DRS mandatory for Member States not hitting 90% collection for single-use bottles and cans by 2029.", roi: "24-48", complexity: "High", capex: "€€€", capexNote: "Bottling line adaptation, inspection systems, wash infrastructure, reverse logistics fleet. Major CapEx requiring multi-year amortisation.", complexityNote: "Major CapEx: bottling line adaptation, inspection systems, reverse logistics, wash infrastructure. Clear ROI at scale.", sector: "Beverage producers, bottlers" },
    { id: 14, category: "Beverage", singleUse: "Shrink-wrap multipacks (bundled bottles/cans)", reusable: "Reusable crates, carriers, baskets", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Hard EU-wide ban on SUP grouped packaging at POS from 1 Jan 2030", ppwrDetail: "Art. 25 + Annex V: BAN from 1 Jan 2030 on single-use plastic grouped packaging at point of sale (purely promotional bundling).", roi: "10-18", complexity: "Medium", capex: "€€", capexNote: "Bottling line end-of-line changes, crate fleet, pooling infrastructure.", complexityNote: "Bottling line end-of-line changes. Crate pooling infrastructure. Retailer shelf standards.", sector: "Beverage, retail" },
    { id: 15, category: "Beverage", singleUse: "Cardboard beer/soft drink multipack trays", reusable: "Returnable beverage crates", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Supports 10%/40% beverage reuse targets", ppwrDetail: "Counts toward 10%/40% beverage reuse targets. Standard practice in mature beverage markets (Germany, Belgium, Netherlands).", roi: "14-22", complexity: "Medium", capex: "€€", capexNote: "Crate fleet, deposit integration, retailer coordination.", complexityNote: "Retailer coordination critical. Industry standards exist. Deposit integration.", sector: "Beverage, brewery" },
    { id: 16, category: "E-commerce", singleUse: "Single-use cardboard shipping boxes with void fill", reusable: "Reusable shipping bags/boxes (RePack, LivingPackets, Opopop)", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "40% e-commerce transport reuse by 2030, 70% by 2040; 50% empty-space cap from 2030", ppwrDetail: "Art. 29 e-commerce transport reuse target: 40% by 2030, 70% by 2040. Art. 24: 50% empty-space ratio cap from 2030 also pushes redesign.", roi: "18-36", complexity: "High", capex: "€€", capexNote: "Reusable mailer/box fleet, reverse logistics network, consumer-facing return infrastructure.", complexityNote: "Reverse logistics for B2C is the hardest variable. Works best with closed-loop retailers or subscription models. Consumer behaviour change needed.", sector: "E-commerce, D2C brands" },
    { id: 17, category: "E-commerce", singleUse: "Bubble wrap, air pillows, paper void fill", reusable: "Reusable protective inserts, foam shaped inserts", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 10 minimisation from 2026; 50% empty-space cap from 2030", ppwrDetail: "Supports Art. 10 minimisation (from 2026) and 50% empty-space cap (2030). EPR eco-modulation will penalise single-use fill.", roi: "12-20", complexity: "Medium", capex: "€", capexNote: "Foam/insert tooling for standard SKUs. Low cost in closed-loop B2B, higher complexity in B2C.", complexityNote: "Works well in closed-loop B2B. More complex in open B2C. SKU-to-insert matching for multi-SKU ops.", sector: "E-commerce, electronics, fragile goods" },
    { id: 18, category: "Industrial & B2B", singleUse: "Single-use plastic drums, jerrycans for chemicals/liquids", reusable: "Reusable IBCs, stainless steel drums, returnable jerrycans", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 B2B: 100% reuse target between sites of same/partner enterprises", ppwrDetail: "Art. 29 B2B transport: 100% reuse target for packaging between sites of same or partner enterprises. EPR eco-modulation penalises single-use.", roi: "14-24", complexity: "Medium", capex: "€€", capexNote: "IBC/drum fleet plus cleaning and decontamination infrastructure. Existing rental/pool market reduces CapEx exposure.", complexityNote: "Cleaning/decontamination infrastructure is the main lift (especially for chemicals). Strong existing market for IBCs.", sector: "Chemicals, lubricants, food ingredients" },
    { id: 19, category: "Industrial & B2B", singleUse: "EPS foam boxes (temperature-sensitive goods)", reusable: "Insulated reusable cool boxes with phase-change materials", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Supports Art. 10 minimisation and Art. 29 transport reuse", ppwrDetail: "Supports Art. 10 minimisation and Art. 29 transport reuse. EPS increasingly restricted under national law and recyclability grade rules.", roi: "16-28", complexity: "High", capex: "€€€", capexNote: "Insulated box units are high unit-cost (€100 to €500+). Phase-change material conditioning infrastructure. Critical for pharma cold chain.", complexityNote: "CapEx on insulated boxes is significant. Reverse logistics and conditioning cycles required. Critical for pharma cold chain.", sector: "Pharma, cold chain, seafood, meal kits" },
    { id: 20, category: "Industrial & B2B", singleUse: "Cardboard gaylord boxes / one-way palletboxes", reusable: "Foldable plastic palletboxes (injection-moulded)", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 B2B 100% reuse target where applicable", ppwrDetail: "Art. 29 B2B transport 100% target where applicable. Strong EPR eco-modulation incentive. Durability of HDPE/PP palletboxes gives high rotation counts.", roi: "18-30", complexity: "Medium", capex: "€€€", capexNote: "Palletbox unit cost €100 to €800 depending on design. Fleet investment significant; amortises over 50+ rotations.", complexityNote: "Handling equipment usually compatible. Folding mechanism reduces return transport cost. Initial unit cost is higher.", sector: "Manufacturing, automotive parts, industrial" },
    { id: 21, category: "Automotive & Parts", singleUse: "Single-use cardboard/foam dividers for parts", reusable: "Reusable dunnage, custom-fit plastic trays", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 B2B closed-loop 100% reuse target", ppwrDetail: "Art. 29 B2B closed-loop: 100% reuse target. Automotive is already a mature reusable dunnage sector (VDA standards).", roi: "12-20", complexity: "Medium", capex: "€€", capexNote: "Custom tooling for part-specific dunnage. VDA/Odette standards reduce design cost.", complexityNote: "Tooling investment for custom dunnage. Existing industry standards (VDA, Odette) ease adoption.", sector: "Automotive, aerospace, industrial" },
    { id: 22, category: "Fashion & Retail", singleUse: "Polybags / dust covers for garments in transit", reusable: "Reusable garment covers / shipping bags", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Supports Art. 29 transport reuse 40% by 2030", ppwrDetail: "Art. 29 transport reuse target 40% by 2030. Fashion industry pressure on single-use polybags accelerates voluntary adoption.", roi: "14-24", complexity: "Medium", capex: "€", capexNote: "Fabric/polymer cover unit cost is low. Scales with garment throughput.", complexityNote: "Works well in closed-loop (DC to store). Harder in open B2C e-commerce. Brand experience considerations.", sector: "Fashion, apparel retail" },
    { id: 23, category: "Fashion & Retail", singleUse: "Cardboard hanger boxes, garment cartons", reusable: "Reusable hanging garment racks/containers", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 transport reuse target 40% by 2030", ppwrDetail: "Art. 29 transport target (40%/70%). Common in apparel DC-to-store (GOH, garment on hanger).", roi: "16-24", complexity: "Medium", capex: "€€", capexNote: "Rack fleet plus truck fitment. Closed-loop DC-to-store reduces reverse logistics cost.", complexityNote: "Closed-loop between DC and store works well. Requires truck fitment, rack pooling.", sector: "Fashion retail" },
    { id: 24, category: "Agriculture & Horticulture", singleUse: "Single-use plastic plant trays, pots", reusable: "Reusable multi-use nursery trays", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 B2B 100% reuse target closed-loop grower-retailer", ppwrDetail: "Art. 29 B2B transport 100% target for closed-loop between grower and retailer. EPR eco-modulation favours reusables.", roi: "12-20", complexity: "Medium", capex: "€€", capexNote: "Tray fleet investment. CC Container pooling model already established for larger units.", complexityNote: "CC (Container Centralen) model well established in horticulture. Expansion to smaller trays is next step.", sector: "Horticulture, nurseries" },
    { id: 25, category: "Agriculture & Horticulture", singleUse: "Single-use plastic sacks (feed, seed, fertiliser)", reusable: "Reusable FIBCs/big bags with tracking", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 B2B transport target; supports Art. 43 waste reduction", ppwrDetail: "Art. 29 B2B transport target. Supports waste reduction under Art. 43. Tracking ensures return loop integrity.", roi: "16-28", complexity: "High", capex: "€€", capexNote: "FIBC unit cost plus cleaning infrastructure and tracking layer. Reverse logistics to dispersed rural sites adds cost.", complexityNote: "Cleaning between uses (contamination risk), tracking, return logistics to rural/dispersed sites.", sector: "Agriculture, animal feed" },
    { id: 26, category: "Construction", singleUse: "Plastic film wrap on building materials", reusable: "Reusable construction packaging covers", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 B2B transport; eco-modulation pressure by 2030", ppwrDetail: "Art. 29 B2B transport. Low adoption currently but eco-modulation and waste targets will push change by 2030.", roi: "18-36", complexity: "High", capex: "€€", capexNote: "Durable covers for harsh site conditions. Return logistics in fragmented construction supply chain is the main cost driver.", complexityNote: "Site-to-site coordination, durability in harsh conditions, return loops in fragmented construction supply chain.", sector: "Construction, building materials" },
    { id: 27, category: "Construction", singleUse: "Cardboard boxes for fittings, fixtures, hardware", reusable: "Stackable returnable tote systems", ppwrStatus: "Mandatory", deadline: 2030, deadlineNote: "Art. 29 transport reuse 40% by 2030", ppwrDetail: "Art. 29 transport reuse target 40% by 2030. Wholesale distribution to contractors is closed-loop friendly.", roi: "10-18", complexity: "Medium", capex: "€€", capexNote: "Tote fleet via distributor network (e.g. Lecot, Gamma Wopla). Route density makes return economics favourable.", complexityNote: "Works well with distributor networks (Lecot, Gamma Wopla, etc.). Crate return via route density.", sector: "Construction, hardware wholesale" },
    { id: 28, category: "Postal & Express", singleUse: "Single-use padded mailers (jiffy bags)", reusable: "Reusable padded shipping envelopes", ppwrStatus: "Recommended", deadline: 2030, deadlineNote: "Art. 29 e-commerce target 40% by 2030; Art. 10 minimisation from 2026", ppwrDetail: "Art. 29 e-commerce target 40% by 2030. Art. 10 minimisation from 2026. Low-hanging fruit for subscription and rental models.", roi: "14-24", complexity: "Medium", capex: "€", capexNote: "Mailer unit cost low. Return label and drop-off network costs via existing postal infrastructure.", complexityNote: "Strong fit for B2B or subscription B2C. Prepaid return labels, drop-off network, consumer compliance.", sector: "E-commerce, subscriptions, rental" }
  ];

  var statusOrder = { Mandatory: 0, Recommended: 1 };
  var complexityOrder = { Low: 0, Medium: 1, High: 2 };
  var capexOrder = { "€": 0, "€€": 1, "€€€": 2 };

  var state = {
    search: "",
    sortBy: "category",
    sortDir: "asc",
    filterStatus: "all",
    filterComplexity: "all",
    filterCapex: "all",
    horizon: "all",
    expandedRow: null
  };

  function escapeHtml(str) {
    if (str == null) return "";
    return String(str)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  function getFiltered() {
    var q = state.search.toLowerCase();
    var result = data.filter(function(item) {
      var matchSearch = !q ||
        item.singleUse.toLowerCase().indexOf(q) !== -1 ||
        item.reusable.toLowerCase().indexOf(q) !== -1 ||
        item.category.toLowerCase().indexOf(q) !== -1 ||
        item.sector.toLowerCase().indexOf(q) !== -1;
      var matchStatus = state.filterStatus === "all" || item.ppwrStatus === state.filterStatus;
      var matchComplexity = state.filterComplexity === "all" || item.complexity === state.filterComplexity;
      var matchCapex = state.filterCapex === "all" || item.capex === state.filterCapex;
      var matchHorizon = state.horizon === "all" ||
        (state.horizon === "2030" && item.deadline <= 2030) ||
        (state.horizon === "2040" && item.deadline <= 2040);
      return matchSearch && matchStatus && matchComplexity && matchCapex && matchHorizon;
    });

    result.sort(function(a, b) {
      var av, bv;
      if (state.sortBy === "status") { av = statusOrder[a.ppwrStatus]; bv = statusOrder[b.ppwrStatus]; }
      else if (state.sortBy === "complexity") { av = complexityOrder[a.complexity]; bv = complexityOrder[b.complexity]; }
      else if (state.sortBy === "capex") { av = capexOrder[a.capex]; bv = capexOrder[b.capex]; }
      else if (state.sortBy === "roi") { av = parseInt(a.roi.split("-")[0], 10); bv = parseInt(b.roi.split("-")[0], 10); }
      else if (state.sortBy === "deadline") { av = a.deadline; bv = b.deadline; }
      else { av = a[state.sortBy]; bv = b[state.sortBy]; }
      if (av < bv) return state.sortDir === "asc" ? -1 : 1;
      if (av > bv) return state.sortDir === "asc" ? 1 : -1;
      return 0;
    });

    return result;
  }

  function statusBadge(status) {
    var cls = status === "Mandatory" ? "mandatory" : "recommended";
    return '<span class="rn-status-badge ' + cls + '"><span class="rn-status-dot"></span>' + escapeHtml(status) + '</span>';
  }

  function complexityBar(level) {
    var levels = { Low: 1, Medium: 2, High: 3 };
    var n = levels[level] || 0;
    var bars = "";
    for (var i = 1; i <= 3; i++) {
      bars += '<div class="rn-complexity-bar' + (i <= n ? ' filled' : '') + '"></div>';
    }
    return '<div class="rn-complexity-wrap"><div class="rn-complexity-bars">' + bars + '</div><span class="rn-complexity-label">' + level + '</span></div>';
  }

  function capexBadge(band) {
    var tier = band === "€" ? "LOW" : band === "€€" ? "MID" : "HIGH";
    return '<div class="rn-capex-wrap"><span class="rn-capex-symbol">' + band + '</span><span class="rn-capex-tier">' + tier + '</span></div>';
  }

  function sortBtn(field, label) {
    var active = state.sortBy === field;
    var arrow = active ? (state.sortDir === "asc" ? "↑" : "↓") : "↕";
    return '<button class="rn-sort-btn' + (active ? ' active' : '') + '" data-sort="' + field + '">' + label + ' <span class="rn-sort-arrow">' + arrow + '</span></button>';
  }

  function expandedContent(item) {
    var capexRange = item.capex === "€" ? "Low (under €50k pilot)" : item.capex === "€€" ? "Mid (€50k to €500k)" : "High (€500k+)";
    return '<tr class="rn-expanded-row"><td colspan="9"><div class="rn-expanded-content">' +
      '<div>' +
        '<div class="rn-exp-label">PPWR Reference</div>' +
        '<p class="rn-exp-text">' + escapeHtml(item.ppwrDetail) + '</p>' +
        '<div class="rn-exp-divider">' +
          '<div class="rn-exp-label accent">Key horizon</div>' +
          '<p class="rn-exp-text muted">' + escapeHtml(item.deadlineNote) + '</p>' +
        '</div>' +
      '</div>' +
      '<div>' +
        '<div class="rn-exp-label">Operational lift</div>' +
        '<p class="rn-exp-text">' + escapeHtml(item.complexityNote) + '</p>' +
      '</div>' +
      '<div>' +
        '<div class="rn-exp-label">CapEx profile</div>' +
        '<div style="margin-bottom:12px;"><div class="rn-capex-big">' + item.capex + '</div><div class="rn-capex-range">' + capexRange + '</div></div>' +
        '<p class="rn-exp-text">' + escapeHtml(item.capexNote) + '</p>' +
      '</div>' +
      '<div>' +
        '<div class="rn-exp-label">Primary sectors</div>' +
        '<p class="rn-exp-text">' + escapeHtml(item.sector) + '</p>' +
        '<div class="rn-exp-divider big">' +
          '<div class="rn-exp-label">Payback window</div>' +
          '<div class="rn-payback-num">' + escapeHtml(item.roi) + ' <span class="rn-payback-unit">months</span></div>' +
        '</div>' +
      '</div>' +
    '</div></td></tr>';
  }

  function render() {
    var root = document.getElementById("rotion-navigator-root");
    if (!root) return;
    var filtered = getFiltered();

    var horizonCaption = {
      "2030": "Showing transitions binding or in force by 1 Jan 2030",
      "2040": "Showing transitions binding or in force by 2040",
      "all": "Showing all transitions across all deadlines"
    }[state.horizon];

    var html = '';

    // HORIZON BAR
    html += '<div class="rn-horizon-bar"><div class="rn-container"><div class="rn-horizon-inner">';
    html += '<div style="display:flex;align-items:center;gap:8px;">';
    html += '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>';
    html += '<span class="rn-label rn-label-medium">Compliance horizon</span>';
    html += '</div>';
    html += '<div class="rn-segmented">';
    ['all','2030','2040'].forEach(function(h) {
      var labels = { all: "All horizons", "2030": "By 2030", "2040": "By 2040" };
      html += '<button data-horizon="' + h + '"' + (state.horizon === h ? ' class="active"' : '') + '>' + labels[h] + '</button>';
    });
    html += '</div>';
    html += '<div class="rn-horizon-caption">' + horizonCaption + '</div>';
    html += '</div></div></div>';

    // CONTROLS
    html += '<div class="rn-controls"><div class="rn-container"><div class="rn-controls-inner">';
    html += '<div class="rn-search-wrapper">';
    html += '<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>';
    html += '<input type="text" id="rn-search" placeholder="Search packaging, sector, or material..." value="' + escapeHtml(state.search) + '"/>';
    if (state.search) html += '<button class="rn-search-clear" id="rn-search-clear">×</button>';
    html += '</div>';

    // PPWR filters
    html += '<div class="rn-filter-group"><span class="rn-label">PPWR</span>';
    ['all','Mandatory','Recommended'].forEach(function(s) {
      html += '<button class="rn-filter-btn' + (state.filterStatus === s ? ' active' : '') + '" data-filter-status="' + s + '">' + (s === 'all' ? 'All' : s) + '</button>';
    });
    html += '</div>';

    // Complexity filters
    html += '<div class="rn-filter-group"><span class="rn-label">Complexity</span>';
    ['all','Low','Medium','High'].forEach(function(c) {
      html += '<button class="rn-filter-btn' + (state.filterComplexity === c ? ' active' : '') + '" data-filter-complexity="' + c + '">' + (c === 'all' ? 'All' : c) + '</button>';
    });
    html += '</div>';

    // CapEx filters
    html += '<div class="rn-filter-group"><span class="rn-label">CapEx</span>';
    [['all','All'],['€','€'],['€€','€€'],['€€€','€€€']].forEach(function(pair) {
      html += '<button class="rn-filter-btn' + (state.filterCapex === pair[0] ? ' active' : '') + '" data-filter-capex="' + pair[0] + '">' + pair[1] + '</button>';
    });
    html += '</div>';

    html += '<div class="rn-count">' + filtered.length + ' <span class="rn-count-total">/ ' + data.length + '</span></div>';
    html += '</div></div></div>';

    // TABLE
    html += '<div class="rn-container"><div class="rn-table-wrap">';
    html += '<table class="rn-table"><thead><tr>';
    html += '<th style="width:48px;"></th>';
    html += '<th>' + sortBtn("category", "Category") + '</th>';
    html += '<th>Single-use</th>';
    html += '<th>Reusable alternative</th>';
    html += '<th>' + sortBtn("status", "PPWR") + '</th>';
    html += '<th>' + sortBtn("deadline", "Deadline") + '</th>';
    html += '<th>' + sortBtn("roi", "ROI (mo)") + '</th>';
    html += '<th>' + sortBtn("capex", "CapEx") + '</th>';
    html += '<th>' + sortBtn("complexity", "Complexity") + '</th>';
    html += '</tr></thead><tbody>';

    filtered.forEach(function(item, idx) {
      var isExpanded = state.expandedRow === item.id;
      html += '<tr class="rn-data-row' + (isExpanded ? ' expanded' : '') + '" data-row-id="' + item.id + '">';
      html += '<td><div class="rn-row-num-wrap"><span class="rn-row-num">' + String(idx + 1).padStart(2, "0") + '</span><span class="rn-chevron">' + (isExpanded ? "▲" : "▼") + '</span></div></td>';
      html += '<td><div class="rn-category">' + escapeHtml(item.category) + '</div></td>';
      html += '<td><div class="rn-single-use">' + escapeHtml(item.singleUse) + '</div></td>';
      html += '<td><div class="rn-reusable">' + escapeHtml(item.reusable) + '</div></td>';
      html += '<td>' + statusBadge(item.ppwrStatus) + '</td>';
      html += '<td><div class="rn-num-display">' + item.deadline + '</div></td>';
      html += '<td><div class="rn-num-display">' + escapeHtml(item.roi) + '</div></td>';
      html += '<td>' + capexBadge(item.capex) + '</td>';
      html += '<td>' + complexityBar(item.complexity) + '</td>';
      html += '</tr>';
      if (isExpanded) html += expandedContent(item);
    });

    html += '</tbody></table>';
    if (filtered.length === 0) html += '<div class="rn-empty">No matches for current filters</div>';
    html += '</div>';

    // LEGEND
    html += '<div class="rn-legend">';
    html += '<div class="rn-legend-item">';
    html += '<div class="rn-label" style="margin-bottom:12px;">PPWR status</div>';
    html += '<div class="rn-legend-row">' + statusBadge("Mandatory") + '<span class="rn-legend-text">Hard EU-wide ban or binding reuse quota under Regulation 2025/40</span></div>';
    html += '<div class="rn-legend-row">' + statusBadge("Recommended") + '<span class="rn-legend-text">Strongly incentivised via reuse targets, EPR eco-modulation, waste reduction</span></div>';
    html += '</div>';
    html += '<div class="rn-legend-item">';
    html += '<div class="rn-label" style="margin-bottom:12px;">CapEx bands</div>';
    html += '<div class="rn-legend-text"><span class="rn-legend-symbol">€</span>Low: under €50k for a typical pilot deployment</div>';
    html += '<div class="rn-legend-text"><span class="rn-legend-symbol">€€</span>Mid: €50k to €500k infrastructure and fleet</div>';
    html += '<div class="rn-legend-text"><span class="rn-legend-symbol">€€€</span>High: €500k+ capital investment at scale</div>';
    html += '</div>';
    html += '<div class="rn-legend-item">';
    html += '<div class="rn-label" style="margin-bottom:12px;">Complexity</div>';
    html += '<div class="rn-legend-row">' + complexityBar("Low") + '<span class="rn-legend-text">Drop-in or near drop-in replacement</span></div>';
    html += '<div class="rn-legend-row">' + complexityBar("Medium") + '<span class="rn-legend-text">Process changes, some infrastructure</span></div>';
    html += '<div class="rn-legend-row">' + complexityBar("High") + '<span class="rn-legend-text">Major operational or supply chain redesign</span></div>';
    html += '</div>';
    html += '</div>';

    // METHODOLOGY
    html += '<div class="rn-methodology">';
    html += '<div class="rn-label" style="margin-bottom:8px;">Methodology note</div>';
    html += '<p>ROI estimates assume tracked reusable assets with average rotation lifespans of 50 to 500+ cycles depending on format. Payback includes asset CapEx, washing/handling, reverse logistics, and tracking infrastructure, offset against avoided single-use material cost and EPR fees. Complexity scores reflect process change, CapEx, and supply-chain coordination. CapEx bands are indicative order-of-magnitude ranges for a pilot to mid-scale deployment and will vary significantly by volume and geography. Deadlines refer to the year by which an obligation becomes enforceable or practically binding, based on Regulation 2025/40 timelines (12 Aug 2026 application date, 1 Jan 2030 for key bans and reuse targets, 2040 for longer-horizon thresholds).</p>';
    html += '</div>';
    html += '</div>';

    root.innerHTML = html;
    attachHandlers();
  }

  function attachHandlers() {
    var root = document.getElementById("rotion-navigator-root");
    if (!root) return;

    // Horizon
    root.querySelectorAll("[data-horizon]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        state.horizon = btn.getAttribute("data-horizon");
        render();
      });
    });

    // Search
    var searchInput = root.querySelector("#rn-search");
    if (searchInput) {
      searchInput.addEventListener("input", function(e) {
        state.search = e.target.value;
        var cursor = e.target.selectionStart;
        render();
        var newInput = document.getElementById("rn-search");
        if (newInput) {
          newInput.focus();
          try { newInput.setSelectionRange(cursor, cursor); } catch(e) {}
        }
      });
    }
    var searchClear = root.querySelector("#rn-search-clear");
    if (searchClear) {
      searchClear.addEventListener("click", function() {
        state.search = "";
        render();
      });
    }

    // Filters
    root.querySelectorAll("[data-filter-status]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        state.filterStatus = btn.getAttribute("data-filter-status");
        render();
      });
    });
    root.querySelectorAll("[data-filter-complexity]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        state.filterComplexity = btn.getAttribute("data-filter-complexity");
        render();
      });
    });
    root.querySelectorAll("[data-filter-capex]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        state.filterCapex = btn.getAttribute("data-filter-capex");
        render();
      });
    });

    // Sort
    root.querySelectorAll("[data-sort]").forEach(function(btn) {
      btn.addEventListener("click", function(e) {
        e.stopPropagation();
        var field = btn.getAttribute("data-sort");
        if (state.sortBy === field) {
          state.sortDir = state.sortDir === "asc" ? "desc" : "asc";
        } else {
          state.sortBy = field;
          state.sortDir = "asc";
        }
        render();
      });
    });

    // Row expand
    root.querySelectorAll(".rn-data-row").forEach(function(row) {
      row.addEventListener("click", function() {
        var id = parseInt(row.getAttribute("data-row-id"), 10);
        state.expandedRow = state.expandedRow === id ? null : id;
        render();
      });
    });
  }

  // Polyfill padStart for older browsers
  if (!String.prototype.padStart) {
    String.prototype.padStart = function(targetLength, padString) {
      targetLength = targetLength >> 0;
      padString = String(typeof padString !== "undefined" ? padString : " ");
      if (this.length > targetLength) return String(this);
      targetLength = targetLength - this.length;
      if (targetLength > padString.length) {
        padString += padString.repeat(targetLength / padString.length);
      }
      return padString.slice(0, targetLength) + String(this);
    };
  }

  render();
})();
</script>