Website Design / Website Development / E-Commerce / SEO / Web Support / 2026
Ecological Adventure: 66 stylesheets, then a page with no words in it, then a platform
Client: Ecological Wilderness Adventure
Ecological Wilderness Adventure has been selling Tanzanian safaris online since 2013, and had been rebuilt twice before we saw it. The last WordPress homepage shipped 741KB of HTML, 66 stylesheets and 66 scripts before a single photograph. The JavaScript rewrite that replaced it shipped 5KB — and not one readable word, because the page was assembled in the browser. We rebuilt it as a Laravel platform: 80 models, 210 screens, a booking engine with NMB payments, a visual page builder and 737 automated tests, and the homepage now serves 14,881 characters of finished HTML before any JavaScript runs.
The challenge
The site had been rebuilt twice already — a Joomla install before 2015, WordPress after it — and each rebuild solved the previous problem by creating a different one.
WordPress was the long chapter. By 2025 the homepage was a page builder standing on a plugin stack: Elementor and Elementor Pro, the Hello theme underneath, and five add-on packs on top — ElementsKit, Exclusive Addons, the Gum add-on, Jeg Elementor Kit and Ultimate Post Kit — with WooCommerce, Formidable Forms, Link Whisper, Perfmatters and a consent plugin alongside them. That homepage answered with 741KB of HTML, 66 stylesheets, 66 external scripts and 10 inline style blocks. The content underneath was scattered across seven custom post types — to_book, safari, destination, accommodation, mountain, portfolio and event — plus a booking plugin's own ba_location, ba_type and ba_amenities, and a separate plugin holding the map. Nothing was a record: a tour was a hand-built page, so its price and its itinerary were paragraphs.
The rewrite that followed moved the whole site into JavaScript, and it is the failure worth dwelling on, because it is invisible from a browser. The archived homepage from March 2026 is 5KB, and the text inside its body element is exactly zero characters long. Everything a reader saw arrived afterwards, from an API, drawn by a script — which is fine for a person with a fast phone and nothing at all for anything that does not run one. The URLs changed shape in the same move: /accommodation/ became /accommodations/, /safari/ became /tours/, and 748 paths the site had spent a decade earning were left with nowhere to land.
The approach
We audited before writing anything. The Next.js application we were migrating from had 170 Prisma models, 34 enums, 208 pages, 299 API route handlers, around 60 admin modules and 148 components across five locales — so the first deliverable was not code, it was a decision per module: port now, defer, or drop. Payments moved from PesaPal to NMB behind a driver contract. The five locale files were archived rather than ported, English first, the schema left translation-ready. Nothing about the design was allowed to change.
Then the rule that fixed the invisible problem: the server renders the page. Inertia and React 19 keep the same component model the team already had, but the HTML that leaves the server is finished — the homepage carries 14,881 characters of readable text before a line of JavaScript executes, against zero on the site it replaced.
Then we modelled the business. Eighty Eloquent models over 72 migrations: Tour, TourItinerary, TourDeparture, TourPricing, SeasonalPricing, PricingRule, KilimanjaroRoute and KilimanjaroItinerary, Accommodation, Destination, Experience — and on the commercial side Booking, BookingTraveler, Payment, PaymentInstallment, PaymentSchedule, Invoice, Refund, Coupon, Deal, FlashSale, LoyaltyTransaction, Referral and TourWaitlist. A tour is a record that knows its own days, its own seasons and its own price, and every listing, filter, sitemap entry and quote is a query.
The rest is the part nobody sees: 122 controllers with form requests and policies rather than fat handlers, two payment drivers behind one contract, media on Cloudflare R2, Sentry on the errors, an audit log and a login-activity trail, real-user web vitals reported back from the field, and 737 automated tests across 117 files standing behind all of it — 188 commits between 15 July and 1 September 2026.
The results
The homepage went from 741KB of HTML to 307KB, from 66 stylesheets to 2, from 66 external scripts to 2, and from 10 inline style blocks to none. The measurement that matters more than any of those is the one the middle rebuild lost: server-rendered text went from 0 characters to 14,881.
Behind it, 405 URLs in the sitemap — 144 accommodations, 88 journal posts, 72 destinations, 44 tours and 17 experiences — all of them records now, filterable and quotable rather than retyped. Bookings take travellers, deposits and instalments; payment goes through NMB or a confirmed bank transfer, both behind the same contract. Marketing edits pages in Timeless Builder with versions and reusable sections rather than filing a ticket. And 56 admin modules cover the things a tour operator actually does between enquiry and invoice: quotes, departures, waitlists, review invitations, referrals, refunds, expenses and vendors.
What is not finished, and we would rather say it than let somebody else find it: the legacy URLs. Of the 748 paths the WordPress site had indexed, 726 still answer 404 and only 19 resolve or redirect. The break happened in the JavaScript rewrite, before this build existed, and this build has not yet undone it. Every piece of machinery needed to is already here: a redirects table, middleware that serves it, an admin screen to manage it, and a log of every 404 a visitor hits. What is missing is the map — and building it from the archive's own index of the old site is the next round of work.