HomeCase studiesA Content Platform and Companion Mobile App...

Case study · Boston, United States

A Content Platform and Companion Mobile App for a National Broadcast Media Non-Profit

Non-profit broadcast media — daily programming, affiliate station syndication, and a large surrounding content library

A national non-profit broadcaster had outgrown a plugin-assembled WordPress estate: publishing was tied to a broadcast clock the CMS could not model, two decades of indexed URLs were at risk in any move, and there was no way to reach an audience on a phone. Our team replatformed the organisation onto a purpose-built Laravel application with a hand-built CMS, treating search-visibility continuity as a dedicated engineering subsystem rather than a launch checklist item, and delivered a hybrid mobile application with native push notifications timed to the broadcast schedule.

Industry
Non-profit broadcast media — daily programming, affiliate station syndication, and a large surrounding content library
Solution
A full replatform. A Laravel application serving the public website, a bespoke administrative CMS and a mobile-facing API, plus a cross-platform mobile application with native push notifications.
Platforms
Cross-platform · Web & API
Stack
Laravel · PHP · Blade · React Native · React · TypeScript
Location
Boston, United States · delivered remotely
Role
Engineering, with the team behind for mobile and QA

Delivered remotely for a business based in Boston, United States. Names withheld by agreement.

Project Overview

Industry: Non-profit broadcast media — daily programming, affiliate station syndication, and a large surrounding content library.

Type of solution: A full replatform. A Laravel application serving the public website, a bespoke administrative CMS and a mobile-facing API, plus a cross-platform mobile application with native push notifications.

Business context: The organisation produces a daily live programme with hosts and guests, carried by a network of affiliate radio stations, and publishes a large volume of surrounding material — episode resources, articles, video, a daily short-form commentary series with audio, downloadable booklets, campaign content and public-record documents. All of it had accumulated on a WordPress installation where each capability came from a different plugin, with no shared content model, no way to express the broadcast schedule, and no mobile presence.

General users: Content editors and administrators; public-facing people records for hosts, guests, authors, on-air talent and staff; registered supporters who follow people, save content and set notification preferences; campaign subscribers; and the general public.

General purpose: To give the organisation one content model that understands its broadcast schedule, an administrative experience built for how it actually publishes, uninterrupted search visibility across a very large body of existing URLs, and a direct, timely channel to its audience on mobile.

The Business Challenge

Publishing is tied to a broadcast clock, not a calendar. "Today's episode" is not simply the record dated today. It depends on air time, on a post-midnight boundary, on whether the programme is currently live, on an archive grace period after it ends, and on different behaviour at weekends. A generic CMS has no concept of any of this, so editors were compensating manually — at air time, every day.

Two decades of indexed URLs were at risk. Search traffic is how a broadcaster is found between airings. A replatform that broke old permalink shapes, archive URLs, feeds or plugin-generated sitemaps would have destroyed the accumulated search visibility of thousands of pages, and no amount of new-platform quality would have compensated.

The legacy corpus was inconsistent. Content, media, dates, authorship and person records had drifted over many years. Media URLs were embedded in free text throughout the database. The same person appeared as several records from several sources.

Every capability came from a different plugin. Forms, redirects, SEO metadata, pop-ups and opt-ins were each a separate vendor with its own admin, its own data and its own failure mode. Nothing shared a content model, and nothing was available to a mobile client.

There was no mobile channel. A broadcaster's most valuable notification — the programme is about to start — could not be delivered. Neither could a daily content prompt, a campaign alert, or any personalised return path.

Supporters had no reason to return. No favourites, no saved items, no way to follow a particular host or contributor, no notification control. Every visit started from zero.

A non-profit has public-record obligations. Programme logs, financial reports, board records and volunteer intake all have to be published and maintained, and none of it fits a blog-shaped content model.

Two audiences, one team. Internal editors and public supporters are entirely different populations with different lifecycles — but the public people who appear in content are also, sometimes, account holders.

Our Approach

Model the broadcast clock once, in one place. We built dedicated services that resolve which episode is "current" and which should lead the homepage, honouring air time, a post-midnight day boundary, a live window, a post-air grace period and distinct weekend behaviour. Every consumer — homepage, hero unit, archive listing, notifications — asks the same service, so the rules cannot drift apart. The services accept an injectable notion of "now", which makes genuinely awkward temporal logic testable rather than something you verify by waiting until tomorrow.

