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.

Free guide: PPWR reuse reporting
What the regulation requires you to track, on what timeline, and how to have the data ready.
We'll email the guide to the address you enter. No spam, only relevant follow-up.
<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>