Identity, site and admin panel for an education centre: an orbit mark and brand book, a landing with events, courses and enquiries, a custom admin with a block-based content builder and Telegram notifications. Next.js exported to static, PHP back end — on the client's ordinary hosting.
KosmosPRO is an education and development centre for children, teachers and parents. Its work builds on the legacy of innovative Russian educators (Shatalov, Shchetinin, Amonashvili, Sukhomlinsky) and on Bazarny's health-preserving learning environment.
They needed a site that conveys this philosophy rather than just listing services — and one that lives without a developer: the centre's team must update the event calendar, courses and news themselves.

"Instead of forcing a hosting migration, we designed a hybrid: Next.js exported to static, everything server-side in PHP."
The site was preceded by the centre's identity — from mark exploration to a brand book and carriers.
Exploration ran in several directions: four-field shield crests (traditions, health, upbringing, educators), dot matrices, "growth" plus-crosses. The simplest metaphor won: orbits. The mark is concentric rings, like a section of a planetary system or the growth rings of a tree: the child at the centre, family, teachers and culture around. The logo is "КОС / МОС / ПРО" set in three lines, where the О in "МОС" is those orbits.



Palette — deep cosmic blue with a golden sunrise gradient; type — a wide grotesque for headings; illustrations — warm scenes with children, stars and Kazan in a textured flat style; icons and graphic elements built from the same rings and four-point stars. Separate slides cover logo rules on photos and coloured grounds, and usage in presentations and social media.


Posters and roll-ups with orbits and the line "Development · Upbringing · Learning", tees and totes, badges, pens and folders, signage on the centre's fence.


A single-page landing with sections About, Events, For Kids, For Teachers, Space, Team, Contacts — plus separate course and article pages. The visual language is "cosmos and gold": brand orbits slowly rotating with scroll parallax, gentle block reveals, smooth scrolling.
Each of the centre's four core values opens in a modal with the full text — the home page stays light, and depth is there for those who want to read.

// Brand orbits: rotation + scroll parallax
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -160]);
<motion.div style={{ y }} aria-hidden>
<motion.svg animate={{ rotate: 360 }}
transition={{ duration: 160, repeat: Infinity, ease: "linear" }}>
<circle r="70" strokeWidth="9" />
<circle r="120" strokeWidth="8" strokeDasharray="540 220" />
<circle r="170" strokeWidth="7" />
<circle r="225" strokeWidth="6" strokeDasharray="900 400" />
</motion.svg>
</motion.div>


Events, articles and courses are assembled from blocks: text, heading, quote, key points, schedule timeline, image. Events share one card template with the key fields "Dates / Time / Age / Price / Place" — the editor never thinks about layout, everything looks consistently neat.
export type Block =
| { type: "text"; text: string }
| { type: "heading"; text: string }
| { type: "quote"; text: string }
| { type: "highlights"; title?: string; items: string[] }
| { type: "timeline"; title?: string; items: TimelineItem[] }
| { type: "image"; url: string; caption?: string };
export type EventRecord = {
published: boolean;
eyebrow: string | null; // "4 September · free entry"
title: string;
meta: EventMeta; // Dates / Time / Age / Price / Place
blocks: Block[]; // the builder
};
Password login, four sections: enquiries, events, articles, courses. Form submissions land in the database with statuses (New / In progress / Done / Archive) and comments — effectively a lightweight CRM. Publishing is a single toggle: drafts are invisible on the site.


A new enquiry instantly arrives in the centre's Telegram chat and by email. If Telegram isn't configured, intake still works: the enquiry is simply saved to the database, no errors.
The client's hosting is shared PHP 8 + MySQL, no Node. The Next.js front end is exported to static (output: export), while everything server-side — enquiries, admin API, auth, image uploads — is written in PHP. Content from the database is "baked" into HTML at build time, so the site is served as static files and loads instantly.
Browser ──> static /index.html, /deti/, /admin/… ← next build (export)
└─> PHP API /api/leads.php, /api/content.php, ← php/
/api/admin/*.php ──> MySQL
Admin auth has no server sessions: a signed cookie whose value is expiry + HMAC-SHA256 over the secret. Secrets live outside the web root and are unreachable over HTTP. Deployment is one FTP command.
function sign_session(int $expires): string {
return $expires . '.' . hash_hmac('sha256', (string) $expires, admin_secret());
}
function is_admin(): bool {
[$expires, $sig] = explode('.', $_COOKIE[ADMIN_COOKIE] ?? '.', 2);
if (!ctype_digit($expires) || (int) $expires < time()) return false;
return hash_equals(hash_hmac('sha256', $expires, admin_secret()), $sig);
}