Treat search-visibility continuity as a subsystem, not a task. We built three layers. Pattern-based middleware maps whole families of legacy URL shapes — old permalink formats, dated post URLs, archive pages, feeds, legacy sitemaps — onto the new structure. An administrator-managed redirect table handles the long tail case by case, with click counts and last-hit timestamps so the team can see what is still being requested. And a centralised metadata builder emits titles, descriptions, canonicals, social cards and structured data, deliberately reproducing the shape the previous site emitted so that search engines saw continuity rather than a new site.

Let the scheduler own content state. Scheduled publishing is promoted by a command that runs every minute, and episodes are archived automatically a fixed window after they finish airing. Publication state is therefore correct regardless of traffic, and editors schedule in advance instead of staying up until air time.

Build migration tooling as real software. Rather than one-off scripts, we built a body of console commands for import, backfill, normalisation, deduplication and repair — including a scanner that introspects every text column in the database looking for legacy URLs and drives bulk rewrites from what it finds, because "the old media URLs are embedded in free text somewhere and nobody knows where" is not a problem you solve by guessing. Person deduplication runs through a single merge service that collision-safely re-points every favourite, save, follow, push token, preference and pivot row onto the surviving record — shared by both a command and an administrative import, so the logic exists exactly once.

Build the CMS by hand, around how this team publishes. No admin framework package. Every list, filter, bulk action, import, export, ordering control and media picker was built for the actual editorial workflow — including a duplicate-to-draft behaviour modelled on what editors already knew from the previous platform, because a replatform is disruptive enough without also retraining everyone on unfamiliar publishing mechanics.

Make notifications configuration rather than code. Notification types are declared with their deep-link payload keys and their mapping to user-facing preference categories. Adding a notification type is a configuration change and a scheduled command, not a new bespoke delivery path.

Ship a hybrid mobile shell, not a second client. The mobile application is a native tab shell around the web experience, with native push, native sharing, native splash and full session control — including cookie clearing across both platform cookie stores on logout, and a JavaScript bridge carrying login, logout and content-change events between the two layers. One content model, one release cycle for content features, native only where native is genuinely required.

The Solution

Broadcast-aware episode publishing. Episodes carry hosts, guests, air date, live state, streaming links and an embedded live player, with automatic promotion and archiving driven by the broadcast schedule rather than by manual intervention.

A structured content library. Episode resources, articles and video are modelled as first-class records linked to episodes with explicit ordering and guest attribution, rather than as loose posts.

A daily commentary series with audio. Short-form commentary items with audio, author attribution and scheduled release, surfaced on the site and delivered by scheduled push.

Advocacy and campaign content. Campaign items with message templates and targeting, a recurring campaign series with its own subscriber list and welcome email, and downloadable resources delivered through hosted request forms.

Affiliate station directory. A searchable, filterable directory of carrying stations with call letters, dial positions, networks, schedules and coverage information, maintained through bulk import and export rather than record by record.

Supporter personalisation. Favourites, saved content and follow relationships spanning every content type, with per-category notification preferences under the supporter's control.

Scheduled push notifications. A pre-air programme reminder on a rolling window, a daily content notification on a weekday schedule, a weekly campaign notification and an optional fundraising campaign — all timezone-pinned to the broadcast schedule, filtered by user preference, and deep-linking into the right destination even from a cold start.

Search-visibility continuity. Pattern-based legacy redirects, an administrator-managed redirect table with click analytics, per-content-type XML sitemaps with an index and scheduled cache invalidation, and centralised structured-data output.

Public-record publishing. Monthly programme logs published as documents with previews, plus financial reports, board records, volunteer opportunities and volunteer application intake.

Document generation. Any content item can be exported as a PDF, with an additional composition flow for producing tailored documents.

A hand-built CMS. Full management of every content type with drafts, scheduling, duplication, soft deletes, authorship tracking and activity logging, plus a media library on cloud object storage, a navigation menu builder, site and home settings, featured-content controls and an embed manager for third-party pop-ups and opt-ins with page-matching rules.

A companion mobile application. Native tabbed navigation across the home, following, favourites and saved views, with native push, native sharing, session continuity with the web layer and platform-correct back-button and tab-reset behaviour.

Key Features

