What the audit found
Before planning anything, I read the existing site and the Review Engine. Four findings change the shape of this project, and one of them is urgent.
The current live site publishes fabricated evidence. The AI builder invented the proof that matters most:
- Five toppers are fictional. Aarav Sharma 98.2%, Priya Gupta 97.6%, Rohan Mehta 97.0%, Sneha Jain 96.4%, Karan Verma 95.8% — generic names attached to generated portrait images hosted on the builder's CDN.
- Three testimonials are fictional — "Anushka G.", "Yash B.", "Riya K.", complete with invented scores and invented quotes.
- Faculty bios contain unverifiable claims — "students consistently break previous marks records", "known across Jaipur as…", plus invented nicknames ("The Concept King", "The Corporate Link") and a Ph.D. credential.
- The page title claims "Best CA & Commerce Coaching in Jaipur" — a superlative with no substantiation.
This is a live liability, not just a quality problem. A parent who recognises a stock face, or a competitor who checks a topper's name, damages the institute permanently. My recommendation is to take the fabricated sections down now, ahead of the rebuild, rather than waiting for launch. Section 22 lists what must replace them.
The homepage is hardcoded with BATCH_DATE = "21st June 2026" and runs a scrolling banner reading "NEW BATCH STARTS 21ST JUNE 2026 — Limited seats". That date passed two months ago. This is exactly the failure the new architecture must prevent: a fact that changes on a cadence was written into source code, so it silently went stale. Section 11 makes dates like this a database record with a validity window, and Section 12 gives the institute the ability to change it without a developer.
The tp-reviews-engine is substantially more mature than the brief assumes. It is a complete, tested system (2,296 offline tests, 26 merged phases) that harvests Google reviews on a schedule, reconciles them against a private ledger, and publishes a small static JSON payload with a versioned public schema. It ships a frontend/ integration kit including a Next.js App Router recipe that reaches zero client-side requests.
This means the brief's proposed chain — Review Engine → Backend → Database → Website — has one hop too many. The engine is the backend. Copying reviews into our own Postgres would fork the source of truth and defeat the engine's publish gate. Section 13 sets out the corrected architecture.
The Commerce Insight config exists as clients/_commerce-insight.config.json but is inert twice over: the leading underscore keeps it out of the registry, and enabled is false. Every identifying value is still a REPLACE- placeholder. Activating it needs client action, not developer action — see Sections 13 and 22.
Facts carried over from the old site
These were in the previous build. They look genuine, but given what else that build invented, every one needs re-confirmation in writing before it goes near the new site.
| Fact | Value found | Note |
|---|---|---|
| Address | Near Pannadhay Circle, Pratap Nagar, Jaipur, Rajasthan – 302033 | Must match the Google Business Profile character for character |
| Phone 1 | +91 95090 17150 | Used as the WhatsApp number |
| Phone 2 | +91 96641 10109 | Two numbers weakens local SEO — nominate one primary |
vanmogaming333@gmail.com | Blocker A personal gaming handle. Unusable for an institute. Needs a domain mailbox. | |
| Courses | Class XI, Class XII (CBSE/RBSE), CA Foundation, CA Intermediate, CMA Foundation & Inter, XII+CAF combo | Broader than the brief's list, which omitted CA Inter and CMA |
| Faculty | Vishnu Sir (Accounts), Dr. Aniket Sir (Economics), CA Akhil Sir (Business Studies) | Names plausible; the bios around them were embellished |
| Branches | 1 — Pratap Nagar | Single-location local SEO strategy |
The brief states the logo has been provided. No logo file is present in the project folder and none exists elsewhere on this machine. Section 8's colour direction is therefore built from the brief's written description of the logo (deep academic blue, white, orange accent, book / graduate / nib symbolism) and will be re-derived from the actual artwork the moment it arrives. The topper images in the old build are remote assets on the builder's CDN and will disappear when that project is deleted — they are not recoverable, and should not be recovered.
The four decisions that shape everything after this
Evidence is the product. Design is the delivery.
The site's persuasive power comes from real results, real faces, real reviews. Every section is designed to degrade honestly when the evidence isn't there yet, so we can launch before all of it arrives.
The Review Engine stays the source of truth for reviews.
We read its published payload server-side and render it in our own components. We never copy it into our database, and the visitor's browser never contacts Google.
Content that changes gets a database. Content that carries design stays in code.
Results, toppers, announcements and enquiries go to Postgres with an admin UI. Course and faculty pages stay typed content in the repo, because a generic CMS shape would flatten exactly the layouts that make this site feel bespoke.
WhatsApp is the primary conversion, not the form.
For a Jaipur institute reached from Instagram and Google on a phone, the realistic next step is a WhatsApp message. The form exists for parents who prefer it; the architecture captures both as leads.
Final recommended sitemap
The brief proposed roughly fourteen top-level areas. I recommend nine. Thin pages hurt both trust and search rankings, and several of the proposed pages would launch nearly empty.
/ Home ├── /about Story, founder, philosophy, why CI ├── /courses All programmes │ ├── /courses/class-11-commerce │ ├── /courses/class-12-commerce │ ├── /courses/ca-foundation │ ├── /courses/ca-intermediate │ └── /courses/cma ├── /results Results + toppers + student stories │ └── /results/[year] e.g. /results/2026 ├── /faculty All mentors │ └── /faculty/[slug] Individual profile ├── /reviews Google reviews, from the engine ├── /videos YouTube library ├── /gallery Institute life ├── /contact NAP, map, directions, hours └── /admissions Enquiry + admission process Utility /privacy · /terms · /sitemap.xml · /robots.txt · /opensearch /admin/* Authenticated, noindex Deferred until content exists (Section 23, Phase 6) /resources · /resources/[slug] Notes, exam updates, articles
What I merged, and why
| Brief proposed | Recommendation | Reason |
|---|---|---|
| /results and /toppers as separate pages | Merge into /results | A topper is a result. Splitting them halves the evidence on each page and creates two pages competing for the same search query. One page with a toppers band above a full results table is stronger on both counts. |
| /students, /students/testimonials, /students/stories, /reviews | /reviews for Google reviews; student stories live as a band on /results and full stories at /results#stories | The brief is right that owned stories and Google reviews are different things. But four pages of social proof is three too many when the institute currently has zero written stories ready. Stories earn their own route once there are five or more. |
| /youtube | /videos | Names the content, not the platform. Survives the institute later posting to Instagram, and reads as a library rather than a follow request. |
| /enquiry and /admission | /admissions | One page. The enquiry form is the bottom half of the admission process page — the reader wants the process before the form. |
| /resources | Deferred | An empty resources page is worse than no resources page: it signals abandonment to visitors and gets crawled as thin content. Build it when there are at least six items to put in it. |
| Announcements page | No page | Announcements are a homepage band and an admissions-page notice, driven by the same database record. A dedicated page for two announcements is not a page. |
Seven items in the primary bar — About · Courses · Results · Faculty · Reviews · Videos · Contact — plus a single visually distinct Enquire action. Courses is the only dropdown. Gallery lives inside the About menu and the footer, because it is a page people browse after they are already interested, not one they navigate to first.
Complete page inventory
Every route, its rendering strategy, its data source, and its single conversion goal. "Blocked" means the page cannot be built to a launchable standard until the client input in Section 22 arrives.
| Route | Render | Data source | Primary goal | State |
|---|---|---|---|---|
/ | ISR 1h | Config + DB + engine payload + YouTube | Understand & segment | Needs evidence |
/about | Static | MDX | Trust the people | Blocked — founder story |
/courses | Static | Typed content | Choose a programme | Needs fees/batches |
/courses/[slug] ×5 | Static | MDX + typed frontmatter | Enquire about this course | Needs syllabus detail |
/results | ISR 1h | Postgres | Believe the outcomes | Blocked — real results |
/results/[year] | ISR 1h | Postgres | Believe the outcomes | Blocked |
/faculty | Static | Typed content | Trust the teaching | Blocked — photos, real bios |
/faculty/[slug] | Static | MDX | Trust one mentor | Blocked |
/reviews | ISR 6h | Engine payload | External validation | Needs engine activation |
/videos | ISR 6h | YouTube Data API | Sample the teaching | Needs channel ID |
/gallery | Static | Curated manifest | Feel the place | Blocked — photography |
/contact | Static | Config module | Call, WhatsApp, or visit | Data available |
/admissions | Static + action | Config + POST route | Submit enquiry | Needs process detail |
/privacy, /terms | Static | MDX | Compliance | Needs legal entity name |
/admin/* | Dynamic | Postgres | Self-service updates | Build task |
Nine of fifteen route groups are blocked or partially blocked on client input, and none of them are blocked on engineering. That ratio is the single most important fact in this plan, and it is why Section 25 front-loads the content collection rather than the build.
Homepage architecture
Section by section, with the job each one does and how it behaves when the evidence behind it is missing. The order follows the brief's recommended flow with two changes, both argued below.
| # | Band | Job | Content | If data is missing |
|---|---|---|---|---|
| 1 | Announcement bar | Time-sensitive news | Active Announcement record with a validity window | Band does not render. No expired batch dates — the bug in Section 0 becomes structurally impossible. |
| 2 | Header | Navigate + convert | 7 links, Enquire button | — |
| 3 | Hero | Say what this is, for whom | Headline, one-line positioning, two CTAs, one real photograph | Photo slot falls back to a typographic composition on deep blue — never a stock classroom. |
| 4 | Credibility strip | Immediate proof | Google rating + review count live from the engine, years teaching, programmes offered | Each metric renders independently; unverified ones are simply absent. No "success rate" — see below. |
| 5 | Course finder | Segment the visitor | 5 course cards: who it's for, subjects, mode, next batch | Cards render without batch dates rather than with stale ones. |
| 6 | Results & toppers | The core proof | Top achievers band + headline numbers + link to /results | Entire band hidden. An empty results band is worse than none. |
| 7 | Why Commerce Insight | Differentiate | 4 pillars, each tied to something observable | Only confirmed pillars render. |
| 8 | Faculty | Put faces to teaching | 3 mentor cards → /faculty | Monogram tiles in brand blue if photos are pending — deliberate, not broken. |
| 9 | Google reviews | Independent validation | Rating summary + 3 recent reviews + "Review us" CTA | Band hidden until the engine publishes. Degraded states in Section 13. |
| 10 | Videos | Sample the teaching | 3 latest videos, thumbnail + title | Band hidden if the channel has fewer than 3 videos. |
| 11 | Student stories | Human depth | 1–2 featured stories | Band hidden. |
| 12 | Gallery strip | Show the place | 5–6 images → /gallery | Band hidden. |
| 13 | Location | Prove it's real and near | Map, address, hours, directions, call, WhatsApp | — |
| 14 | Final CTA | Convert | Enquire / WhatsApp / Call | — |
| 15 | Footer | Complete the map | Links, NAP, social, legal | — |
Two changes from the brief's flow
Results moved above Why Commerce Insight. The brief places the value-proposition pillars before results. But pillars are claims and results are evidence, and evidence read first makes the claims that follow credible. Reversed, the pillars read as marketing the reader has to take on faith.
Videos moved above student stories. Video is the one piece of proof where a visitor can judge teaching quality directly, in seconds, without trusting anyone's word. It belongs higher than written stories.
The brief lists "Success Rate" among the credibility metrics. I recommend dropping it entirely. There is no standard definition — percentage clearing CA Foundation, percentage above 90%, percentage passing? — so any number invites the question "of what?", and the honest answer is usually smaller than the headline suggests. It is the metric most likely to be challenged and least likely to survive the challenge.
Replace it with the strongest number available: the live Google rating and review count read from the engine. Unlike every other statistic on the page, a visitor can verify it in one tap. That is a genuine differentiator over every competitor site in Jaipur, and it costs nothing because the engine already produces it.
Page-by-page UX strategy
What each page is for, what it must do differently from every other page, and the one decision it is trying to unblock.
/about — the founder page in disguise
The brief is right that a 300-word paragraph fails here. But the mistake most institute About pages make is describing the institute when readers want the person. Structure: a short statement of what Commerce Insight is → the founder's story at real length, with a photograph → teaching philosophy stated as method, not adjectives → the four differentiators → faculty preview → CTA. The YouTube channel belongs here too, as evidence the founder teaches publicly and can be judged before enrolment.
/courses — a comparison tool, not a list
A visitor arriving here is choosing between two or three options, so the page must support comparison rather than scrolling. Cards carry the same four fields in the same positions — who it's for, subjects, mode, duration — so the eye can scan a column. A short "not sure which?" prompt routes the genuinely undecided to WhatsApp rather than leaving them to guess.
/courses/[slug] — the highest-intent page on the site
Follows the brief's structure with one addition: "Who this is not for" placed immediately after "Who this is for". Naming who should not enrol is the single cheapest trust signal available to an institute, it costs nothing, and no competitor does it. Sticky bottom enquiry bar on mobile, pre-filled with the course name so the lead arrives attributed.
/results — make it auditable
Toppers band, then a full filterable table by year, programme and board. Table over cards for the body of the data: a table looks like a record and cards look like marketing, and here the reader is deciding whether to believe you. Each entry carries year, programme, board and score. Student stories sit at the bottom as depth for readers who want it.
/faculty — qualifications, not adjectives
Given Section 0's findings, this page gets the strictest content rule on the site: every claim must be a verifiable credential or an observable fact. "Ph.D., Economics, [university]" is publishable. "The Academic Edge" is not. Each profile carries subjects taught, qualifications, years teaching, and optionally a short statement in the mentor's own words.
/reviews — show the engine's honesty
Rating summary, distribution bars, full review list with owner replies, and — unusually — a quiet freshness line reading "Last updated [date]". Most sites hide this. Showing it is a trust signal precisely because it proves the reviews are synced rather than typed.
/videos, /gallery, /contact, /admissions
/videos: latest first, filtered by subject only once each filter has three or more videos. /gallery: masonry grid, category filters, keyboard-navigable lightbox, every image with real alt text. /contact: actions before information — call, WhatsApp, directions as large touch targets at the top, then hours, then map, then form. /admissions: process as a numbered sequence (numbered because it genuinely is one), then fees if the client publishes them, then the form.
Student journeys
Three real paths, each with the moment it typically fails and the design response.
A — the Instagram tap (highest volume)
Instagram bio link → / → hero → course finder → /courses/class-12-commerce → sticky bar → WhatsApp, course pre-filled
Fails at: the hero, on a mid-range phone over patchy mobile data. If the largest element takes four seconds, the visitor is gone before reading a word. Response: the hero is the performance budget's first claim — see Section 18.
B — the Google search (highest intent)
"ca foundation coaching pratap nagar" → /courses/ca-foundation (no homepage) → scan → /results → /reviews → back → enquire
Fails at: orientation. This visitor never saw the homepage, so a course page that assumes context loses them. Response: every course page opens by restating what Commerce Insight is in one line, and carries breadcrumbs and full contact access in the footer. Every deep page is treated as a potential entry point.
C — the researcher
Multiple visits over days → / → /faculty → /videos → watches → /results → /reviews → /gallery → /admissions → form
Fails at: nothing sudden — this journey fails by attrition when a page has nothing new to offer. Response: every page ends with a contextual next step rather than a generic CTA, and the video library gives this visitor a reason to return before deciding.
Parent journeys
Parents are usually the decision-makers and almost never the first visitors. They arrive second, sceptical, and looking for different things.
D — the verification visit
Student shows parent → parent opens later, alone → / → /faculty → /results → /reviews → /contact → calls (does not fill the form)
What a parent checks, in order: Is this a real place with a real address? Who teaches? What have their students actually scored? What do other parents say? Can I speak to someone now?
Design response. Four things matter for this visitor:
- The phone number is always reachable — in the header on desktop, in a sticky bar on mobile. Parents call. They do not fill forms.
- Faculty credentials are legible without jargon — qualification, subject, years teaching, stated plainly.
- Results read as a record, not a poster — the table format in Section 4 exists for this reader.
- Google reviews carry more weight than testimonials — a parent knows the institute cannot edit them. This is the strongest argument for prioritising the engine integration.
E — the WhatsApp forward
Parent receives a forwarded link → opens in the WhatsApp in-app browser → lands mid-page → judges in ~5 seconds
Fails at: the link preview and the in-app browser. A forward with no image and a truncated title reads as spam. Response: every page gets a deliberate Open Graph image and title (Section 17), and every page is verified in the WhatsApp and Instagram in-app browsers — not just Chrome — because that is where a large share of this audience actually reads the site.
Students respond to energy and momentum; parents respond to restraint and evidence. These pull in opposite directions, and most coaching sites resolve it by shouting, which wins the student and loses the parent who holds the money.
Resolution: the design stays calm and the evidence does the persuading. Energy comes from photography, real faces and real numbers — not from colour saturation or motion. This is also precisely what the brief's "premium, not flashy" direction asks for, so the two constraints agree.
Conversion strategy
One primary action per page, chosen to match where the reader actually is, plus a persistent low-friction escape hatch.
| Page | Primary | Secondary | Why this one |
|---|---|---|---|
/ | Explore Courses | Talk to Us | Nobody enrols from a homepage. The job is segmentation. |
/courses | View course | Not sure? WhatsApp | Still choosing. |
/courses/[slug] | Enquire about this course | Call | Highest intent on the site. Pre-fill the course. |
/results | Explore Courses | Enquire | Just convinced by evidence — send them to choose. |
/faculty | Explore Courses | Watch on YouTube | Trust the person, now find their programme. |
/reviews | Enquire Now | Leave a Google review | Peak trust. Also the right place to ask existing students for reviews — which feeds the engine. |
/videos | Subscribe on YouTube | Explore Courses | Low intent. A subscription is a realistic ask; enrolment is not. |
/contact | Call | WhatsApp / Directions | Someone on this page wants a human now. |
/admissions | Submit enquiry | End of funnel. |
The persistent action
A single floating WhatsApp button, bottom-right, on every page except /admin. One button, brand-coloured rather than WhatsApp green so it does not fight the palette, with an aria-label and a real touch target. Its message is pre-filled with the page context, so an enquiry from /courses/ca-foundation arrives already saying which course.
The brief says don't spam CTAs, and this is where most coaching sites fail: a CTA after every band trains readers to ignore all of them. The rule here is one primary action per screenful, maximum. If a band already sits next to a CTA, it does not get its own.
Measuring it
Without measurement none of the above is more than an opinion. Minimum instrumentation at launch: WhatsApp clicks by source page, phone-tap clicks, form submissions by source page, course-page depth, and video plays. Section 18 covers doing this with a privacy-respecting, lightweight analytics tool rather than the full Google Analytics bundle.
Brand & design direction
Derived from the brief's description of the logo, and to be re-derived from the artwork itself once it arrives. This document is set in the proposed palette and typography, so it doubles as the first proof of the system.
The positioning idea
The record, not the pitch. Commerce is a discipline of ledgers, statements and audited numbers — work where a claim is only worth what its evidence supports. That is also, precisely, the problem with the old site. So the design direction and the content-integrity requirement come from the same place: a site that looks like a record and reads like a record is both more honest and more persuasive than one that looks like an advertisement.
Practically, this means ruled tables over floating cards where the content is data; generous white space over decorative fills; typographic hierarchy over colour to signal importance; and orange used the way an accountant uses a highlighter — rarely, and always to mean something.
Colour
Blue and white dominate; orange is reserved. The discipline is a ratio: roughly 60% white/paper, 30% blue, under 5% orange. If orange ever appears twice in one viewport, one of them is wrong.
| Token | Light | Dark | Used for |
|---|---|---|---|
brand-900 | #0A2A5E | #93B9F2 | Headings, primary surfaces, footer |
brand-600 | #1D5BBF | #6FA0EA | Links, interactive states, focus rings |
brand-50 | #E9F0FC | #14243A | Tinted panels, active nav |
accent | #C85E08 | #EE9445 | Section eyebrows, active indicator, achievement marks — nothing else |
ink | #111C2B | #DEE7F4 | Body text — blue-biased near-black, not neutral grey |
muted | #576782 | #93A3BC | Secondary text, captions |
rule | #DBE3EF | #243146 | Borders, dividers, table rules |
paper / surface | #FFFFFF / #F3F6FB | #0A111B / #111A28 | Page ground and raised panels |
The neutrals carry a slight blue bias throughout rather than being pure grey — it is a small thing that makes the palette read as one considered system instead of brand colours dropped onto a default template.
These hex values are a considered starting point, not a match. Once the logo artwork arrives, the exact blue and orange are sampled from it and these tokens are corrected — a change in one file, because everything derives from the tokens. Contrast is re-verified after sampling, since brand oranges frequently fail AA on white and need darkening for text use while the original stays for fills.
Typography
Two families, three roles. Source Serif 4 for display and headings — academic weight and editorial authority without period costume; it is what makes the site read as an institution rather than a startup. IBM Plex Sans for body and interface — institutional, highly legible at small sizes on Android, and a genuine complement rather than a neutral default. IBM Plex Mono for labels, eyebrows, scores and tabular data, which also gives numbers a deliberate treatment — appropriate for a commerce institute where numbers are the proof.
Both families are on Google Fonts, cover Latin comprehensively, and are subset and self-hosted at build time (Section 18). Notably, IBM Plex also has a Devanagari cut, so a future Hindi surface would not need a new typeface.
Motion
Three effects, total: a short fade-and-rise on band entry (16px, 320ms, once); a 120ms elevation change on card hover; and a count-up on the credibility strip only. Everything respects prefers-reduced-motion. No parallax, no scroll-jacking, no page transitions — page transitions in particular cost real perceived performance on the mid-range Android devices most of this audience uses.
Design system
Tokens first, components second. Every value below lives in one Tailwind theme file so a brand change is a single edit.
Scale and spacing
A 4px base unit with a restricted spacing set — 4 8 12 16 24 32 48 64 96 128. Restricting the set is the point: unrestricted spacing is how a site drifts into looking assembled rather than designed. Section padding is 64px mobile, 96px tablet, 128px desktop. Container max width 1200px, gutters 20/32/48.
Type scale
| Role | Family | Mobile | Desktop | Weight / spacing |
|---|---|---|---|---|
| Display | Source Serif 4 | 36px | 60px | 700 / −0.02em / 1.05 |
| H1 | Source Serif 4 | 30px | 44px | 700 / −0.02em / 1.12 |
| H2 | Source Serif 4 | 24px | 32px | 700 / −0.015em / 1.2 |
| H3 | IBM Plex Sans | 18px | 20px | 600 / 1.35 |
| Body | IBM Plex Sans | 16px | 17px | 400 / 1.65 / max 68ch |
| Small | IBM Plex Sans | 14px | 14px | 400 / 1.55 |
| Label | IBM Plex Mono | 11px | 11px | 600 / 0.14em / uppercase |
| Data | IBM Plex Mono | 14px | 14px | 500 / tabular-nums |
Body text never goes below 16px on mobile — below that, iOS Safari zooms on input focus and the layout jumps.
Other primitives
- Radius:
4pxinputs and badges,8pxcards,12pxmodals. Nothing fully rounded except avatars. The brief's warning about "excessive rounded cards" is a real one — heavy rounding is what makes a site read as a template. - Elevation: three levels only, all blue-tinted rather than black, so shadows sit in the palette instead of muddying it.
- Borders: 1px
ruleis the default separator. This system prefers a rule to a shadow — it reads as a document. - Buttons: primary (blue fill), secondary (blue outline), tertiary (text + underline), plus WhatsApp and Call as distinct icon-led variants. Minimum target 44×44px. Every state defined: rest, hover, active, focus-visible, disabled, loading.
- Forms: labels always visible above the field — never placeholder-as-label, which fails accessibility and memory both. Errors sit below the field, in words, tied by
aria-describedby.
Component architecture
Three tiers. Server Components by default; 'use client' only where interaction genuinely requires it, which keeps the JavaScript budget in Section 18 achievable.
primitives/ no domain knowledge Button · Input · Select · Textarea · Badge · Tag · Card Dialog · Disclosure · Tabs · Skeleton · Rule · Prose patterns/ composition, still domain-free Section · SectionHeader · Grid · Lightbox · Carousel Breadcrumbs · Pagination · FilterBar · EmptyState domain/ knows Commerce Insight SiteHeader · SiteFooter · AnnouncementBar · Hero CredibilityStrip · CourseCard · CourseDetailLayout ResultsTable · TopperCard · FacultyCard · FacultyProfile ReviewCard · ReviewSummary · ReviewFreshness VideoCard · VideoGrid · StoryCard · GalleryGrid MapPanel · EnquiryForm · WhatsAppButton · StickyActionBar
Given how much of this site is blocked on client content (Section 2), the empty state is not an edge case — it is the launch state for several bands. Building it as a real component with a considered design, rather than an afterthought if branch, is what lets us ship on schedule and fill in evidence as it arrives. Every domain component declares what it renders with no data, and in most cases the answer is deliberately nothing at all.
Client-component boundary
Only these are client components: SiteHeader (mobile menu), FilterBar, Lightbox, EnquiryForm, Tabs, Dialog, and the count-up in CredibilityStrip. Everything else — including all review, video, result and course rendering — is server-rendered. Reviews and videos in particular arrive as HTML with zero client JavaScript, which is the entire performance argument for Next.js on this project.
Data model
PostgreSQL via Prisma. Deliberately small: only content that genuinely changes on a cadence, or that must be captured, gets a table.
| Model | Key fields | Why a table |
|---|---|---|
Enquiry | id, name, phone, email?, classLevel, courseSlug, message, source, sourcePage, status, notes, consentAt, ip, createdAt | Leads must be captured and worked. The only table that is unambiguously required. |
Topper | id, name, photoUrl?, score, scoreUnit, programme, board?, year, subjectScores[], highlight?, consentRef, published, sortOrder | Changes every results season. consentRef is mandatory — see Section 19. |
ResultRecord | id, name, score, programme, board?, year, published | The full results table behind the toppers band. |
Announcement | id, message, href?, startsAt, endsAt, priority, published | Fixes the expired-batch bug. The validity window means a stale notice removes itself. |
StudentStory | id, slug, studentName, programme, year, photoUrl?, challenge, journey, outcome, quote, consentRef, published | Editorial, added occasionally. |
Batch | id, courseSlug, startsAt, mode, seatsNote?, published | The other stale-date source. Course pages read the next published batch, or show nothing. |
AdminUser | id, email, passwordHash, role, lastLoginAt | Two or three rows, ever. |
AuditLog | id, actorId, action, entity, entityId, at | Who published which topper, and when. Cheap now, invaluable in a dispute about published marks. |
Deliberately not in the database
| Content | Lives in | Reason |
|---|---|---|
| Reviews | Engine payload, fetched server-side | Copying them forks the source of truth and bypasses the engine's publish gate. Section 13. |
| Videos | YouTube API, ISR-cached | YouTube is already the database. Section 14. |
| Courses | MDX + typed frontmatter in the repo | Course pages are long-form and design-bearing. A generic CMS shape would flatten exactly the layouts that make the site feel bespoke — and they change once a year at most. |
| Faculty | Typed content in the repo | Same reasoning. Three people. Version control is the right audit trail for credential claims. |
| Gallery | Curated manifest + optimised assets | Images need art direction and build-time optimisation; an upload form produces neither. |
| NAP, hours, socials | src/config/institute.ts | One typed module is the single source of truth for address, phones, email, hours and social links — consumed by the footer, contact page, map, and schema.org output. This is what makes the NAP consistency in Section 17 structural rather than a matter of remembering. |
Admin architecture
The brief wants a full CMS across twelve content areas. I recommend building roughly a third of that, and explaining the omission rather than quietly shipping it.
A bespoke CMS for twelve content types means twelve CRUD surfaces, a media pipeline, role management and a permanent security surface — for an institute that will realistically log in a handful of times a year. That is a maintenance liability disguised as a feature, and its most likely end state is an admin panel nobody logs into and a developer still making the edits.
Build the admin for content that actually changes on a cadence, and leave design-bearing content in code where it is version-controlled, reviewable, and cannot be visually broken by a well-meaning edit. This is the same split as Section 11.
| Area | Phase 1 | Rationale |
|---|---|---|
| Enquiries | Build | The reason the admin exists. List, filter, status, notes, CSV export. |
| Toppers | Build | The "beta, ek topper add karna hai" case from the brief, exactly. |
| Results | Build | Bulk entry each results season. |
| Announcements | Build | Directly prevents the expired-batch bug recurring. |
| Batches | Build | Same reason. |
| Student stories | Phase 2 | Wait until there are enough to justify the editor. |
| Courses, Faculty, Gallery | Code | Design-bearing, rarely changed, and better served by a reviewed pull request than a form. |
| Reviews, YouTube | Automatic | Nothing to manage — both sync themselves. |
| Resources | Deferred | Ships with the resources section, if it ships. |
How it works
Auth.js with credentials, Argon2id hashes, HTTP-only session cookies, and a rate-limited login. No public sign-up — accounts are seeded. Everything under /admin is protected in middleware, returns noindex, and is excluded from the sitemap. Photo uploads go to Vercel Blob through a server-issued token, with MIME and size validated server-side and every image re-encoded with sharp so an uploaded file is never served back as it arrived.
The topper flow the brief describes is the design target: log in → Add Topper → upload photo → enter marks → confirm consent on record → publish, with the site reflecting it within the hour via revalidation. The consent step is not optional — Section 19 explains why.
Review Engine integration
The most technically interesting part of this project, and the one where the brief's proposed architecture should be simplified rather than followed.
Corrected architecture
Brief proposed:
Google Business Profile → Review Engine → Backend → Database → Website
Recommended:
Google Business Profile
↓ scheduled harvest, offline
tp-reviews-engine → private ledger (state branch)
↓ publish gate
reviews.json (public data branch, HTTPS)
↓ server-side fetch, revalidate 6h
Next.js Server Component → HTML
↓
Visitor — zero client requests, never contacts Google
The engine already is the backend: it maintains the durable ledger, reconciles changes, and gates publication on invariants. Adding our own database between it and the site would fork the source of truth, bypass the publish gate the engine exists to enforce, and create a second thing that can be stale. We fetch the published payload and render it.
Implementation
The engine ships a Next.js App Router recipe. We follow its data contract but render with our own components rather than its reference DOM renderer — the bundled widget is deliberately unopinionated, and this site needs review cards that match the design system. What we do adopt from the kit is its safety discipline, which is well-reasoned:
- Review text is rendered as text, never as HTML. React escapes by default; the rule is that
dangerouslySetInnerHTMLnever touches payload data. This is the kit's first safety rule and it is correct. - Server-side fetch with
next: { revalidate: 21600 }— matched to the harvest cadence, not to visitor traffic. Per-request fetching would turn a static file into load proportional to traffic. - Reserve the container height before the payload renders, so no layout shift reaches Core Web Vitals.
- A failure is never the visitor's problem. If the fetch fails, the band does not render — no error text, no empty box.
Degraded states — and why we show them
The payload carries honest freshness signals that most integrations discard: notices, stats.completeness, and last_full_harvest_at. We surface them, quietly:
| Payload state | What the site shows |
|---|---|
completeness: "full" | Everything, plus "Synced from Google · [date]" |
notices: ["harvest_partial"] | Reviews render; the count is labelled "showing recent reviews" rather than presented as a total |
notices: ["awaiting_first_full_harvest"] | Band hidden entirely |
| Fetch fails or payload absent | Band hidden entirely |
The config has publish.schema_org: false. Keep it false, and do not emit AggregateRating or Review structured data for Google-sourced reviews. Google's structured-data guidelines restrict marking up reviews collected on another platform as your own, and misuse risks a manual action against the whole domain — a far larger loss than the star snippets are worth. The reviews still display beautifully on the page; they simply are not claimed as first-party structured data.
The engine is code-complete but not cleared for production. Recommended path is the Business Profile API adapter, not the DOM adapter — it is sanctioned by Google, free, complete, and needs no written authorisation record. To activate:
- Confirm who owns and has admin access to the Commerce Insight Google Business Profile.
- Complete Google's Business Profile API access request for that account.
- Obtain the
accounts/{id}/locations/{id}identifier and a refresh token (stored as a secret, never in the repo). - Rename
_commerce-insight.config.json, replace everyREPLACE-value including the exact listing name, setenabled: true. - Run a harvest, confirm the payload, then point the site at the published URL.
Steps 1–3 are client-dependent and are the long pole. The site is built to launch without them, with the reviews band simply absent until the payload exists.
YouTube integration
Dynamic, server-side, and quota-aware. The naive implementation exhausts the daily quota and is the most common way this integration fails in production.
YouTube channel ↓ playlistItems.list on the uploads playlist — 1 quota unit Server-side route, API key in env ↓ ISR, revalidate 6h + on-demand webhook Next.js Server Component → HTML ↓ Visitor — no YouTube JS until a video is actually played
The obvious call is search.list filtered by channel. It costs 100 quota units against a 10,000/day default. Called per request, a modest traffic day exhausts the quota and the videos band disappears — usually noticed weeks later.
The correct call is playlistItems.list against the channel's uploads playlist, which costs 1 unit, returns the same videos in upload order, and includes thumbnails and titles. Cached for six hours, that is four calls a day regardless of traffic.
- The API key never reaches the browser. Server-side only, in an environment variable, restricted to the YouTube Data API in Google Cloud.
- No YouTube iframe until play. We render our own thumbnail and title, and load the embed only on click — via
youtube-nocookie.com. A YouTube iframe costs roughly half a megabyte and sets tracking cookies before the visitor has asked for anything. - Graceful failure. API error, quota exhaustion, or fewer than three videos → the band does not render.
- Curated override. A small config array can pin specific videos ahead of chronological order — useful when the channel is young and the best video is not the newest.
Needs the channel URL or ID, and confirmation of how many videos exist. If the channel has fewer than six videos, I recommend not building /videos at launch — a three-video library reads as abandoned. Put the three videos on the homepage and About page instead, and add the route when the library justifies it.
Location & Maps
Pratap Nagar is a large sector. For a local institute, "can I find it and is it near me?" is a genuine conversion question, not a footer detail.
The location experience appears twice: as a band on the homepage and as the substance of /contact. Both render the same component reading the same config module, so the NAP cannot drift.
What it contains
- Actions before information. Get Directions, Call, WhatsApp as large targets at the top — Directions opens the native maps app on mobile via a
geo:-aware Google Maps link, which is what people actually want. - Full address exactly as it appears on the Google Business Profile, plus a human landmark line, because "Near Pannadhay Circle" is how people in Jaipur actually navigate.
- Opening hours as structured data and as readable text, with the current open/closed state computed server-side in IST.
- The map, loaded on interaction. A static styled map image or a lightweight placeholder renders first; the interactive embed loads on click. An eagerly-loaded Google Maps iframe is typically the heaviest thing on a page and it sits below the fold — there is no reason to pay for it on every visit.
Needs the Google Business Profile link or Place ID, exact coordinates, the address string as Google holds it, opening hours per day, and any landmark directions the institute gives over the phone. If a Maps Embed API key is used it is restricted by HTTP referrer; the no-key embed is also acceptable and avoids the key entirely.
Enquiry & lead system
Two capture paths, one lead record, and enough anti-abuse that the institute's inbox stays usable.
WhatsApp button ──┐ pre-filled, page-attributed
├──→ lead
Enquiry form ─────┘
↓ POST /api/enquiry (Zod validated, rate limited, honeypot)
Postgres Enquiry
↓
Email notification → institute inbox
↓
/admin/enquiries status · notes · CSV export
Fields
Name, phone, class/level, course of interest, and an optional message. Nothing else. Every additional field measurably reduces completion, and none of the others are needed to make a follow-up call. Email is optional, not required — this audience converts on phone.
Abuse and integrity
- Server-side validation with Zod, including a real Indian mobile format check. Client validation is for the user's benefit only and is never trusted.
- Honeypot field plus a minimum time-to-submit — catches most naive bots at zero cost to real users.
- Rate limiting by IP on the route. Cloudflare Turnstile added only if spam actually materialises; a challenge on every form is friction paid in advance for a problem that may not arrive.
- Consent line above the button, with the submission timestamp stored as
consentAt.
The brief raises automated WhatsApp follow-up. I recommend against it for now. The WhatsApp Business API requires Meta business verification, a template approval process and a messaging cost, and none of that is justified before there is lead volume to justify it. The wa.me deep link delivers the conversion path today, at zero cost and zero approval. Revisit when enquiries exceed what a person can follow up manually — the data model already captures everything an automation would need.
Email delivery via Resend or Postmark on the institute's own domain, with SPF and DKIM configured — notification emails sent from a Gmail address land in spam, which silently loses leads. The lead is written to the database before the email is attempted, so a mail failure never loses an enquiry.
SEO architecture
For a single-location institute, local search is nearly the whole game. National keyword ambition is a distraction; ranking for "commerce classes near Pannadhay Circle" is worth more than any generic term.
Priority
| Tier | Intent | Target |
|---|---|---|
| 1 | Local + programme | /courses/[slug] — e.g. CA Foundation coaching in Pratap Nagar, Jaipur |
| 2 | Local + general | / and /contact — commerce coaching Pratap Nagar |
| 3 | Brand | / — must rank first for "Commerce Insight Jaipur", unambiguously |
| 4 | Evidence | /results, /faculty — the queries a parent runs before deciding |
| 5 | Informational | Deferred with /resources |
Implementation
- Metadata per route via the Next.js Metadata API — unique title and description on every page, no templated duplication.
- Structured data:
EducationalOrganization+LocalBusinesson the homepage with NAP generated from the config module,Courseon each course page,BreadcrumbListsite-wide,VideoObjecton/videos. NoAggregateRatingorReview— Section 13. - NAP consistency is structural, because the footer, contact page, map and schema.org output all read the same typed module. Human error is designed out rather than watched for.
- Open Graph images per page, generated at build with the brand system. This matters more than usual here: Journey E in Section 6 is a WhatsApp forward, and the preview card is the first impression.
sitemap.xmlgenerated from actual routes,robots.txtdisallowing/adminand/api, canonical URLs everywhere, one<h1>per page with a real heading hierarchy beneath it.
The old site's title tag reads "Best CA & Commerce Coaching in Jaipur". Unsubstantiated superlatives are a poor ranking signal, they invite ASCI-style scrutiny of advertising claims, and — most practically — every competitor makes the same claim, so it differentiates nothing. Replace with something specific and defensible: the programmes taught and the locality served. Specificity ranks better than superlatives anyway.
Off-site, which matters more than anything on-site here: a complete and active Google Business Profile with correct hours, categories and photos, and a steady flow of genuine reviews. The /reviews page's "Leave us a Google review" CTA feeds this loop directly — and the Review Engine then displays the result. That circuit is the highest-leverage thing in this entire plan.
Performance
Budgets, not aspirations. The target device is a mid-range Android phone on a congested mobile network in Jaipur — not a laptop on office wifi.
| Metric | Budget | Measured on |
|---|---|---|
| LCP | < 2.0s | Moto G Power class, Slow 4G |
| INP | < 200ms | Same |
| CLS | < 0.05 | Same |
| JS shipped, homepage | < 120KB gzip | Build output |
| Hero image | < 120KB | AVIF, responsive srcset |
| Total homepage weight | < 600KB | First visit, cold cache |
How the budgets are met
- Server Components by default. Reviews, videos, results and course content ship as HTML with no accompanying JavaScript. This is the single largest lever.
- Fonts subset and self-hosted at build via
next/font,font-display: swap, preloaded for the two faces used above the fold. No render-blocking request to a third-party font host in production. - Images through
next/image— AVIF with WebP fallback, explicit dimensions on every image so CLS stays near zero, lazy below the fold, and the hero eagerly loaded withfetchPriority="high". - Nothing third-party loads eagerly. The map, the YouTube embed and any chat widget load on interaction. Between them these are typically 80% of a coaching site's weight.
- ISR everywhere — pages are served from cache and revalidated on a schedule, so a visitor never waits on the reviews or YouTube fetch.
- Analytics via a lightweight, cookieless tool (Vercel Analytics or Plausible, ~1KB) rather than GA4's ~45KB. If GA4 is required for the client's reporting, it loads deferred and after interaction.
Budgets are enforced in CI with Lighthouse CI on pull requests, so a regression fails the build rather than being discovered months later.
Security
Standard web hygiene, plus one obligation specific to this project that most coaching sites get wrong.
This site publishes photographs, full names and examination marks of Class 11 and 12 students. Most of them are under 18. Under India's Digital Personal Data Protection Act 2023, a person under 18 is a child, and processing a child's personal data requires verifiable parental consent. Publishing marks alongside a named photograph is exactly that kind of processing.
This is not a theoretical concern — it is the most likely source of a real complaint against the institute, and the current site publishes fabricated versions of precisely this data.
Requirements: a written parental consent form for every topper and student story before publication; a consentRef field on the record, which the admin publish action requires; a documented takedown route in the privacy policy; and a default of initials-plus-surname rather than full names where consent covers the result but not the identification.
Everything else
- Secrets in environment variables only — database URL, YouTube key, admin session secret, mail key, Blob token. Never in the repo, never in a
NEXT_PUBLIC_variable, and.env*in.gitignorefrom the first commit. A pre-commit secret scan is worth the five minutes it takes to configure. - Admin auth: Argon2id, HTTP-only
SameSite=Laxcookies, short session lifetime, rate-limited login with generic failure messages, no public registration, middleware-enforced route protection. - Every input validated server-side with Zod, including admin forms. Prisma's parameterised queries handle SQL injection; the discipline is to never bypass them with raw SQL on user input.
- Uploads: server-issued tokens, MIME and size validated server-side, re-encoded with
sharp, randomised filenames, served from Blob storage rather than the app origin. - Headers: a real Content-Security-Policy (no
unsafe-inline), HSTS,X-Content-Type-Options,Referrer-Policy, and a restrictivePermissions-Policy. Note the Review Engine's integration kit is explicitly built to work under a strict CSP — that alignment is deliberate on their part and we should not weaken the policy to accommodate anything else. - Dependencies: minimal surface, Dependabot enabled,
npm auditin CI.
Accessibility
Target WCAG 2.2 AA. Beyond the ethical case, an accessible site is a better site for the low-end devices and bright-sunlight conditions much of this audience browses in.
- Contrast verified against the final logo colours, not assumed. Brand oranges frequently fail AA as text on white — the plan is a darkened orange for text and the original for fills, verified rather than eyeballed.
- Semantic HTML throughout — real
<nav>,<main>,<table>for the results table, buttons that are buttons and links that are links. This single discipline resolves most keyboard and screen-reader issues before they exist. - Full keyboard operability — including the mobile menu, the gallery lightbox (Escape closes, arrows navigate, focus is trapped while open and restored on close), and every filter control.
- Visible focus states everywhere — a 2px brand-blue ring with offset. Never removed, only restyled.
- Forms: visible persistent labels, errors in text tied by
aria-describedby, error summary focused on failed submit, no colour-only error signalling. - Images: meaningful alt text written per image — "Class 12 students in the Pratap Nagar classroom", not "image1" — and empty
alton genuinely decorative ones. prefers-reduced-motionhonoured for all three motion effects, including the count-up, which jumps to its final value.- Skip-to-content link, correct language attribute, and a page title that identifies the page first.
Verification is automated (axe in CI) and manual — a full keyboard pass and a screen-reader pass on the homepage, a course page, the results table and the enquiry form. Automated tools catch perhaps 40% of real issues.
Responsive strategy
Mobile-first as a working method, not a label: every component is designed and built at 360px first, then allowed to expand.
| Breakpoint | Width | Represents |
|---|---|---|
base | 360–639 | The primary target. Most traffic. |
sm | 640–767 | Large phones, small phones landscape |
md | 768–1023 | Tablets |
lg | 1024–1279 | Laptops — where desktop nav appears |
xl | 1280+ | Desktop; container caps at 1200px |
Per-component behaviour
| Component | Mobile | Tablet | Desktop |
|---|---|---|---|
| Header | Logo + call icon + hamburger; full-screen drawer with Enquire pinned at the bottom, in thumb reach | Same | Full nav, one dropdown, Enquire button |
| Hero | Stacked, image below text, single-column CTAs | Stacked, wider | Split — text left, image right |
| Credibility strip | 2×2 grid | 4 across | 4 across, rules between |
| Course cards | 1 column, full width | 2 columns | 3 columns |
| Results table | Stacked cards — a 5-column table does not survive 360px | Scrollable table | Full table |
| Topper cards | Horizontal snap-scroll carousel | 2–3 columns | 4 columns |
| Faculty | 1 column | 2 columns | 3 columns |
| Reviews | 1 column, snap carousel on home | 2 columns | 3 columns |
| Gallery | 2-column masonry | 3 columns | 4 columns |
| Forms | Full width, 16px inputs, correct inputmode and autocomplete | Same | Two-column where fields pair naturally |
| Footer | Accordion sections | 2 columns | 4 columns |
On course pages, mobile gets a sticky bottom action bar (Enquire + WhatsApp) once the reader passes the hero — the single highest-value mobile pattern on this site, because it removes the scroll-back that kills enquiries.
Testing includes the in-app browsers — WhatsApp, Instagram and Chrome on real Android hardware, not just devtools emulation. Journeys A and E in Sections 5 and 6 both land there, and in-app browsers have their own viewport and font-scaling quirks that emulation hides.
Client inputs required
The critical path. Nothing here is blocked on engineering, and most of the site cannot reach launch quality without it. Grouped by urgency.
Blocking — needed before design can be finalised
| Item | Detail | Blocks |
|---|---|---|
| Logo artwork | Vector (SVG/AI/PDF) if possible; high-res PNG minimum. Horizontal and stacked variants, plus a monochrome version. | Colour tokens, header, favicon, OG images |
| Decision on fabricated content | Written confirmation to remove the invented toppers, testimonials and bios from the live site. | Ethical launch; also removes a live liability today |
| Real results data | Year, student name, programme, board, score. Whatever genuinely exists, however modest. | /results, homepage results band |
| Parental consent forms | Signed, per student, for every published name, photo and mark. | Any topper or story publication — Section 19 |
| Faculty facts | Real qualification, subjects, years teaching, per mentor. No embellishment. | /faculty, homepage faculty band |
| Founder's story | An interview is fine — we can write from a recorded conversation. | /about |
| Photography | Faculty portraits, classrooms, students at work, the building exterior and entrance. | Hero, faculty, gallery, OG images — the largest single quality risk |
Blocking — needed before launch
| Item | Note |
|---|---|
| Professional email address | vanmogaming333@gmail.com cannot appear on an institute's website. A domain mailbox is also required for reliable enquiry notifications (Section 16). |
| Domain name | Plus registrar access for DNS. |
| Confirmed NAP | Address, primary phone, WhatsApp number, hours per day — matching the Google Business Profile exactly. Nominate one primary phone. |
| Google Business Profile access | Owner/admin confirmation, profile URL or Place ID — and the API access steps in Section 13. |
| YouTube channel URL | Plus video count, to decide whether /videos ships at launch. |
| Course details | Per course: who it's for, subjects, duration, mode, batch timings, and whether fees are published. |
| Admission process | The actual steps, including whether a demo class is offered. |
| Legal entity name | For the privacy policy and terms. |
| Verified statistics | Only those the institute can substantiate. Any it cannot are simply not published. |
Useful, not blocking
Instagram and other verified social handles; student stories with consent; event and achievement photographs; FAQs from questions actually asked on the phone; any notes or study material for the deferred resources section.
Site quality will track photography quality more closely than any other single input. A half-day shoot with a local photographer, art-directed against a shot list we provide, is the highest-return spend on this project. Deliverables: three faculty portraits against a clean background in consistent lighting; four to six classroom images with students genuinely working; the building exterior and entrance for the location section; and a hero-candidate frame with room for text overlay. Until it exists, the design uses honest fallbacks rather than stock imagery — a stock classroom on a real institute's website is a trust cost, not a placeholder.
Development phases
Sequenced so that work blocked on client content never blocks work that isn't. Each phase has an exit gate.
| Phase | Work | Exit gate |
|---|---|---|
| 0 — Triage | Remove fabricated content from the live site. Set up the repo, git, CI. Collect Section 22 blocking items. | Nothing false is publicly live; logo received |
| 1 — Content | Founder interview, faculty facts, course details, results collection, consent forms, photography shoot. | Real content exists for home, about, courses, faculty |
| 2 — Design system | Tokens sampled from the real logo, type scale, primitives, contrast verification. Homepage and one course page designed at 360px and 1280px. | Design approved; contrast passes AA |
| 3 — Foundation | Next.js + TS + Tailwind scaffold, config module, layout, header, footer, primitives, SEO infrastructure, CI with Lighthouse and axe. | Empty shell deploys, passes budgets |
| 4 — Core pages | Home, About, Courses, course details, Faculty, Contact, Admissions + enquiry API + database + email. | A visitor can find a course and enquire, end to end |
| 5 — Evidence & integrations | Results, toppers, gallery, Review Engine, YouTube, admin dashboard. | Reviews and videos live; institute can publish a topper unaided |
| 6 — Audit | Functional, UX, mobile-on-device, performance, SEO, accessibility, security, and a full content-integrity pass. | Every audit signed off |
| 7 — Launch | DNS, redirects from old URLs, Search Console, GBP link, analytics, handover documentation and admin training. | Live, indexed, and the client can operate it |
| 8 — Post-launch | Monitor Core Web Vitals and enquiry conversion. Resources section and student stories if content materialises. | — |
Removing the fabricated toppers and testimonials should not wait for the new site. It is a small change to the existing build, it removes a live risk today, and it costs nothing to do this week.
Risks
Ordered by expected impact. The top four are all content or consent risks, not technical ones — which is the honest shape of this project.
| # | Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|---|
| 1 | Fabricated content survives into the new site, or is re-requested because the real numbers look modest | Medium | Severe | Phase 0 removal; a written content-integrity rule in the project; every published claim traced to a source. If real results are modest, we frame them honestly — a small verified set outperforms an impressive unverifiable one. |
| 2 | Photography never arrives | High | High | Designed fallbacks that look deliberate; art-directed shot list; treat the shoot as a Phase 1 gate rather than a nice-to-have. |
| 3 | Parental consent not obtained for published minors' data | Medium | Severe | Admin publish action requires consentRef; initials-only default; documented takedown route. Section 19. |
| 4 | Little or no real results data exists yet | Medium | High | Decide early. If the institute is young, replace the results band with teaching evidence — video, methodology, faculty credentials — rather than shipping an empty page. This changes the homepage, so it must be settled in Phase 1. |
| 5 | Review Engine activation stalls on Google API access | Medium | Medium | The site launches without it; the band appears when the payload does. Start the API request in Phase 0 — it is the longest lead time in the project. |
| 6 | Scope creep from the admin/CMS ambition | High | Medium | The Section 12 split is written down and agreed. Additions go to a Phase 8 list, not into Phase 5. |
| 7 | NAP inconsistency across site, GBP and directories | Medium | Medium | Single typed config module; one nominated primary phone; a pre-launch audit against the live GBP. |
| 8 | YouTube channel too small to fill /videos | Medium | Low | Threshold rule — fewer than six videos and the route ships later. |
| 9 | Third-party embeds erode the performance budget after launch | Medium | Medium | Lighthouse CI as a merge gate; interaction-loading for map and video is architectural, not a convention. |
| 10 | Handover gap — institute cannot operate the site | Medium | Medium | Phase 7 training on the admin, plus a short written runbook for the five things they will actually do. |
Implementation roadmap
Sequenced by dependency rather than dated, because the true critical path runs through client content, not engineering. Engineering durations are estimates; the gates are firm.
| Stage | Track | Est. | Depends on |
|---|---|---|---|
| Remove fabricated content from live site | Triage | 1 day | Client sign-off |
| Repo, git, CI, environments | Engineering | 1 day | — |
| Start Google Business Profile API request | Client + TP | Start now | GBP ownership — longest lead time |
| Content collection + founder interview | Content | 1–2 weeks | Client availability |
| Photography shoot | Content | 1 day + edit | Scheduling |
| Design system from real logo | Design | 3–4 days | Logo artwork |
| Homepage + course page designs | Design | 4–5 days | Design system, content |
| Foundation build | Engineering | 3–4 days | Design system |
| Core pages + enquiry system | Engineering | 1.5–2 weeks | Foundation, content |
| Results, toppers, gallery | Engineering | 4–5 days | Real results, consent, photos |
| Review Engine integration | Engineering | 2 days | Engine activated |
| YouTube integration | Engineering | 2 days | Channel ID |
| Admin dashboard | Engineering | 4–5 days | Database |
| Eight-part audit | QA | 3–4 days | Feature complete |
| Launch + handover training | All | 2 days | Audits signed off |
- Take the fabricated toppers and testimonials off the live site. One day's work, removes a live risk.
- Begin the Google Business Profile API access request. It has the longest lead time and nothing else depends on us to start it.
- Book the photography. It gates the design phase, and it is the input most likely to slip.
Everything else can proceed in parallel once the logo arrives.
Status: This plan is complete and awaiting approval. No application code has been written. On approval, work begins at Phase 0 — and the open questions in Section 22 should be answered in parallel, since they, not the engineering, set the launch date.
Prepared by TradyPerch · 21 August 2026