| Server IP : 103.243.232.44 / Your IP : 216.73.216.237 Web Server : LiteSpeed System : Linux server17213-10344.hostycare.online 5.14.0-687.38.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 12 17:19:12 EDT 2026 x86_64 User : iamakash ( 1400) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/iamakash/kautuki.co.in/ |
Upload File : |
# Kautuki marketing site — agent guide
## What this is
The **marketing website** for Kautuki (kautuki.co.in) — a live tutoring marketplace for Class 6–12
students in Tier 2–4 India. This repo is **not** the product: there is no booking, no auth, no class
player here. The only working feature is an early-access lead form.
The site is **pre-launch**. `partials/head.php` sets `<meta name="robots" content="noindex">` on every
PHP page, and the footer's registered-office address is still a visible `[TODO]` placeholder. Product
claims on these pages describe the intended launch product — treat them as copy to keep internally
consistent, not as a spec of what ships today.
## Stack — deliberately zero-build
Flat PHP on shared cPanel hosting. **No npm, no framework, no bundler, no preprocessor, no
dependencies of any kind.** PHP is used only for `include`-ing shared partials and for one lead
endpoint; there is no database, no composer, no router.
Do not introduce a build step, a package manager, a CSS framework, or a JS library. If a task seems to
need one, say so and ask before adding it.
```
index.php classes.php subjects.php selfstudy.php safety.php <- the 15 "exam paper" pages
teach.php schools.php about.php contact.php gallery.php
faq.php privacy.php terms.php refund.php 404.php
partials/head.php form.php foot.php <- included by all 15
assets/exam.css exam.js <- the ONLY live CSS/JS
assets/app.css app.js <- DEAD: no page uses these now
api/lead.php api/mail.php api/config.example.php <- the only endpoint + its SMTP sender
logo/ gallery/ <- brand assets, photos, posters
index-old.html v2/ <- abandoned; excluded from deploy
```
### Page anatomy
Every content page is three lines of PHP wrapping plain HTML:
```php
<?php $PAGE = 'classes'; $TITLE = 'Classes & fees — Kautuki'; $DESC = '…'; include 'partials/head.php'; ?>
<section class="q body-cell" id="classes"> … </section>
<?php include 'partials/form.php'; include 'partials/foot.php'; ?>
```
- `$PAGE` must be one of `home classes subjects selfstudy safety teach schools about gallery faq
contact privacy terms refund` (and `''` on `404.php`) — it drives the nav's active state via the
`on()` / `on_any()` helpers in `head.php`. The three legal pages are footer-only by design: they set
`$PAGE` but have no nav entry, so nothing highlights. Don't "fix" that by adding them to the menu.
`on()` emits a whole ` class="on"` attribute, so it only works on a link that has **no** `class` of
its own. On a link that already carries one, use `on_any()` *inside* the attribute —
`class="foo<?php on_any(array('about')); ?>"` — or you silently emit a duplicate `class` that the
browser discards.
- `$TITLE` and `$DESC` are escaped with `htmlspecialchars` in the head, so write them as plain text
with real `&` and `—`, not entities.
- **Adding a page** means: create `newpage.php`; add `$PAGE` to the list in `head.php`'s comment; add
nav links in `head.php` — both the desktop `.menu`/`.drop` entry *and* an entry in the `.drawer`,
since the phone menu is a separate off-canvas card; add a footer link in `foot.php`; add a `<url>` to `sitemap.xml`
using the extensionless form (`/newpage`, per the `.htaccess` rewrite) — but check first that no
directory of that name exists, or the pretty URL resolves to the directory instead of the page (this
is exactly what has gone wrong with `/gallery`).
### CSS and JS
`assets/exam.css` (~940 lines) and `assets/exam.js` (~250 lines) are hand-authored and cover all eleven
pages. Both are plain, commented and organised by section — read the neighbouring block before adding
to either.
- All colour, spacing and layout constants live in `:root` at the top of `exam.css`. Use the tokens
(`--ink`, `--paper`, `--red`, `--amber`, `--m`, `--gutter`, `--bleed-l/r`), never raw hex.
- Fluid type and spacing via `clamp()`. **No fixed pixel widths, no media-query-only layouts** — the
site is mobile-first. Four breakpoint values, in two places. The main block sits together around
line 645: `960px` (grids halve), `720px` (the phone layout: the red margin rule is dropped and
`.marginal` Q-numbers become inline stamps), `620px` (the comparison table restacks, two-column
indexes go to one) and `520px` (grids go to one column, and the `nowrap` escapes live here).
A **second `720px` block at the very end** belongs to the multi-page nav — that is where the whole
`.idx` row is hidden and the off-canvas `.drawer` takes over (a `520px` block follows it, trimming
the header CTA so logo + CTA + burger fit one 320px row). Put nav rules there, page rules in the
main block;
and note `prefers-reduced-motion` has its own block around line 918.
- **There are two kinds of grid here, and they are responsive in different ways.**
- *Fixed column counts*, which need a breakpoint for every step: `.trust-row` and `.arc` are
`repeat(4, 1fr)`, `.kit` is `repeat(3, 1fr)`, `.gal` is `repeat(3, 1fr)`, `.circular` is
`repeat(2, 1fr)`. All six are covered in the media queries — keep it that way if you add one.
- *Self-responsive `auto-fit` grids*: `.formats`, `.fees`, `.syllabus`, `.board-grid`, `.pages`,
`.stories`. Every one is written `repeat(auto-fit, minmax(min(NNNpx, 100%), 1fr))` and the
**`min(NNNpx, 100%)` is not decoration** — a bare `minmax(280px, 1fr)` overflows the screen below
280px. Copy the whole pattern or don't use it.
- Match your item count to the columns, or add a modifier. Four `.kit` cards strand one on its own row,
which is what `.kit.four` exists for: it steps 4 → 2 → 1 and deliberately never 3. Prefer explicit
counts over `auto-fit` when an orphan matters — `auto-fit` settles on whatever happens to fit, which
can be the orphaning number. And note a modifier like `.kit.four` out-specifies the plain `.kit`
rules inside the media queries, so it needs its own entry in each one.
- `.ticks` is a horizontal wrapping row, meant for short chips. Whole-sentence items need the stacked
variant — `.fees .ticks` and `.abt-ticks .ticks` set `flex-direction: column` and make each `li` a
flex row, so wrapped text hangs beside the tick instead of running back underneath it.
- A long unbreakable token (an email address) will overflow a narrow card, because there is nowhere to
break it. Give it `overflow-wrap: anywhere` — see `.fmt .mail`.
- **`white-space: nowrap` needs an escape at 520px.** It is used deliberately — to keep `.marks` on the
heading's line, a `.gate .tag` unbroken, a `.books .ext` domain intact — but a nowrap run cannot
break, so on a 320px screen it just overruns. `.marks` gets `white-space: normal` and `.books a`
gets `flex-wrap: wrap` in the 520px block for exactly this reason. Check any new nowrap the same way:
longest string × font-size × ~0.53 against roughly 284px of content width at 320px.
- **`body` has `overflow-x: hidden`.** It stops the page scrolling sideways, but it does that by
*hiding* overflow rather than preventing it — an element that runs off the edge is silently clipped
instead of showing a scrollbar. Do not read "no horizontal scrollbar" as "the layout fits".
- **Working out how wide something actually is:** `.paper` is `max-width: 1160px`, centred — so the
container stops growing there even though `--m` and `--gutter` keep scaling with `vw`. `.body-cell`
is `padding-left: var(--m) + var(--gutter)` but `padding-right: clamp(16px, 4vw, 48px)` — the two
sides are *not* symmetric. Above 1160px a `vw`-scaled font therefore keeps growing while its column
does not, which is how a figure that fitted at 1000px overflowed at 1440px. Content width is
`min(vw, 1160) − (m + gutter) − clamp(16,4vw,48)`, and below 720px simply `vw − 36`.
- **Banners.** The home hero and the six page banners (`classes`, `subjects`, `selfstudy`, `teach`,
`schools`, `gallery`) are the *same* component — copy the markup from `index.php` rather than
inventing a variant. A slide is: a `<div>` holding `h1`/`h2` + `.sub` + `.go`, then a sibling
`.art`. Two things carry the theme and are easy to leave out —
- **`.hl`** on part of the headline: the amber highlighter that sweeps 0 → 100% as the slide
activates. It is the most recognisable move on the site. `.dim` (amber text) is a quieter partner,
not a substitute.
- **`.art`**: a line drawing on a 340×240 canvas in the palette (`#0F2557` strokes, `#C93B2E`
accents, `#F5A623` fills, `#F6EFE2`, white) — or a photo from `gallery/`. Page banners open with a
photo and use SVGs for the abstract slides.
A banner that skips these reads as off-theme even when the geometry is right — that is exactly how
a hand-made `.fig` card variant went wrong and had to be thrown away.
- `.band` is the quiet closing banner on the legal pages (`#help` anchor): a bordered panel with a
heading, a line of copy and one `.stamp`. It lifts on hover and is registered `rv-t`. Service, not
sales — the legal pages carry no lead form on purpose.
- **Check a new class name against the existing ones before you take it.** `.plain` was already the
site's secondary-link style (`.q-cta .plain`, `.slide .go .plain`); a legal-page component briefly
reused the name and put a red rule down the side of every secondary link on every page. It is
`.gloss` now. `grep` the class in `exam.css` first.
- **Long-form documents** (privacy, terms, refund) use `.toc` + `.legal` / `.clause` / `.gloss`, not
the marketing components. The legal text is never collapsed — burying terms a user is agreeing to
behind an accordion is the wrong call, and it hurts Ctrl+F. What folds is the `.gloss` "in plain
English" gloss beside each clause. These pages also skip the wry `.marks` asides: `[10 clauses]`
is fine, `[3 marks — no partial credit]` is not, on a page someone is agreeing to.
- Legal wording is load-bearing. When restyling one of those pages, change only the chrome around the
text — then diff the rendered prose against the old file sentence by sentence before deleting it.
- The nav dropdown sits 8px below its button, and that gap is bridged by an invisible
`.idx .drop::after` strip. Without it the pointer crosses dead space on the way down, `:hover` goes
false and the panel shuts before you reach it. The strip belongs to `.drop`, so hovering it still
counts as hovering inside `.menu`. Don't delete it as an empty rule — and if you change the 8px
offset, change the strip to match.
- **The phone menu is a separate component, not a reflow of the desktop one.** Below 720px `.idx-wrap`
is hidden outright and `.burger` (in `.hdr-top`) opens `.drawer` — an index card that slides in from
the right, carrying its own red margin rule, Caveat group labels, the same stamp links and one
`.stamp` CTA. Open/shut is a single `nav-open` class on `<html>`: the CSS slides the card, fades
`.nav-scrim` and locks page scroll. The card is hidden with `visibility`, not `display`, so it stays
out of the tab order while shut and can still animate. A new page needs a link in **both** the
desktop `.menu`/`.drop` and the `.drawer`.
- `exam.js` is nine small IIFEs: scroll reveals, sticky-header measurement, dropdown menus, the phone
drawer, progress rule, role pre-select, form POST, gallery lightbox, banner sliders. ES5-ish, no
build, no polyfills.
The slider IIFE drives **every `.slider` on the page**, not just the home hero — each gets its own
index, its own hold flag and its own 5s timer. Add a `.slider` containing a `.slides` track and a
`.dots` box and it wires itself up; no IDs are involved.
- **New animated blocks must be registered** in the `groups` array at the top of `exam.js`, or they
never reveal on scroll. Use `'rv'` normally, `'rv-t'` for anything that carries its own tilt or
hover-lift transform, `'rv-m'` for red-margin annotations.
- Everything must degrade without JS: the `.js` class is added by the script and all reveal styles are
scoped under it, so a no-JS visitor gets a fully readable page.
`assets/app.css` / `app.js` are the **legacy v1 design system** (Plus Jakarta Sans / Inter, blue
`#0F3F7A`). **No live page uses them any more** — the last five (`contact`, `privacy`, `terms`,
`refund`, `404`) were rebuilt on the exam-paper system and their `.html` files deleted. The two files
are kept only because `index-old.html` still references them. Don't build anything new on them.
When converting an `.html` page to `.php`, the rebuild and the delete have to happen together:
`.htaccess` resolves `.html` before `.php`, so a leftover `foo.html` silently shadows the new
`foo.php`. The pretty URLs (`/privacy`, `/contact`) did not change, but every explicit `foo.html`
link did — grep for the old filename across `*.php`, `*.xml` and `.htaccess`.
**Asset paths in the partials are absolute** (`/assets/exam.css`, `/logo/…`, and `form.php` posts to
`/api/lead.php`). That is required, not stylistic: `404.php` is served for *any* URL, so relative
paths would resolve against `/deep/path/` and the error page would render unstyled. Keep them
absolute — the site is served from the domain root.
`gallery/` holds seven AI-generated photographs — `photo-class`, `photo-doubt`, `photo-whiteboard`,
`photo-verify`, `poster-video`, `school-hall`, `teach-notice` — and `gallery/PROMPTS.md` records the
house style and the exact prompt behind each. That file also specifies four slots that were planned
and **never produced**: `hero-1/2/3.jpg` and `photo-teacher.jpg`. If a page needs an image that isn't
in the seven, generate it from the documented prompt rather than inventing a new style, and keep the
`PROMPTS.md` table in step. `PROMPTS.md` is excluded from deploy.
`index-old.html` (the original single-page v1) and `v2/` (an abandoned "cinematic" alternative) are
dead explorations kept for reference. **Never update them alongside a live page.**
## The design idiom — an exam paper
This is the strongest constraint in the repo. The whole site is staged as a school exam answer paper: a
cream sheet lying on a desk, one continuous red margin rule running the full height of the page,
sections numbered like questions, and red-pen annotations in a handwriting face.
New content has to arrive **as a question on that paper**:
```html
<section class="q body-cell" id="verify">
<span class="marginal" style="top:clamp(56px,7vw,100px)">Q1.</span> <!-- red-margin annotation -->
<div class="q-head">
<h2>The verification story.</h2>
<span class="marks">[3 marks — no partial credit]</span> <!-- always bracketed, wry -->
</div>
<p class="q-kicker">One or two sentences setting up the section.</p>
…
</section>
```
- `.body-cell` goes on every top-level section — it supplies the left padding that clears the margin
rule. A section without it sits under the red line.
- Three type faces, each with one job: **Fraunces** (headings), **Hanken Grotesk** (body), **Caveat**
(`.redpen` / `.script` — the teacher's red pen: ✓ ticks, ✎ notes, the "Kautuki:" labels, "rough work"
headings). Never mix these roles.
- Recurring furniture: `.stamp` (the rubber-stamp CTA button), `.ticks` (red-pen checklists), `.rough`
(a "rough work" table showing worked rupee arithmetic), `.marks` (the bracketed aside), and
"— end of paper —" above the footer.
- Use HTML entities the way the existing markup does: `— – ’ “ ”
₹ ✓ →`. Files are **UTF-8 with CRLF line endings** — preserve both when editing.
## Voice and copy rules
The copy is deliberately anti-edtech, and this matters more here than any technical convention.
- **Never** write "AI-powered", "revolutionary", "world-class", "unlock your potential", countdown
urgency, fake scarcity, or testimonials and statistics that aren't real.
- Write for a parent in a Tier-2 town deciding whether to spend ₹300. Concrete over abstract: rupee
figures, "the night before a test", "the auto fare, twice a day, six days a week".
- Show the arithmetic. Every fee or earnings claim appears as a visible `.rough` table, footnoted as
illustrative, with "rates are set by each teacher and shown on their profile before you book".
- Keep the five load-bearing promises consistent wherever they appear (`index.php#why`, `classes.php`,
`faq.php`, the `classes.php#compare` table): first class ₹0 · pay per class, no package · teachers
hand-verified by a person · 1:1 or batches of ≤12 · a free self-study library between classes.
Changing a number in one place means grepping for it in the others.
- The comparison table argues against "the usual model" generically and says so in a footnote.
**Do not name a competitor** anywhere on the site.
## Running it locally
Needs PHP on PATH — the pages use `include`, so opening a `.php` file from disk will not work.
```sh
php -S localhost:8000 # then http://localhost:8000/
php -l api/lead.php # the lint CI runs
```
`.htaccess` rewrites only apply under Apache, so on `php -S` use the full `classes.php` form rather
than the extensionless `/classes`.
Under a local Apache (WampServer on Windows) point the vhost `DocumentRoot` at the repo and the
rewrites work, so the extensionless URLs can be tested too — worth doing, since a new page's pretty
URL can collide with a real directory. Two stock-WampServer defaults bite there and are worth knowing
before you debug the wrong thing:
- **`mod_expires` is off**, so the `.htaccess` caching rules do nothing at all — including the one that
tells browsers never to cache HTML. The browser then caches by heuristic and you edit a page and keep
seeing the old one. Fix it on the dev vhost with `Header always set Cache-Control "no-store"` rather
than enabling `mod_expires`, which would also switch on the 7-day CSS/JS caching and hide your edits.
- **`mod_access_compat` is off**, which makes `Deny from all` in `leads/.htaccess` an unparseable
directive, so `/leads/*` returns 500 rather than 403. Nothing leaks either way, but enable the module
locally if you want to check that guard behaves.
## Deploy and data handling
`main` → GitHub Actions (`.github/workflows/deploy.yml`) → FTPS sync to Hostycare cPanel. The `check`
job lints `api/lead.php` and **fails the build if any lead CSV was committed**. `index-old.html`,
`gallery/PROMPTS.md` and `leads/` are excluded from the upload.
`api/lead.php` validates the form (Indian mobile regex, a honeypot field named `company`, an explicit
consent flag), appends to `leads/leads.csv` and emails the team. The email goes through Google
Workspace SMTP (`api/mail.php`, a dependency-free client) when `api/config.php` exists on the server;
that file holds an App Password, is gitignored, excluded from the deploy sync and web-blocked by
`api/.htaccess`, and is created by hand from `api/config.example.php` via cPanel File Manager. Without
it the script falls back to PHP `mail()`, which fails SPF/DKIM and lands in junk. SMTP failures are
appended to `leads/mail.log` (never the password). That CSV holds names and phone numbers of real
people:
- `leads/*.csv` and `leads/*.log` are gitignored, blocked by `leads/.htaccess`, and 403'd by a
`RedirectMatch` in the root `.htaccess`. **Never commit one, never paste its contents anywhere,
never weaken any of those three guards.**
- The privacy copy commits to the DPDP Act 2023 and data residency in Mumbai. Don't add a third-party
script, pixel, font host or analytics tag without asking — today the only external requests are to
Google Fonts.
## Before launch (open items)
1. Remove `<meta name="robots" content="noindex">` from `partials/head.php`.
2. Fill the `[Registered office address — add before launch]` placeholder. It now appears in **two**
places: `partials/foot.php` and `contact.php` (Q3).
3. ~~Rebuild the legacy-design pages on the exam-paper system.~~ **Done** — all five (`contact`,
`privacy`, `terms`, `refund`, `404`) are converted. `assets/app.css` / `app.js` are now dead
weight, kept only for `index-old.html`; they can go whenever that file does.
Known bugs, none of them fixed:
4. **`/gallery` never reaches `gallery.php`.** The extensionless URL collides with the real `gallery/`
photo directory, and `mod_dir` redirects to `/gallery/` before the `.htaccess` rewrite gets a look.
The nav links straight to `gallery.php` so visitors are fine, but `sitemap.xml` lists `/gallery` and
so points crawlers at a directory. Either rename the folder or use `/gallery.php` in the sitemap.
5. ~~The phone nav never highlights the current page.~~ **Fixed** — the `.mob` duplicate links that
carried the duplicate-`class` bug are gone; below 720px the nav is the `.drawer`, whose links have
no `class` of their own and so take `on()` correctly.