Broadcast-clock content resolution Dedicated services decide which episode is current and which leads the homepage, accounting for air time, a post-midnight day boundary, live state, a post-air grace period and weekend behaviour — resolved once and consumed everywhere.

Layered legacy URL continuity Pattern middleware for whole families of legacy URL shapes, plus an administrator-managed exact-path redirect table with click counting for the long tail, so a very large body of indexed URLs keeps resolving after the platform change.

Structured-data and metadata parity A centralised builder emitting titles, descriptions, canonicals, social cards and structured data in a shape that deliberately matches what the previous site produced, protecting search presence through the transition.

Scheduler-driven publication state Per-minute promotion of scheduled content and automatic post-air archiving, so publication state is correct without anyone being at a keyboard at air time.

Database-wide legacy URL scanning A scanner that introspects every text column on a connection to locate embedded legacy URLs, driving bulk rewrites onto cloud storage paths — an evidence-based answer to a problem usually attacked by guesswork.

Collision-safe person-record merging A single merge service that re-points favourites, saves, follows, push tokens, preferences and pivot rows from a duplicate person record onto the surviving one, shared by both a deduplication command and an administrative import.

Preference-aware scheduled push A configuration-declared notification catalogue mapping types to deep-link payloads and user preference categories, delivered by independent timezone-pinned schedules and deep-linking correctly from a cold start.

Affiliate station directory with bulk operations A filterable public directory of carrying stations, maintained through CSV import and export and bulk administrative actions.

Hand-built CMS with editorial continuity A bespoke administrative interface covering every content type, including duplicate-to-draft behaviour modelled on the publishing mechanics editors already knew, with soft deletes, authorship tracking and activity logging applied across the board.

Hybrid mobile shell with true session continuity A native tab shell around the web experience with native push, sharing and splash, a JavaScript bridge for login, logout and content-change events, and cookie clearing across both platform cookie stores so logout genuinely logs out.

Technical Architecture

Web application layer. A Laravel monolith serving four surfaces from one codebase: the public website rendered in Blade with a Tailwind and Vite asset pipeline, a hand-built administrative CMS under its own prefix, a small REST API, and a mobile route group on a separate authentication guard.

Domain services layer. Non-trivial logic is extracted into services: broadcast-clock and hero-episode resolution, push dispatch, cloud storage and image handling, person-record merging, content duplication, navigation composition and the legacy-URL scanner.

Scheduled operations layer. Content lifecycle and outbound messaging run as scheduled console commands on a fixed broadcast timezone — scheduled publishing, post-air archiving, four independent notification schedules and periodic sitemap cache invalidation — keeping all of it out of the request path.

Request middleware layer. A deliberately deep stack handling pattern-based legacy redirects, administrator-managed exact-path redirects with click tracking, URL canonicalisation, a custom URL generator, and the session-continuity path used by the mobile shell.

Migration tooling layer. A large body of console commands for import, backfill, normalisation, deduplication and repair, plus readers for the legacy platform's database, used to move a long-lived corpus without losing dates, authorship, media or identity.

Identity layer. Two authentication guards over two models: internal editors and administrators, and public-facing people and supporters — the latter covering hosts, guests, authors, on-air talent, staff and registered users, with roles, directory ordering and merge handling.

Data layer. A relational database with content, campaign, directory, governance and engagement tables, polymorphic favourites, saves and follows, soft deletes throughout, authorship tracking on every record, activity logging, and dedicated migrations adding composite indexes for the platform's real query shapes.

Media layer. Cloud object storage with date-partitioned paths, server-side image handling, signed-URL delivery and in-process signed-URL caching.

Mobile layer. A React Native tab shell around web views, with native push through Firebase Cloud Messaging and a local notification library for display and press handling, native sharing, cookie management across both platform stores, and a bridge carrying authentication and content events between native and web.

Flow: Mobile shell (native tabs, push, share) ⇄ Web application (public site + CMS + API) → Domain services → Relational database → Cloud object storage → Scheduled commands (publishing, archiving, notifications, cache invalidation) → Push delivery and email

Technology Stack

