Fortetsia. A website for an underground shelter builder
A website for a company that builds underground shelters for private houses and plots. A three.js model on the first screen, an engineering cross-section, a 404 page with a radar, two languages, lead handling and a sales process built around it.
- Client
- Fortetsia - engineered underground shelters
- Industry
- Design and construction of shelters under private houses
- Stack
- Figma · three.js · vanilla JS · Schema.org
- Our role
- Design, front-end, lead funnel concept
The company and the brief
Fortetsia designs and builds self-contained underground shelters. The full cycle: geology, design, concrete works, engineering systems - filtered ventilation, power supply, communications, automation.
The company needed a website that explains the service through its specs: depth, protection class, autonomy, floor areas, timelines and pricing per configuration. We covered design, front-end, two language versions and the lead handling scheme.
Design in Figma
We designed the site in Figma from scratch. A dark scheme: #141618 background, gold #c9a227 accent. Headings are set in Unbounded, text and figures in IBM Plex Sans and IBM Plex Mono. The styling is built on draughtsman graphics: thin lines, coordinates, level marks.
The file describes interface states for buttons, links, form fields and the language switcher - default, hover and focus. The UI kit lives in a separate frame.

A 3D model of the house and shelter on the first screen
The first screen shows a three-dimensional model of the plot: the house, the entrance shaft, the living module, ventilation and the power unit. On load the wireframe is drawn group by group. The callout labels are plain HTML elements projected onto points of the model, so they follow the geometry as the scene sways.
The model is glTF/GLB, rendered with three.js: a wireframe is built from the geometry and appears through line hatching. The scene initialises while the main thread is idle and builds in chunks, so it never blocks page load. When WebGL is unavailable, an SVG version of the scene with the same animation takes over.
A panel for tuning line weights
Line weight is set separately for each group of elements - ground, house walls, stairs, shelter shell, ventilation. To dial the values in we built an internal panel: it opens with the ?tune=1 parameter in the address and never shows up on regular pages.
The sliders change the parameters right in the browser, so we could show options and approve them with the client on the spot. Later he played with the settings himself and picked the look he wanted - all we had left to do was copy the finished set of values into the config and apply it to the site.

An engineering cross-section of the plot
The second section is the plot in cross-section: the house above ground, the entrance shaft, the filtration airlock, the living module, the power unit and the air intake. As the section scrolls into view, the drawing lines are traced one after another with a cascading delay.
Below the drawing sit the key figures: 6 m depth, 90 days of autonomy, a 60 cm concrete wall and up to 120 sq m of floor area. The values count up from zero.

Configurations and stages of work
A shelter comes in several configurations: from a compact refuge to a spacious residence with the full set of engineering systems. We laid them out as cards where each one shows what is inside and how it differs from its neighbour - you can choose calmly, without comparing paragraphs of text.
The stages of work went into a separate block: geology, design, excavation and concrete, engineering systems, handover. The client sees the whole path of the build at once and knows what happens in what order.


Animation and prefers-reduced-motion
There are four kinds of animation on the site: blocks appearing on scroll, drawings being traced, counters for the figures, and parallax on the background grids. Everything runs on native JavaScript via IntersectionObserver and requestAnimationFrame, with no third-party libraries. Movement goes through transform, and scroll handlers are passive.
When the system prefers-reduced-motion setting is on, the animations switch off and elements appear straight away in their final state.
The 404 page
We wanted to bring something lively and memorable into the project, and a 404 is the perfect spot for it - nobody is waiting for that page anyway. That is how the radar idea appeared.
The sweep rotates with a four-second period, blips flash across the field, and below it a scan log prints line by line: route request, sector coordinates, a check of the drawing archive and a closing line saying there is no object at this address. The coordinates in the log are recalculated every 90 ms with a random offset around the Kyiv point.
The page is kept out of the index through robots.txt and a noindex meta tag. With prefers-reduced-motion the log prints instantly, without animation or blinking.
It turned out well - the page became one more highlight of the project, the kind you want to show people.
Performance
The site is built without a bundler: HTML, CSS and native ES modules, one minified stylesheet, woff2 fonts self-hosted and split into Cyrillic and Latin subsets. The GLB model is served gzipped.
The 3D scene has load limits. Pixel density is capped at 2x, and at 1.5x on devices with four cores or fewer. Antialiasing only kicks in at low density. Once built, the scene renders on a 33 ms step, roughly 30 frames per second, and rendering stops as soon as it leaves the viewport. When WebGL is unavailable, the SVG version is shown.
PageSpeed of the home page on mobile
Measured in Lighthouse, mobile profile, home page with the 3D scene on the first screen. Inner pages score the same.
SEO and two language versions
The site is bilingual: the Ukrainian version sits at the root and the English one under /en with a mirrored page structure. Each pair is linked with hreflang attributes - uk, en and x-default. If the browser language does not match the page language, a bar offers to switch, and the choice is stored in localStorage.
Pages carry canonical, Open Graph and Twitter Card tags. The Schema.org markup in JSON-LD covers Organization, WebSite, HomeAndConstructionBusiness, Blog, Article, CollectionPage, AboutPage, BreadcrumbList and Person. The sitemap holds 18 addresses with language alternates, and utility pages are closed off in robots.txt.
The request form and the path of a lead
The form collects a name, a phone number and a comment. It handles required-field validation, error and success states, keyboard control, focus return after the modal closes, and messages for screen readers.
For projects with a back-end the lead handling scheme looks like this: the request goes to a Telegram bot with status buttons, is saved in the CRM with its stage and source, and conversion is measured end to end from source to signed contract. Below is a demo mock-up of such a dashboard: the flow of requests, stages, traffic sources and metrics. The data on the mock-up is illustrative.

Git, automated deploy and tests
The project lives in Git. Deployment runs with a single command: build the CSS bundle, stamp asset versions to bust the browser cache, upload files over rsync and set permissions on the server.
On a static site there is nothing to test. In projects with a Laravel back-end and lead handling we cover lead intake and validation, the Telegram delivery and the CRM record with unit tests, and after deploy we run a smoke check on the key pages and the form.
The project in numbers
What the front-end is made of: language versions, markup, animation, third-party scripts.
Want results like these?