Category Technology
Backend language PHP 8.1+
Backend framework Laravel 10
Database MySQL, with dedicated performance-index migrations
Templating & assets Blade, Vite, Tailwind CSS
Rich text editing Tiptap (with table, image, link, alignment and image-resize extensions)
API authentication Laravel Sanctum tokens, plus separate session guards for administrators and supporters
Object storage Google Cloud Storage with signed-URL delivery
Push notifications Firebase Cloud Messaging (native and web), via the Firebase Admin SDK for PHP
Document generation DomPDF
Audit trail Activity logging package, with soft deletes and authorship tracking across all tables
SEO Metadata, OpenGraph, Twitter card and JSON-LD generation through a centralised builder
Monitoring Sentry SDKs on both backend and browser, plus in-app log inspection
Form protection Google reCAPTCHA on public forms
Scheduling Laravel scheduler with timezone-pinned cron expressions
Code quality Laravel Pint, PHPUnit
Deployment Capistrano deployment driven by CircleCI, branch-gated per environment
Mobile framework React Native with React 19 and TypeScript
Mobile navigation React Navigation bottom tabs with a WebView content surface
Mobile push Firebase Cloud Messaging with a local notification library for display and press handling
Mobile platform features Cross-store cookie management, native share sheet, splash screen, gesture handling, Reanimated, SVG rendering
Mobile monitoring Sentry React Native

Technical Challenges & Solutions

Challenge Our Approach
Two decades of indexed URLs at risk in a platform change Three layers of continuity: pattern middleware mapping whole families of legacy URL shapes, an administrator-managed exact-path redirect table with click and last-hit tracking for the long tail, and per-content-type sitemaps with scheduled cache invalidation — plus structured-data output deliberately shaped to match what the previous site emitted.
Content bound to a broadcast clock rather than a calendar Dedicated resolution services owning air time, a post-midnight day boundary, live state, a post-air grace period and weekend behaviour, with an injectable notion of "now" so the logic is testable. Every consumer asks the same service, so the rules cannot diverge.
Legacy media URLs embedded in free text across an unknown set of columns A scanner that introspects every text column on a database connection and searches it directly, then drives bulk rewrites onto cloud storage paths from the result — replacing guesswork with evidence.
The same person existing as several records from several sources A single collision-safe merge service that re-points every reference — favourites, saves, follows, push tokens, preferences, pivot rows and embedded data — onto a surviving record, shared by both a deduplication command and an administrative import so the logic exists once.
Publication state depending on someone being present at air time Scheduled publishing promoted by a per-minute command and automatic archiving a fixed window after air, so state is correct regardless of traffic and editors schedule in advance.
Notifications needing to be timely, preference-aware and correctly targeted A configuration-declared notification catalogue mapping each type to its deep-link payload and its user preference category, delivered on independent timezone-pinned schedules, deep-linking correctly even when the app is launched from a cold start.
Serving one content model to both web and mobile without building it twice A hybrid native shell around the web experience, with a JavaScript bridge carrying login, logout and content-change events, and native implementations only where native is required — push, tabs, sharing, splash and cookie control.
An app store rejection for unresponsive behaviour A documented, fix-by-fix remediation of the mobile shell: removing a timing-dependent navigation interception, replacing an input-blocking modal loader with a non-blocking overlay, debouncing view remounts triggered by concurrent events, moving navigation calls out of mid-navigation state callbacks, and adding a load timeout that cannot leave the shell stuck.
Replacing several plugins with one coherent platform without retraining the editorial team A hand-built CMS shaped around the team's existing workflow, including duplicate-to-draft behaviour deliberately modelled on the publishing mechanics they already knew, so a disruptive platform change did not also become a disruptive process change.

Security & Reliability

Separated identity models. Administrators and public supporters authenticate through distinct guards over distinct models, so the two populations cannot be conflated by an authorisation mistake.

Layered request protection. CSRF protection, signed-route validation, trusted-host and trusted-proxy configuration, explicit cross-origin policy, and reCAPTCHA on public-facing forms.

Complete change history. Activity logging on content changes, authorship tracking on every record, and soft deletes across all tables, so an editorial change can be traced and an accidental deletion recovered.

Controlled media delivery. Assets are served from cloud object storage through time-limited signed URLs rather than being exposed as permanently public objects.

Deliberate session control on mobile. Logout clears cookies across both platform cookie stores rather than only the one the framework manages by default — the difference between appearing to log out and actually logging out.

Resilient third-party handling. Remote assets are proxied with validation, caching and a graceful failure path, so an upstream provider's outage degrades one component rather than breaking the page.

Production monitoring. Error monitoring on both the backend and the browser, with in-application log inspection for operational diagnosis.

Recoverable content operations. Bulk actions, imports and merges operate through soft deletes and shared, collision-safe services rather than direct destructive queries.

Scalability & Performance

Indexes matched to real query shapes. Dedicated migrations add composite indexes covering the status, publication-date and featured combinations that every listing, archive and feed actually uses — rather than indexing by intuition.

Cached generated output. Sitemaps are cached with scheduled invalidation, and proxied remote assets are cached with long lifetimes, so crawler traffic and third-party latency do not reach the database or the upstream provider on every request.

Memoised signed URLs. Signed object URLs are cached in-process, so an image-dense listing page signs each object once rather than repeatedly.

Work moved out of the request path. Publishing, archiving, notification dispatch and cache invalidation all run as scheduled commands, keeping user-facing requests free of batch work.

Media served from object storage. Images and documents are delivered from cloud storage with date-partitioned paths, keeping the application server out of the asset path entirely.

Stateless-friendly application tier. Externalised media and token-based mobile authentication keep application instances free of local state.

A deliberately thin mobile client. The shell carries almost no business logic, so content features ship without an app release, and startup cost stays low.

Mobile responsiveness engineered explicitly. Debounced view remounting under concurrent events, a non-blocking loading overlay, and a load timeout that guarantees the interface recovers even when the network does not.

Business Outcomes

  • The organisation publishes on its own schedule. Broadcast-clock logic and scheduled publishing mean episodes go live, lead the homepage and archive themselves without anyone intervening at air time.
  • Search visibility survived the platform change, because legacy URL continuity and structured-data parity were built as an engineering subsystem rather than checked at the end.
  • A long-lived legacy corpus moved intact, with media, dates, authorship and person records normalised through purpose-built tooling instead of manual cleanup.
  • Several plugin vendors were replaced by one coherent platform, with redirects, metadata, forms, opt-ins and media all operating on the same content model.
  • The organisation now has a direct mobile channel, including the notification a broadcaster most needs to send — the programme is about to start.
  • Supporters have a reason to return, through favourites, saved content, following individual contributors and control over what they are notified about.
  • Editors kept their working habits, because the new CMS was shaped around the publishing mechanics they already knew rather than around a framework's defaults.
  • Content operations are traceable and recoverable, with activity logging, authorship tracking and soft deletes applied consistently across every content type.
  • Public-record obligations are met inside the platform, rather than through separate uploads and manual pages.

Why it worked

Replatforming an organisation that has been publishing for two decades is not primarily a build problem. The new system is the easy half. The hard half is everything the old system accumulated: the URLs search engines have indexed, the media paths embedded in free text, the duplicate records created by five years of imports, and the working habits of a team that publishes every single day and cannot stop while you migrate.

Our team builds for that reality. We treated search-visibility continuity as a subsystem with three deliberate layers, not a redirect file written the week before launch. We wrote a scanner to find legacy URLs across every text column in the database rather than guessing which ones mattered. We put the awkward broadcast-clock rules into services with an injectable clock, so the logic that decides what the homepage shows at 1:47 a.m. on a Saturday is something you can test rather than something you find out about. And we modelled the new CMS on the publishing behaviour the editorial team already had, because the fastest way to fail a replatform is to make the people who use it every day slower at their jobs.

Our teams work across Laravel and modern PHP, large-scale content migration, SEO-critical replatforming, custom CMS design, scheduled and notification-driven systems, cloud media delivery, and cross-platform mobile applications — with the judgement to know when a hybrid shell is the right answer and when it is not.

Final Summary

A national non-profit broadcaster was running a daily live programme, an affiliate station network and a large content library on a WordPress installation assembled from plugins. The CMS could not express a broadcast schedule, so editors compensated by hand at air time every day. Two decades of indexed URLs made any platform change genuinely risky. And there was no mobile channel at all — no way to tell an audience the programme was about to start.

Our team replatformed the organisation onto a purpose-built Laravel application with a hand-built CMS. Broadcast-clock logic — air time, a post-midnight boundary, live state, a post-air grace period, weekend behaviour — was resolved once in dedicated services and consumed everywhere, with scheduled commands promoting and archiving content so publication state is correct without anyone being present. Search-visibility continuity was built as three deliberate layers: pattern middleware for legacy URL families, an administrator-managed redirect table with click analytics for the long tail, and structured-data output shaped to match what the previous site emitted. The legacy corpus was moved with real tooling, including a scanner that reads every text column in the database to find embedded legacy URLs and a collision-safe merge service for duplicate person records.

Around that sits what the organisation actually needed: a searchable affiliate station directory maintained by bulk import, campaign and advocacy content, downloadable resources, public-record publishing, supporter personalisation through favourites, saves and follows, and a configuration-declared notification catalogue delivering preference-aware push on timezone-pinned schedules. A hybrid mobile application wraps the web experience in a native shell with push, sharing and genuine session control — one content model, one release cycle, native only where native earns its place. The organisation now publishes on its own schedule, keeps the search presence it spent twenty years building, and can reach its audience directly for the first time.

01 — Questions

asked about this kind of project

How do you migrate from WordPress to a custom platform without losing search traffic?

By treating it as a subsystem rather than a launch task. You need at least three layers: pattern-based rules that map whole families of legacy URL shapes, a database-backed redirect table for the long tail that patterns cannot express, and metadata and structured-data output shaped closely enough to the previous site that search engines read continuity rather than a new property. Add click tracking on the redirect table, because the URLs still being requested six months later are the ones you did not predict.

When is a hybrid WebView mobile app the right choice?

When the product is fundamentally content, the web experience is already good, and the native requirements are specific and bounded — push notifications, tabs, sharing, splash, session control. You get one content model and one release cycle for content features. It is the wrong choice when the app needs heavy offline behaviour, complex device integration or interaction performance that a web view cannot deliver. The failure mode is the middle ground: a shell that keeps growing native logic until you have two clients and the disadvantages of both.

How do you model content that depends on a broadcast schedule?

Put the rules in one service with an injectable clock. Air time, day boundaries, live windows, grace periods and weekend behaviour interact in ways that produce genuinely surprising cases, and if that logic is spread across controllers and templates it will drift and you will find out from an audience member. One service, one set of rules, testable at any hour of any day without waiting for that hour to arrive.

What is the hardest part of migrating a long-lived content estate?

The things nobody catalogued. Media URLs embedded in free-text fields, dates that mean different things in different tables, the same person entered five times from three sources. We built a scanner that reads every text column in the database to find embedded legacy URLs, and a single merge service that re-points every reference from a duplicate record onto a surviving one. Both exist because guessing which columns matter is how migrations quietly go wrong.

Should notification behaviour be code or configuration?

Configuration, as far as possible. Declaring notification types with their deep-link payloads and their mapping to user-facing preference categories means adding a type is a config change and a scheduled command, not a new bespoke delivery path. It also keeps preference filtering consistent, which matters: a user who has turned a category off must stay off across every send, and that is difficult to guarantee when each notification type has its own code path.

Why build a CMS by hand instead of using an admin framework?

Usually you should not — admin frameworks are excellent and most projects should use one. It is justified when the editorial workflow is unusual and the team is publishing daily. Here, publishing behaviour was shaped by a broadcast schedule and by habits built over years on the previous platform, and matching those habits closely was worth more than the time an admin package would have saved. A replatform is disruptive enough without also making the daily job slower.

How do you keep content publication correct without manual intervention?

Let the scheduler own it. A command promotes scheduled content on a short interval, and another retires time-bound content once its window has passed. State is then correct regardless of traffic, and editors schedule in advance instead of being present at the moment something must go live — which, for a daily broadcast, is the difference between a workflow and an obligation.

What should be built first in a replatform?

The migration and continuity tooling, before the features. It is the least visible work and it decides whether the project succeeds: if legacy URLs break, or content arrives with the wrong dates and duplicated people, the new platform is judged on those failures no matter how good it is. Build the scanner, the importers, the merge logic and the redirect layers first, then build the product on top of data you can trust.

03 — Similar project?

describe what is different about yours

Build yours.

Tell me what exists, what you need and the deadline. Fixed price for a defined scope, or hourly from $10 with a written estimate first.

Ahmedabad, India · IST (UTC+5:30) · --:-- IST · Mon–Fri 09:00–18:00 IST · US & EU overlap daily

No newsletter, no CRM. Just a reply.