Spaces:
Running
Running
Initial MTMehitus Space
Browse files- ANTIGRAVITY_BUILDER_SPEC.md +398 -0
- ANTIGRAVITY_HANDOFF.md +96 -0
- README.md +48 -4
- app.js +611 -0
- index.html +2009 -18
ANTIGRAVITY_BUILDER_SPEC.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Antigravity Builderi spetsifikatsioon: MTM Project Studio
|
| 2 |
+
|
| 3 |
+
## 1. Builderile antav põhikirjeldus
|
| 4 |
+
|
| 5 |
+
Ehita MTM Ehitus OÜ-le kõikehõlmav eestikeelne projektimüügi, disaini valiku ja täislahenduse tellimise platvorm. Tegemist ei ole kinnisvaraportaaliga ega valmis majade müügiga. Müüdavad tooted on:
|
| 6 |
+
|
| 7 |
+
1. 3D-disain ja arhitektuurne kontseptsioon;
|
| 8 |
+
2. valitud kontseptsioonist arendatud ehitusprojekt;
|
| 9 |
+
3. personaalne eriprojekt;
|
| 10 |
+
4. olemasoleva disaini kohandamine krundile ja kliendi vajadustele;
|
| 11 |
+
5. projekteerimise, eelarvestamise, juhtimise ja ehituse võtmed-kätte täislahendus.
|
| 12 |
+
|
| 13 |
+
Kasutaja peab saama alustada päris tehtud 3D-töö või kontrollitud näidislahenduse vaatamisest, võrrelda variante, salvestada lemmikud, valida sobiv projekt lähtepunktiks, kohandada tehnilisi parameetreid, saada esialgne hinnavahemik ja ajakava ning saata MTM Ehitusele struktureeritud lähteülesanne.
|
| 14 |
+
|
| 15 |
+
Platvormi peamine eesmärk ei ole koguda võimalikult palju üldisi kontaktivorme. Eesmärk on muuta tundmatu külastaja kvalifitseeritud projektipäringuks, mille juures on teada:
|
| 16 |
+
|
| 17 |
+
- valitud disain või soovitud arhitektuurne suund;
|
| 18 |
+
- hoone kasutusotstarve ja piirkond;
|
| 19 |
+
- soovitud pindala, korrused ja ruumiprogramm;
|
| 20 |
+
- konstruktsiooni, katuse, küttesüsteemi ja viimistluse eelistused;
|
| 21 |
+
- tellitav teenusepakett;
|
| 22 |
+
- soovitud ajakava ja eelarveklass;
|
| 23 |
+
- krundi ning lähteandmete valmidus;
|
| 24 |
+
- kliendi kontaktandmed ja andmetöötluse nõusolek.
|
| 25 |
+
|
| 26 |
+
## 2. Tootepõhimõtted
|
| 27 |
+
|
| 28 |
+
- Ära esita portaali valmis majade või kinnisvara müügina.
|
| 29 |
+
- Ära nimeta näidist “tehtud klienditööks”, kui selle kohta puudub tõend ja avaldamisluba.
|
| 30 |
+
- Kõik hinnad peavad olema märgitud kas fikseeritud hinnana, “alates” hinnana või esialgse vahemikuna.
|
| 31 |
+
- Kalkulaatori tulemus ei ole siduv pakkumine.
|
| 32 |
+
- Ära pane API-võtmeid, meiliteenuse paroole ega muid saladusi frontend-koodi.
|
| 33 |
+
- Tootmisversioonis ei tohi kliendi kontaktandmeid salvestada ainult `localStorage`-isse.
|
| 34 |
+
- Mobiilivaade, klaviatuuriga kasutamine, kontrast ja ekraanilugeja tugi on kohustuslikud.
|
| 35 |
+
- Päris projektifotodel peab olema kasutusõigus, optimeeritud formaat ja sisuline `alt`-tekst.
|
| 36 |
+
|
| 37 |
+
## 3. Kasutajarollid
|
| 38 |
+
|
| 39 |
+
### Avalik külastaja
|
| 40 |
+
|
| 41 |
+
- vaatab projekte ja pakette;
|
| 42 |
+
- filtreerib, otsib ja salvestab lemmikuid;
|
| 43 |
+
- koostab projekti lähteülesande;
|
| 44 |
+
- saab esialgse hinnavahemiku;
|
| 45 |
+
- saadab päringu või broneerib konsultatsiooni.
|
| 46 |
+
|
| 47 |
+
### Müügikonsultant
|
| 48 |
+
|
| 49 |
+
- näeb uusi päringuid;
|
| 50 |
+
- filtreerib neid staatuse ja teenusemahu järgi;
|
| 51 |
+
- lisab märkmeid ja määrab vastutaja;
|
| 52 |
+
- muudab päringu staatust;
|
| 53 |
+
- loob kliendile järgmise tegevuse.
|
| 54 |
+
|
| 55 |
+
### Sisuhaldur
|
| 56 |
+
|
| 57 |
+
- lisab ja muudab projektikaarte;
|
| 58 |
+
- haldab fotosid, 3D-renderdusi, silte ja pakette;
|
| 59 |
+
- märgib, milline väide on avaldamiseks kinnitatud;
|
| 60 |
+
- muudab hinnavahemikke ning kalkulaatori koefitsiente.
|
| 61 |
+
|
| 62 |
+
### Administraator
|
| 63 |
+
|
| 64 |
+
- haldab kasutajaid ja õigusi;
|
| 65 |
+
- näeb süsteemi logisid;
|
| 66 |
+
- ekspordib päringuid;
|
| 67 |
+
- haldab integratsioone, nõusolekuid ja andmete säilitusaega.
|
| 68 |
+
|
| 69 |
+
## 4. Põhiandmemudelid
|
| 70 |
+
|
| 71 |
+
### Project
|
| 72 |
+
|
| 73 |
+
```text
|
| 74 |
+
id
|
| 75 |
+
slug
|
| 76 |
+
title
|
| 77 |
+
short_description
|
| 78 |
+
long_description
|
| 79 |
+
building_type
|
| 80 |
+
style
|
| 81 |
+
area_m2
|
| 82 |
+
floor_count
|
| 83 |
+
room_count
|
| 84 |
+
materials[]
|
| 85 |
+
base_design_price
|
| 86 |
+
available_packages[]
|
| 87 |
+
customizable_fields[]
|
| 88 |
+
status_label
|
| 89 |
+
is_verified_client_work
|
| 90 |
+
publication_consent_id
|
| 91 |
+
images[]
|
| 92 |
+
model_3d_url
|
| 93 |
+
floorplan_files[]
|
| 94 |
+
featured
|
| 95 |
+
published
|
| 96 |
+
created_at
|
| 97 |
+
updated_at
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
### Package
|
| 101 |
+
|
| 102 |
+
```text
|
| 103 |
+
id
|
| 104 |
+
name
|
| 105 |
+
description
|
| 106 |
+
pricing_mode
|
| 107 |
+
price_from
|
| 108 |
+
price_to
|
| 109 |
+
price_per_m2_from
|
| 110 |
+
duration_min_weeks
|
| 111 |
+
duration_max_weeks
|
| 112 |
+
deliverables[]
|
| 113 |
+
stages[]
|
| 114 |
+
revision_count
|
| 115 |
+
active
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
### Lead
|
| 119 |
+
|
| 120 |
+
```text
|
| 121 |
+
id
|
| 122 |
+
reference
|
| 123 |
+
name
|
| 124 |
+
email
|
| 125 |
+
phone
|
| 126 |
+
company
|
| 127 |
+
selected_project_id
|
| 128 |
+
selected_package_id
|
| 129 |
+
project_brief_json
|
| 130 |
+
budget_range
|
| 131 |
+
desired_start_date
|
| 132 |
+
plot_status
|
| 133 |
+
consent_version
|
| 134 |
+
consent_at
|
| 135 |
+
source
|
| 136 |
+
utm_parameters
|
| 137 |
+
status
|
| 138 |
+
owner_id
|
| 139 |
+
created_at
|
| 140 |
+
updated_at
|
| 141 |
+
```
|
| 142 |
+
|
| 143 |
+
### PriceRule
|
| 144 |
+
|
| 145 |
+
```text
|
| 146 |
+
id
|
| 147 |
+
package_id
|
| 148 |
+
rule_type
|
| 149 |
+
condition_json
|
| 150 |
+
amount
|
| 151 |
+
multiplier
|
| 152 |
+
priority
|
| 153 |
+
valid_from
|
| 154 |
+
valid_to
|
| 155 |
+
active
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
## 5. Peamine kasutajateekond
|
| 159 |
+
|
| 160 |
+
```text
|
| 161 |
+
Avaleht
|
| 162 |
+
→ projektikataloog
|
| 163 |
+
→ filtrid või otsing
|
| 164 |
+
→ projekti detailvaade
|
| 165 |
+
→ “Kasuta lähtepunktina”
|
| 166 |
+
→ paketi valik
|
| 167 |
+
→ tehnilised lähteandmed
|
| 168 |
+
→ esialgne hinnavahemik ja ajakava
|
| 169 |
+
→ lähteülesande kontroll
|
| 170 |
+
→ kontaktandmed ja nõusolek
|
| 171 |
+
→ päringu saatmine
|
| 172 |
+
→ kinnitusekraan ja konsultatsiooni broneerimine
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
Kasutaja valikud peavad säilima, kui ta liigub kataloogi, pakettide ja planeerija vahel. Tagasi tulles ei tohi lähteülesanne kaduda.
|
| 176 |
+
|
| 177 |
+
## 6. Antigravity Builderi master-prompt
|
| 178 |
+
|
| 179 |
+
```text
|
| 180 |
+
Ehita olemasoleva MTM Project Studio prototüübi põhjal tootmiskõlblik eestikeelne veebirakendus. Säilita visuaalne suund: tumesinine, kuldne aktsent, valge taust, selged kaardid ja tugev ehitusvaldkonna professionaalsus.
|
| 181 |
+
|
| 182 |
+
Äriline positsioneerimine:
|
| 183 |
+
- me ei müü valmis maju;
|
| 184 |
+
- müüme 3D-disainilahendusi, ehitusprojekte, kohandamist ja täislahendusi;
|
| 185 |
+
- projektikataloog sisaldab kontrollitud näiteid ning avaldamisloaga tehtud töid;
|
| 186 |
+
- iga projekt on võimalik valida uue tellimuse lähtepunktiks.
|
| 187 |
+
|
| 188 |
+
Kohustuslikud moodulid:
|
| 189 |
+
1. responsiivne avaleht ja selge väärtuspakkumine;
|
| 190 |
+
2. andmepõhine projektikataloog koos otsingu, filtrite, sortimise ja lemmikutega;
|
| 191 |
+
3. projektide detaillehed galeriiga, põhiandmetega, kohandamisvõimalustega ja 3D-mudeli kohaga;
|
| 192 |
+
4. kolme teenusepaketi võrdlus;
|
| 193 |
+
5. mitmeastmeline projekti konfiguraator;
|
| 194 |
+
6. administreeritavate reeglitega hinnavahemiku kalkulaator;
|
| 195 |
+
7. automaatselt genereeritav projektibrief;
|
| 196 |
+
8. turvaline päringuvorm, nõusolek ja e-posti teavitus;
|
| 197 |
+
9. päringute halduse töölaud;
|
| 198 |
+
10. sisuhaldus projektidele, piltidele, pakettidele ja hinnareeglitele;
|
| 199 |
+
11. analüütika, SEO, ligipääsetavus, jõudlus ja turvalisus;
|
| 200 |
+
12. Hugging Face Space’i demo ning tootmiskeskkonna eraldi konfiguratsioon.
|
| 201 |
+
|
| 202 |
+
Kasuta kontrollitavaid andmemudeleid. Ära pane saladusi frontend-koodi. Ära fabritseeri kliendiprojekte, asukohti, hindu ega ostude arvu. Kui sisu pole kinnitatud, märgi see näidissisuks.
|
| 203 |
+
|
| 204 |
+
Koosta iga arendusosa jaoks testid ning täida allolevad vastuvõtukriteeriumid.
|
| 205 |
+
```
|
| 206 |
+
|
| 207 |
+
## 7. Tööde backlog
|
| 208 |
+
|
| 209 |
+
### Epic A – sisu ja bränd
|
| 210 |
+
|
| 211 |
+
- [ ] `A-01 P0` Kinnita ettevõtte nimi, registriandmed, telefon, e-post ja teeninduspiirkonnad.
|
| 212 |
+
- [ ] `A-02 P0` Kinnita avaldatavad väited: kogemus, objektide arv, garantiid ja reageerimisaeg.
|
| 213 |
+
- [ ] `A-03 P0` Kogu päris 3D-renderdused, plaanid ja tehtud tööde fotod koos avaldamisloaga.
|
| 214 |
+
- [ ] `A-04 P1` Koosta igale projektile nimi, lühikirjeldus, põhiandmed, kohandamisvõimalused ja `alt`-tekstid.
|
| 215 |
+
- [ ] `A-05 P1` Loo projektide märgistamise süsteem: hoonetüüp, stiil, pindala, korrused, konstruktsioon ja teenusepakett.
|
| 216 |
+
- [ ] `A-06 P1` Kinnita pakettide tegelik sisu, hinnastamisloogika, parandusringid ja üleantavad failid.
|
| 217 |
+
|
| 218 |
+
Vastuvõtt: ükski avalik fakt ega klienditöö staatus ei ole kontrollimata.
|
| 219 |
+
|
| 220 |
+
### Epic B – projektikataloog
|
| 221 |
+
|
| 222 |
+
- [ ] `B-01 P0` Vii projektid kõvakodeeritud JavaScriptist andmebaasi või hallatavasse JSON/CMS-kihti.
|
| 223 |
+
- [ ] `B-02 P0` Loo projektide loend ja SEO-sõbralikud detaillehed.
|
| 224 |
+
- [ ] `B-03 P0` Lisa otsing nime, stiili, materjali ja märksõnade järgi.
|
| 225 |
+
- [ ] `B-04 P0` Lisa filtrid hoonetüübi, pindala, korruste, stiili ja paketi järgi.
|
| 226 |
+
- [ ] `B-05 P1` Lisa sortimine soovitatuse, pindala, disainihinna ja värskuse järgi.
|
| 227 |
+
- [ ] `B-06 P1` Lisa lemmikud anonüümsele kasutajale ja sisselogitud kliendile.
|
| 228 |
+
- [ ] `B-07 P1` Lisa vähemalt kuuest päris või selgelt märgistatud näidisprojektist koosnev algkataloog.
|
| 229 |
+
- [ ] `B-08 P2` Lisa projektide omavaheline võrdlus kuni kolme projekti kaupa.
|
| 230 |
+
|
| 231 |
+
Vastuvõtt: filtrid töötavad koos, URL säilitab filtrid ja tühja tulemuse vaade annab selge järgmise tegevuse.
|
| 232 |
+
|
| 233 |
+
### Epic C – projektide detailvaade
|
| 234 |
+
|
| 235 |
+
- [ ] `C-01 P0` Loo pildigalerii WebP/AVIF piltide ning suurendamisega.
|
| 236 |
+
- [ ] `C-02 P0` Näita pindala, korruseid, ruume, konstruktsiooni, materjale ja sobivaid pakette.
|
| 237 |
+
- [ ] `C-03 P0` Lisa “Kasuta lähtepunktina” tegevus, mis eeltäidab konfiguraatori.
|
| 238 |
+
- [ ] `C-04 P1` Lisa kohandatavate ja fikseeritud omaduste eristus.
|
| 239 |
+
- [ ] `C-05 P1` Lisa PDF-plaanide või brošüüri allalaadimine.
|
| 240 |
+
- [ ] `C-06 P2` Lisa GLB/GLTF 3D-mudeli vaatur ja mobiilis AR-lingi võimalus.
|
| 241 |
+
|
| 242 |
+
Vastuvõtt: projekti valik kandub konfiguraatorisse ilma andmekadudeta.
|
| 243 |
+
|
| 244 |
+
### Epic D – konfiguraator ja hinnastamine
|
| 245 |
+
|
| 246 |
+
- [ ] `D-01 P0` Loo mitmeastmeline vorm: kasutusotstarve, piirkond, krunt, pindala, ruumid, konstruktsioon, katus, küte, viimistlus ja ajakava.
|
| 247 |
+
- [ ] `D-02 P0` Näita sammude edenemist ja puuduvaid kohustuslikke andmeid.
|
| 248 |
+
- [ ] `D-03 P0` Hoia hinnareeglid administreeritavas `PriceRule` mudelis.
|
| 249 |
+
- [ ] `D-04 P0` Arvuta minimaalse ja maksimaalse hinna vahemik, mitte eksitav üksikhind.
|
| 250 |
+
- [ ] `D-05 P0` Näita, millised valikud hinda suurendavad või vähendavad.
|
| 251 |
+
- [ ] `D-06 P1` Arvuta teenusepaketi ajakava ja projekti valmidusaste.
|
| 252 |
+
- [ ] `D-07 P1` Salvesta anonüümne mustand brauserisse ning sisselogitud kliendi mustand andmebaasi.
|
| 253 |
+
- [ ] `D-08 P1` Genereeri versioonitud projektibrief JSON- ja PDF-kujul.
|
| 254 |
+
- [ ] `D-09 P2` Lisa konsultandi kinnitatud hinnastamise režiim ja pakkumise versioonid.
|
| 255 |
+
|
| 256 |
+
Vastuvõtt: sama sisend annab sama hinnangu; reeglite muudatus on auditeeritav; tulemusel on kuupäev ja lahtiütlus.
|
| 257 |
+
|
| 258 |
+
### Epic E – päringud ja müügiprotsess
|
| 259 |
+
|
| 260 |
+
- [ ] `E-01 P0` Loo serveripoolne `/api/leads` endpoint.
|
| 261 |
+
- [ ] `E-02 P0` Valideeri ja puhasta kõik väljad serveris.
|
| 262 |
+
- [ ] `E-03 P0` Salvesta päring, brief, nõusoleku versioon, ajatempel ja allikas andmebaasi.
|
| 263 |
+
- [ ] `E-04 P0` Saada kliendile kinnituskiri ning müügile struktureeritud teavitus.
|
| 264 |
+
- [ ] `E-05 P0` Lisa rämpspostikaitse: honeypot, kiirusepiirang ja vajadusel Turnstile.
|
| 265 |
+
- [ ] `E-06 P1` Loo unikaalne päringu viitenumber.
|
| 266 |
+
- [ ] `E-07 P1` Lisa Cal.comi või Google Calendari konsultatsiooni broneerimine.
|
| 267 |
+
- [ ] `E-08 P1` Lisa CRM-integratsiooni adapter, mitte teenusepõhine loogika otse vormi.
|
| 268 |
+
- [ ] `E-09 P2` Lisa automaatsed järeltegevused vastavalt valitud paketile.
|
| 269 |
+
|
| 270 |
+
Vastuvõtt: päring ei kao, dubleeritud submit ei loo topeltkirjet ja klient saab kinnituse.
|
| 271 |
+
|
| 272 |
+
### Epic F – administraatori töölaud
|
| 273 |
+
|
| 274 |
+
- [ ] `F-01 P0` Lisa turvaline sisselogimine ja rollipõhine ligipääs.
|
| 275 |
+
- [ ] `F-02 P0` Loo projektide, pakettide, hindade ja meedia CRUD.
|
| 276 |
+
- [ ] `F-03 P0` Loo päringute tabel otsingu, filtrite, staatuste ja vastutajaga.
|
| 277 |
+
- [ ] `F-04 P1` Lisa päringu detailvaade koos briefi, ajajoone ja märkmetega.
|
| 278 |
+
- [ ] `F-05 P1` Lisa CSV-eksport ja kustutustaotluse töövoog.
|
| 279 |
+
- [ ] `F-06 P1` Lisa sisu eelvaade ning mustand/avaldatud olek.
|
| 280 |
+
- [ ] `F-07 P2` Lisa auditilogi hinnareeglite, sisu ja päringu staatuse muudatustele.
|
| 281 |
+
|
| 282 |
+
Vastuvõtt: avalik kasutaja ei pääse admin-andmetele ning iga muutus on seotud kasutajaga.
|
| 283 |
+
|
| 284 |
+
### Epic G – 3D ja AI
|
| 285 |
+
|
| 286 |
+
- [ ] `G-01 P1` Lisa päris 3D-renderduste haldus ja optimeerimine.
|
| 287 |
+
- [ ] `G-02 P2` Lisa GLB-mudelite vaatur koos varulaadimise ning jõudluspiiridega.
|
| 288 |
+
- [ ] `G-03 P2` Loo serveripoolne idee analüüs, mis tagastab struktureeritud JSON-i.
|
| 289 |
+
- [ ] `G-04 P2` Seo analüüsi väljund konfiguraatori väljadega ainult kasutaja kinnituse järel.
|
| 290 |
+
- [ ] `G-05 P2` Lisa pildigeneratsioon ainult serveri kaudu ja selge “kontseptsioon, mitte projektjoonis” märgistusega.
|
| 291 |
+
- [ ] `G-06 P2` Salvesta prompt, mudeliversioon, nõusolek ja genereeritud vara päritolu.
|
| 292 |
+
|
| 293 |
+
Vastuvõtt: AI ei anna siduvat konstruktsiooni- ega hinnalubadust ja kasutaja näeb, millised väljad automaatselt pakuti.
|
| 294 |
+
|
| 295 |
+
### Epic H – kvaliteet, turvalisus ja privaatsus
|
| 296 |
+
|
| 297 |
+
- [ ] `H-01 P0` Lisa CSP, turvalised päised, HTTPS ja sõltuvuste kontroll.
|
| 298 |
+
- [ ] `H-02 P0` Hoia saladused ainult Space Secretsis või tootmiskeskkonna secrets-halduses.
|
| 299 |
+
- [ ] `H-03 P0` Lisa privaatsusteade, küpsiste loogika ja nõusoleku versioonimine.
|
| 300 |
+
- [ ] `H-04 P0` Määra päringute säilitusaeg ja kustutamisprotsess.
|
| 301 |
+
- [ ] `H-05 P0` Tee WCAG 2.2 AA ligipääsetavuse kontroll.
|
| 302 |
+
- [ ] `H-06 P0` Testi telefonis, tahvlis ning Chrome’i, Edge’i, Safari ja Firefoxiga.
|
| 303 |
+
- [ ] `H-07 P1` Lisa unit-, integratsiooni- ja end-to-end testid kriitilisele ostuteekonnale.
|
| 304 |
+
- [ ] `H-08 P1` Saavuta Lighthouse sihid: Performance ≥ 90, Accessibility ≥ 95, SEO ≥ 95.
|
| 305 |
+
- [ ] `H-09 P1` Lisa varundus ja taastamistest andmebaasile ning meediale.
|
| 306 |
+
|
| 307 |
+
Vastuvõtt: P0 turva- või ligipääsetavusvigu ei ole.
|
| 308 |
+
|
| 309 |
+
### Epic I – SEO ja analüütika
|
| 310 |
+
|
| 311 |
+
- [ ] `I-01 P0` Lisa unikaalsed title, description, canonical ja Open Graph andmed.
|
| 312 |
+
- [ ] `I-02 P0` Lisa Organization, Service, BreadcrumbList ja Project/CreativeWork struktureeritud andmed.
|
| 313 |
+
- [ ] `I-03 P0` Loo sitemap ja robots konfiguratsioon.
|
| 314 |
+
- [ ] `I-04 P1` Mõõda sündmused: projekti vaatamine, lemmik, paketi valik, kalkulaatori lõpetamine ja päringu saatmine.
|
| 315 |
+
- [ ] `I-05 P1` Säilita UTM parameetrid päringuga.
|
| 316 |
+
- [ ] `I-06 P1` Loo müügile funnel-raport ning kanalite konversioon.
|
| 317 |
+
- [ ] `I-07 P2` Lisa eesti, inglise ja soome keele arhitektuur.
|
| 318 |
+
|
| 319 |
+
Vastuvõtt: isikuandmeid ei saadeta analüütikasse ning sündmuste nimed on dokumenteeritud.
|
| 320 |
+
|
| 321 |
+
### Epic J – Hugging Face Space ja tootmine
|
| 322 |
+
|
| 323 |
+
- [ ] `J-01 P0` Avalda praegune demo Static Space’ina.
|
| 324 |
+
- [ ] `J-02 P0` Kontrolli, et `README.md` sisaldab `sdk: static` ja `app_file: index.html`.
|
| 325 |
+
- [ ] `J-03 P0` Lisa automaatne kontroll HTML-i, JavaScripti ja katkiste linkide jaoks.
|
| 326 |
+
- [ ] `J-04 P1` Kui lisatakse server või andmebaas, migreeri Docker Space’ile või eraldi tootmishostingule.
|
| 327 |
+
- [ ] `J-05 P1` Hoia demoandmed ja tootmisandmed täielikult lahus.
|
| 328 |
+
- [ ] `J-06 P1` Lisa keskkonnamuutujad, health check, logid ja veajälgimine.
|
| 329 |
+
- [ ] `J-07 P1` Sea Space Secretid ainult serveripoolsetele integratsioonidele.
|
| 330 |
+
- [ ] `J-08 P2` Lisa staging-keskkond enne avalikku tootmisversiooni.
|
| 331 |
+
|
| 332 |
+
Vastuvõtt: repo värskendamine ehitab Space’i veatult; saladusi ei leidu failides ega brauseri lähtekoodis.
|
| 333 |
+
|
| 334 |
+
## 8. Minimaalne API leping tootmiskeskkonnale
|
| 335 |
+
|
| 336 |
+
```text
|
| 337 |
+
GET /api/projects
|
| 338 |
+
GET /api/projects/:slug
|
| 339 |
+
GET /api/packages
|
| 340 |
+
POST /api/estimate
|
| 341 |
+
POST /api/leads
|
| 342 |
+
GET /api/leads/:reference/status
|
| 343 |
+
POST /api/briefs
|
| 344 |
+
GET /api/briefs/:id.pdf
|
| 345 |
+
POST /api/auth/login
|
| 346 |
+
GET /api/admin/leads
|
| 347 |
+
PATCH /api/admin/leads/:id
|
| 348 |
+
POST /api/admin/projects
|
| 349 |
+
PATCH /api/admin/projects/:id
|
| 350 |
+
```
|
| 351 |
+
|
| 352 |
+
Kõigil mutatsioonidel peab olema serveripoolne valideerimine. Admin-endpointidel peab olema autentimine, rollikontroll, auditilogi ja CSRF-kaitse vastavalt valitud autentimismudelile.
|
| 353 |
+
|
| 354 |
+
## 9. Teststsenaariumid
|
| 355 |
+
|
| 356 |
+
1. Kasutaja filtreerib alla 100 m² eramud ja näeb ainult sobivaid projekte.
|
| 357 |
+
2. Kasutaja lisab projekti lemmikuks, värskendab lehte ja lemmik säilib.
|
| 358 |
+
3. Kasutaja valib projekti lähtepunktiks ning pindala ja hoonetüüp eeltäidetakse.
|
| 359 |
+
4. Paketi vahetamine muudab hinda, ajakava, etappe ja üleantavaid materjale.
|
| 360 |
+
5. Sama konfiguratsioon annab pärast lehe värskendamist taastatud mustandis sama tulemuse.
|
| 361 |
+
6. Projektibrief sisaldab valitud projekti, tehnilisi eelistusi, hinnavahemikku ja lahtiütlust.
|
| 362 |
+
7. Puuduv nõusolek või vigane e-post takistab päringu saatmist.
|
| 363 |
+
8. Honeypot-välja täitnud robot ei loo päringut.
|
| 364 |
+
9. Topeltklõps submit-nupul ei loo kaht päringut.
|
| 365 |
+
10. Klaviatuuriga saab avada ja sulgeda detailmodaali ning läbida kogu vormi.
|
| 366 |
+
11. Mobiilivaates ei teki horisontaalset kerimist.
|
| 367 |
+
12. Ükski frontend-fail ei sisalda API-võtit ega parooli.
|
| 368 |
+
|
| 369 |
+
## 10. Definition of Done
|
| 370 |
+
|
| 371 |
+
Funktsioon loetakse valmis ainult siis, kui:
|
| 372 |
+
|
| 373 |
+
- äriline eesmärk ja kasutajateekond on dokumenteeritud;
|
| 374 |
+
- disain töötab vähemalt 360 px kuni 1440 px laiuses;
|
| 375 |
+
- tühjad, laadivad, edukad ja veaolekud on olemas;
|
| 376 |
+
- sisend valideeritakse kliendis ja serveris;
|
| 377 |
+
- ligipääsetav nimi ning klaviatuuritugi on olemas;
|
| 378 |
+
- testid läbivad CI-s;
|
| 379 |
+
- analüütikasündmus on lisatud, kui funktsioon kuulub funnelisse;
|
| 380 |
+
- privaatsus- ja turvamõju on kontrollitud;
|
| 381 |
+
- sisu on kinnitatud ega esita näidisandmeid päris klienditööna;
|
| 382 |
+
- dokumentatsioon ja juurutusjuhis on uuendatud.
|
| 383 |
+
|
| 384 |
+
## 11. Tehniline juurutusotsus
|
| 385 |
+
|
| 386 |
+
### Praegune demo
|
| 387 |
+
|
| 388 |
+
Kasuta Hugging Face **Static Space’i**. See vajab ainult `README.md`, `index.html` ja `app.js` faile. Kõik funktsioonid töötavad brauseris.
|
| 389 |
+
|
| 390 |
+
### Tootmiskõlblik versioon
|
| 391 |
+
|
| 392 |
+
Kui lisanduvad päringute andmebaas, admin, e-kirjad või serveripoolne AI, kasuta:
|
| 393 |
+
|
| 394 |
+
- Docker Space + FastAPI/PostgreSQL; või
|
| 395 |
+
- eraldi frontendi ja hallatud backend’i, näiteks Supabase/PostgreSQL.
|
| 396 |
+
|
| 397 |
+
Hugging Face Static Space ei sobi saladusi vajava vormi-API hoidmiseks, sest kogu JavaScript on avalik. Staatiline demo võib jätkata eraldi, kuid tootmisandmeid ei tohi demo `localStorage`-isse või avalikku reposse kirjutada.
|
| 398 |
+
|
ANTIGRAVITY_HANDOFF.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Antigravity handoff: MTMehitus Space
|
| 2 |
+
|
| 3 |
+
## Space
|
| 4 |
+
|
| 5 |
+
Sihtrepositoorium:
|
| 6 |
+
|
| 7 |
+
```text
|
| 8 |
+
https://huggingface.co/spaces/techprotrade/MTMehitus
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
Space’i tüüp:
|
| 12 |
+
|
| 13 |
+
```text
|
| 14 |
+
Hugging Face Static Space
|
| 15 |
+
sdk: static
|
| 16 |
+
app_file: index.html
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
## Antigravityle antav ülesanne
|
| 20 |
+
|
| 21 |
+
```text
|
| 22 |
+
Tööta olemasoleva Hugging Face Space’i repositooriumiga:
|
| 23 |
+
https://huggingface.co/spaces/techprotrade/MTMehitus
|
| 24 |
+
|
| 25 |
+
Loe enne muudatuste tegemist täielikult:
|
| 26 |
+
1. README.md
|
| 27 |
+
2. ANTIGRAVITY_BUILDER_SPEC.md
|
| 28 |
+
3. ANTIGRAVITY_HANDOFF.md
|
| 29 |
+
4. index.html
|
| 30 |
+
5. app.js
|
| 31 |
+
|
| 32 |
+
Projekti eesmärk:
|
| 33 |
+
MTMehitus ei ole valmis majade või kinnisvara müügiportaal. See on 3D-disainide, ehitusprojektide, kohandatavate näidislahenduste ja võtmed-kätte täislahenduste müügi- ning päringukeskkond.
|
| 34 |
+
|
| 35 |
+
Säilita praegu töötavad funktsioonid:
|
| 36 |
+
- kuue näidisega projektikataloog;
|
| 37 |
+
- otsing, filtrid ja sortimine;
|
| 38 |
+
- lemmikud;
|
| 39 |
+
- projektide detailvaade;
|
| 40 |
+
- projekti valimine lähtepunktiks;
|
| 41 |
+
- paketivõrdlus;
|
| 42 |
+
- hinnavahemiku ja ajakava arvutus;
|
| 43 |
+
- projektibrief;
|
| 44 |
+
- mustandi salvestamine;
|
| 45 |
+
- allalaadimine, printimine, e-post ja WhatsApp;
|
| 46 |
+
- valideeritud kontaktivorm.
|
| 47 |
+
|
| 48 |
+
Arenda funktsioone ainult väikeste kontrollitavate commit’idega. Iga commiti järel:
|
| 49 |
+
1. kontrolli JavaScripti süntaksit;
|
| 50 |
+
2. testi avalehe laadimist;
|
| 51 |
+
3. testi kataloogi filtreid;
|
| 52 |
+
4. testi projekti valimist;
|
| 53 |
+
5. testi paketivahetust ja hinnakalkulatsiooni;
|
| 54 |
+
6. testi mustandi taastamist;
|
| 55 |
+
7. kontrolli mobiilivaadet;
|
| 56 |
+
8. kontrolli, et Space’i YAML metadata jäi kehtivaks.
|
| 57 |
+
|
| 58 |
+
Ära lisa frontend-koodi API-võtmeid ega paroole.
|
| 59 |
+
Ära nimeta näidisprojekti päris klienditööks ilma tõendi ja avaldamisloata.
|
| 60 |
+
Ära muuda kalkulaatori tulemust siduvaks hinnapakkumiseks.
|
| 61 |
+
Ära eemalda hinnavahemiku lahtiütlust.
|
| 62 |
+
|
| 63 |
+
Esimene arendusetapp:
|
| 64 |
+
1. asenda ikoonidega näidised päris optimeeritud 3D-renderdustega;
|
| 65 |
+
2. tõsta projektiandmed app.js failist data/projects.json faili;
|
| 66 |
+
3. lisa projektide unikaalsed detail-URL-id;
|
| 67 |
+
4. lisa kuni kolme projekti võrdlus;
|
| 68 |
+
5. lisa eelarveklass ja soovitud algusaeg konfiguraatorisse;
|
| 69 |
+
6. lisa katkiste piltide varuvaade;
|
| 70 |
+
7. lisa täielik klaviatuuritugi modaalile;
|
| 71 |
+
8. lisa SEO ja Open Graph metadata;
|
| 72 |
+
9. lisa projektide JSON-LD struktureeritud andmed;
|
| 73 |
+
10. kirjuta automaattestid kriitilisele kasutajateekonnale.
|
| 74 |
+
|
| 75 |
+
Kui lisad päringute andmebaasi, e-posti saatmise, administraatori või serveripoolse AI:
|
| 76 |
+
- migreeri Space Docker SDK-le või kasuta eraldi turvalist backend’i;
|
| 77 |
+
- hoia saladused Hugging Face Space Secretsis;
|
| 78 |
+
- valideeri sisend serveris;
|
| 79 |
+
- lisa rämpspostikaitse, nõusoleku logi ja andmete säilitamisreeglid.
|
| 80 |
+
|
| 81 |
+
Põhjalik backlog, andmemudelid, API leping ja Definition of Done asuvad failis ANTIGRAVITY_BUILDER_SPEC.md.
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
## Soovitatud tööviis
|
| 85 |
+
|
| 86 |
+
Antigravity peaks tegema iga suurema funktsiooni eraldi branch’is või väikese commit’ina. Space’i `main` haru peab jääma alati käivitatavaks. Enne merge’i tuleb kontrollida vähemalt:
|
| 87 |
+
|
| 88 |
+
- staatilise Space’i build;
|
| 89 |
+
- JavaScripti runtime-vead;
|
| 90 |
+
- 360 px mobiilivaade;
|
| 91 |
+
- projektikataloogi filtrid;
|
| 92 |
+
- kalkulaatori kolm paketti;
|
| 93 |
+
- andmete püsimine pärast värskendamist;
|
| 94 |
+
- kontaktivormi valideerimine;
|
| 95 |
+
- saladuste puudumine repositooriumis.
|
| 96 |
+
|
README.md
CHANGED
|
@@ -1,10 +1,54 @@
|
|
| 1 |
---
|
| 2 |
title: MTMehitus
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
|
|
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: MTMehitus
|
| 3 |
+
emoji: 🏗️
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: yellow
|
| 6 |
sdk: static
|
| 7 |
+
app_file: index.html
|
| 8 |
pinned: false
|
| 9 |
+
short_description: 3D-disainid, ehitusprojektid ja täislahendused
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# MTMehitus
|
| 13 |
+
|
| 14 |
+
Interaktiivne eestikeelne koduleht 3D-disainide, ehitusprojektide ja võtmed-kätte täislahenduste tutvustamiseks ning müügipäringute koostamiseks.
|
| 15 |
+
|
| 16 |
+
Planeeritud Space’i repositoorium: `techprotrade/MTMehitus`.
|
| 17 |
+
|
| 18 |
+
## Space’is töötavad funktsioonid
|
| 19 |
+
|
| 20 |
+
- filtreeritav kuue näidislahendusega projektikataloog;
|
| 21 |
+
- lemmikute salvestamine külastaja brauserisse;
|
| 22 |
+
- projekti detailvaade ja kohandamisvõimalused;
|
| 23 |
+
- 3D-kontseptsiooni, ehitusprojekti ja täislahenduse võrdlus;
|
| 24 |
+
- pindala ja tehniliste valikute järgi muutuv hinnavahemik;
|
| 25 |
+
- automaatne tööetappide, ajakava ja valmidusastme arvutus;
|
| 26 |
+
- näidisprojekti kasutamine uue projekti lähtepunktina;
|
| 27 |
+
- nutikas tekstipõhine projektisoovitus ilma API-võtmeta;
|
| 28 |
+
- projektimustandi taastamine brauseri `localStorage` abil;
|
| 29 |
+
- lähteülesande allalaadimine, kopeerimine ja PDF-ina printimine;
|
| 30 |
+
- e-posti ning WhatsAppi kaudu jagamine;
|
| 31 |
+
- kontaktivormi valideerimine ja e-kirja mustandi avamine.
|
| 32 |
+
|
| 33 |
+
## Failid
|
| 34 |
+
|
| 35 |
+
- `index.html` – kujundus, sisu ja ligipääsetav HTML-struktuur;
|
| 36 |
+
- `app.js` – kataloog, kalkulaator, salvestamine, jagamine ja vormiloogika;
|
| 37 |
+
- `ANTIGRAVITY_BUILDER_SPEC.md` – tootmiskõlbliku versiooni täielik ehitusplaan.
|
| 38 |
+
- `ANTIGRAVITY_HANDOFF.md` – valmis juhis, mille saab koos Space’i aadressiga Antigravityle anda.
|
| 39 |
+
|
| 40 |
+
## Hugging Face Space’i lisamine
|
| 41 |
+
|
| 42 |
+
1. Loo uus Hugging Face Space.
|
| 43 |
+
2. Vali SDK-ks **Static**.
|
| 44 |
+
3. Lisa selle kausta failid Space’i repositooriumi juurkausta.
|
| 45 |
+
4. Kontrolli, et `README.md` YAML-plokis oleks `sdk: static` ja `app_file: index.html`.
|
| 46 |
+
5. Tee commit. Space avaldab lehe automaatselt.
|
| 47 |
+
|
| 48 |
+
Hugging Face’i ametliku konfiguratsiooniviite järgi võib staatiline Space kasutada HTML/CSS/JavaScripti ning `app_file` peab osutama renderdatavale HTML-failile.
|
| 49 |
+
|
| 50 |
+
## Enne avalikku kasutust
|
| 51 |
+
|
| 52 |
+
Praegune versioon on toimiv müügi- ja demonstratsioonirakendus, kuid kontaktivorm ei saada andmeid serverisse. See koostab e-kirja külastaja enda e-posti rakenduses. Tootmiskeskkonnas tuleb lisada turvaline vormi-API, nõusolekute logi, päringute andmebaas ja administraatori töölaud.
|
| 53 |
+
|
| 54 |
+
Projektikaartide graafilised ikoonid tuleb asendada MTM Ehituse päris 3D-renderduste ning kontrollitud projektiandmetega. Ära avalda kliendi nime, aadressi ega projekti staatust ilma loata.
|
app.js
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* MTM Project Studio – browser-native sales application.
|
| 2 |
+
Works without API keys or a backend. Leads are prepared as email drafts and
|
| 3 |
+
project drafts/favorites are stored only in the visitor's browser. */
|
| 4 |
+
|
| 5 |
+
const MTM_EMAIL = "info@mtmehitus.ee";
|
| 6 |
+
const MTM_PHONE = "3725071042";
|
| 7 |
+
const storageKeys = {
|
| 8 |
+
favorites: "mtm-project-favorites-v1",
|
| 9 |
+
draft: "mtm-project-draft-v1",
|
| 10 |
+
leads: "mtm-project-leads-v1"
|
| 11 |
+
};
|
| 12 |
+
|
| 13 |
+
const projects = [
|
| 14 |
+
{
|
| 15 |
+
id: "nordic-140",
|
| 16 |
+
name: "Nordic 140",
|
| 17 |
+
type: "home",
|
| 18 |
+
typeLabel: "Eramu",
|
| 19 |
+
area: 140,
|
| 20 |
+
rooms: 5,
|
| 21 |
+
floors: 1,
|
| 22 |
+
style: "Põhjamaine",
|
| 23 |
+
material: "Puit ja krohv",
|
| 24 |
+
designPrice: 1850,
|
| 25 |
+
status: "Kliendi valitud suund",
|
| 26 |
+
package: "3D-disain + kohandamine",
|
| 27 |
+
icon: "fa-house-chimney-window",
|
| 28 |
+
gradient: "linear-gradient(135deg,#78909c,#263238)",
|
| 29 |
+
description: "Valgusküllane ühekorruseline lahendus, kus köök ja elutuba avanevad terrassile. Selge ruumiloogika sobib nii linnalähedasele kui maapiirkonna krundile.",
|
| 30 |
+
customize: "Pindala 115–165 m², 3–4 magamistuba, fassaadimaterjalid, katusekalle, terrassi asukoht ja tehnosüsteemid."
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
id: "compact-92",
|
| 34 |
+
name: "Compact 92",
|
| 35 |
+
type: "home",
|
| 36 |
+
typeLabel: "Eramu",
|
| 37 |
+
area: 92,
|
| 38 |
+
rooms: 4,
|
| 39 |
+
floors: 1,
|
| 40 |
+
style: "Ajatu",
|
| 41 |
+
material: "Puitkarkass",
|
| 42 |
+
designPrice: 1450,
|
| 43 |
+
status: "Projektina valitud",
|
| 44 |
+
package: "3D-disain + ehitusprojekt",
|
| 45 |
+
icon: "fa-house",
|
| 46 |
+
gradient: "linear-gradient(135deg,#90a4ae,#455a64)",
|
| 47 |
+
description: "Kompaktne viilkatusega kodu, mis annab väikese ehitusaluse pinna juures hea ruumikasutuse ja kontrollitava ehituseelarve.",
|
| 48 |
+
customize: "Pindala 78–110 m², 2–3 magamistuba, varjualune, terrass, katusematerjal ja siseplaneering."
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
id: "courtyard-168",
|
| 52 |
+
name: "Courtyard 168",
|
| 53 |
+
type: "home",
|
| 54 |
+
typeLabel: "Eramu",
|
| 55 |
+
area: 168,
|
| 56 |
+
rooms: 6,
|
| 57 |
+
floors: 1,
|
| 58 |
+
style: "Modernne",
|
| 59 |
+
material: "Kergplokk ja puit",
|
| 60 |
+
designPrice: 2450,
|
| 61 |
+
status: "Täislahenduse lähteprojekt",
|
| 62 |
+
package: "Disainist teostuseni",
|
| 63 |
+
icon: "fa-building",
|
| 64 |
+
gradient: "linear-gradient(135deg,#8d6e63,#3e2723)",
|
| 65 |
+
description: "L-kujuline sisehooviga lahendus eraldab privaatse magamistubade tiiva ja avatud eluala. Hoov loob tuulevarjulise väliruumi.",
|
| 66 |
+
customize: "Pindala 145–195 m², garaaž või varjualune, sisehoovi suurus, fassaadikombinatsioon ja ruumide arv."
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
id: "forest-76",
|
| 70 |
+
name: "Forest 76",
|
| 71 |
+
type: "summer",
|
| 72 |
+
typeLabel: "Puhkemaja",
|
| 73 |
+
area: 76,
|
| 74 |
+
rooms: 3,
|
| 75 |
+
floors: 1,
|
| 76 |
+
style: "Looduslähedane",
|
| 77 |
+
material: "Termopuit",
|
| 78 |
+
designPrice: 1350,
|
| 79 |
+
status: "Kohandatav näidislahendus",
|
| 80 |
+
package: "3D-kontseptsioon",
|
| 81 |
+
icon: "fa-tree-city",
|
| 82 |
+
gradient: "linear-gradient(135deg,#7cb342,#1b5e20)",
|
| 83 |
+
description: "Kompaktne puhkemaja suure klaasfassaadi, saunaploki ja kaetud terrassiga. Sobib metsa, mere või järve lähedale.",
|
| 84 |
+
customize: "Pindala 55–95 m², saun, magamiskohtade arv, terrass, vai- või plaatvundament ja aastaringne kasutus."
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
id: "studio-48",
|
| 88 |
+
name: "Studio 48",
|
| 89 |
+
type: "outbuilding",
|
| 90 |
+
typeLabel: "Stuudio / abihoone",
|
| 91 |
+
area: 48,
|
| 92 |
+
rooms: 2,
|
| 93 |
+
floors: 1,
|
| 94 |
+
style: "Minimalistlik",
|
| 95 |
+
material: "Puit ja plekk",
|
| 96 |
+
designPrice: 980,
|
| 97 |
+
status: "Kohandatav näidislahendus",
|
| 98 |
+
package: "3D-disain",
|
| 99 |
+
icon: "fa-warehouse",
|
| 100 |
+
gradient: "linear-gradient(135deg,#607d8b,#102027)",
|
| 101 |
+
description: "Paindlik väikehoone kodukontoriks, stuudioks, külalistemajaks või rendipinnaks. Tehnosüsteemid saab lahendada põhiehitisega koos või eraldi.",
|
| 102 |
+
customize: "Pindala 30–60 m², pesuruum, kööginurk, eraldi sissepääsud, fassaad ja tehniline valmidus."
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
id: "workhub-210",
|
| 106 |
+
name: "WorkHub 210",
|
| 107 |
+
type: "office",
|
| 108 |
+
typeLabel: "Ärihoone",
|
| 109 |
+
area: 210,
|
| 110 |
+
rooms: 8,
|
| 111 |
+
floors: 2,
|
| 112 |
+
style: "Kaasaegne",
|
| 113 |
+
material: "Sandwich-paneel ja klaas",
|
| 114 |
+
designPrice: 2900,
|
| 115 |
+
status: "Ärilahenduse kontseptsioon",
|
| 116 |
+
package: "3D-disain + põhiprojekt",
|
| 117 |
+
icon: "fa-city",
|
| 118 |
+
gradient: "linear-gradient(135deg,#5c6bc0,#1a237e)",
|
| 119 |
+
description: "Kahekorruseline paindlik töö- ja teenindushoone, mille ruume saab jagada ühe või mitme kasutaja vahel.",
|
| 120 |
+
customize: "Pindala 160–320 m², töökohtade arv, lao- või teenindusala, parkimine, reklaampinnad ja energialahendus."
|
| 121 |
+
}
|
| 122 |
+
];
|
| 123 |
+
|
| 124 |
+
const packageMeta = {
|
| 125 |
+
concept: {
|
| 126 |
+
name: "3D-kontseptsioon",
|
| 127 |
+
duration: "3–6 nädalat",
|
| 128 |
+
stages: ["Lähteülesanne", "Eskiissuund", "3D-mudel", "Visualiseeringud", "Üleandmine"],
|
| 129 |
+
descriptions: [
|
| 130 |
+
"Vajadused, krundiinfo, ruumiprogramm ja eesmärgid.",
|
| 131 |
+
"Kaks ruumilist suunda ja eelistatud variandi valik.",
|
| 132 |
+
"Mahud, fassaadid, avad ja põhikompositsioon.",
|
| 133 |
+
"Materjalid, valgus ja esitlusvaated.",
|
| 134 |
+
"Failipakett ning soovitused järgmise etapi jaoks."
|
| 135 |
+
],
|
| 136 |
+
shares: [0.15, 0.2, 0.3, 0.25, 0.1],
|
| 137 |
+
deliverables: ["Lähteülesanne ja ruumiprogramm", "2 eskiissuunda", "3D-mudel ja 4 vaadet", "Materjali- ja värvikaart"]
|
| 138 |
+
},
|
| 139 |
+
project: {
|
| 140 |
+
name: "Ehitusprojekt",
|
| 141 |
+
duration: "8–16 nädalat",
|
| 142 |
+
stages: ["Lähteülesanne", "3D-kontseptsioon", "Põhijoonised", "Tehniline kooskõla", "Üleandmine"],
|
| 143 |
+
descriptions: [
|
| 144 |
+
"Krundiinfo, ruumiprogramm ja tehnilised eesmärgid.",
|
| 145 |
+
"Mahud, plaaniloogika, välisilme ja materjalid.",
|
| 146 |
+
"Plaanid, vaated, lõiked ja põhikonstruktsioonid.",
|
| 147 |
+
"Konstruktsioonide ja tehnosüsteemide lähteinfo.",
|
| 148 |
+
"Koondfailid, konsultatsioon ja järgmiste sammude plaan."
|
| 149 |
+
],
|
| 150 |
+
shares: [0.12, 0.22, 0.3, 0.24, 0.12],
|
| 151 |
+
deliverables: ["Lähteülesanne ja ruumiprogramm", "3D-kontseptsioon ja materjalid", "Plaanid, vaated ja lõiked", "Tehniliste osade lähteinfo"]
|
| 152 |
+
},
|
| 153 |
+
turnkey: {
|
| 154 |
+
name: "Täislahendus",
|
| 155 |
+
duration: "9–16 kuud",
|
| 156 |
+
stages: ["Projekteerimine", "Vundament", "Konstruktsioonid", "Tehnosüsteemid", "Viimistlus ja üleandmine"],
|
| 157 |
+
descriptions: [
|
| 158 |
+
"Disain, projekt, kooskõlastused ja detailne eelarve.",
|
| 159 |
+
"Pinnasetööd, vundament, soojustus ja aluspõrand.",
|
| 160 |
+
"Kandekonstruktsioonid, katus, avatäited ja fassaad.",
|
| 161 |
+
"Elekter, vesi, kanalisatsioon, küte ja ventilatsioon.",
|
| 162 |
+
"Siseviimistlus, seadistus, kontroll ja dokumentatsioon."
|
| 163 |
+
],
|
| 164 |
+
shares: [0.08, 0.14, 0.3, 0.21, 0.27],
|
| 165 |
+
deliverables: ["Disain ja ehitusprojekt", "Detailne eelarve ja ajakava", "Ehitusjuhtimine ja kvaliteedikontroll", "Valmis objekt ja üleandmisdokumendid"]
|
| 166 |
+
}
|
| 167 |
+
};
|
| 168 |
+
|
| 169 |
+
let appState = {
|
| 170 |
+
package: "project",
|
| 171 |
+
selectedProjectId: null,
|
| 172 |
+
favoritesOnly: false,
|
| 173 |
+
estimate: null
|
| 174 |
+
};
|
| 175 |
+
|
| 176 |
+
let configState = {
|
| 177 |
+
area: 120,
|
| 178 |
+
floors: 1,
|
| 179 |
+
foundation: "plate",
|
| 180 |
+
wall: "block",
|
| 181 |
+
roof: "gable-tin",
|
| 182 |
+
heating: "geo",
|
| 183 |
+
finish: "turnkey",
|
| 184 |
+
region: "harju",
|
| 185 |
+
use: "home"
|
| 186 |
+
};
|
| 187 |
+
|
| 188 |
+
function safeJsonRead(key, fallback) {
|
| 189 |
+
try {
|
| 190 |
+
const parsed = JSON.parse(localStorage.getItem(key));
|
| 191 |
+
return parsed ?? fallback;
|
| 192 |
+
} catch {
|
| 193 |
+
return fallback;
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
function saveJson(key, value) {
|
| 198 |
+
try {
|
| 199 |
+
localStorage.setItem(key, JSON.stringify(value));
|
| 200 |
+
return true;
|
| 201 |
+
} catch {
|
| 202 |
+
return false;
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
function formatCurrency(value) {
|
| 207 |
+
return Math.round(value).toLocaleString("et-EE") + " €";
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
function packageValueToForm(value) {
|
| 211 |
+
return packageMeta[value]?.name || "Ehitusprojekt";
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
function showToast(message) {
|
| 215 |
+
const toast = document.getElementById("toast");
|
| 216 |
+
toast.textContent = message;
|
| 217 |
+
toast.classList.add("show");
|
| 218 |
+
clearTimeout(showToast.timer);
|
| 219 |
+
showToast.timer = setTimeout(() => toast.classList.remove("show"), 3200);
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
window.selectOption = function(type, value, element) {
|
| 223 |
+
configState[type] = value;
|
| 224 |
+
element.parentElement.querySelectorAll(".option-card").forEach(card => card.classList.remove("selected"));
|
| 225 |
+
element.classList.add("selected");
|
| 226 |
+
updateCalculator();
|
| 227 |
+
};
|
| 228 |
+
|
| 229 |
+
window.switchTab = function(tabName) {
|
| 230 |
+
document.querySelectorAll(".tab-btn").forEach(btn => btn.classList.remove("active"));
|
| 231 |
+
document.querySelectorAll(".tab-content").forEach(content => content.classList.remove("active"));
|
| 232 |
+
const custom = tabName === "customizer";
|
| 233 |
+
document.querySelectorAll(".tab-btn")[custom ? 0 : 1].classList.add("active");
|
| 234 |
+
document.getElementById(custom ? "tab-customizer" : "tab-ai-prompt").classList.add("active");
|
| 235 |
+
};
|
| 236 |
+
|
| 237 |
+
window.selectPackage = function(packageId, element) {
|
| 238 |
+
appState.package = packageId;
|
| 239 |
+
document.querySelectorAll(".package-option").forEach(card => card.classList.remove("selected"));
|
| 240 |
+
const target = element || document.querySelector(`[data-package="${packageId}"]`);
|
| 241 |
+
if (target) target.classList.add("selected");
|
| 242 |
+
document.getElementById("form-package").value = packageValueToForm(packageId);
|
| 243 |
+
updateCalculator();
|
| 244 |
+
};
|
| 245 |
+
|
| 246 |
+
window.choosePackageAndScroll = function(packageId) {
|
| 247 |
+
selectPackage(packageId);
|
| 248 |
+
document.querySelector("#kalkulaator").scrollIntoView({behavior: "smooth"});
|
| 249 |
+
};
|
| 250 |
+
|
| 251 |
+
window.updateCalculator = function() {
|
| 252 |
+
const area = Number(document.getElementById("house-area").value);
|
| 253 |
+
configState.area = area;
|
| 254 |
+
configState.foundation = document.getElementById("foundation-type").value;
|
| 255 |
+
configState.wall = document.getElementById("wall-type").value;
|
| 256 |
+
configState.roof = document.getElementById("roof-type").value;
|
| 257 |
+
configState.heating = document.getElementById("heating-type").value;
|
| 258 |
+
configState.region = document.getElementById("project-region").value;
|
| 259 |
+
configState.use = document.getElementById("building-use").value;
|
| 260 |
+
document.getElementById("area-val").textContent = area;
|
| 261 |
+
|
| 262 |
+
let baseRate = 1200;
|
| 263 |
+
if (configState.floors === 1.5) baseRate += 100;
|
| 264 |
+
if (configState.floors === 2) baseRate += 180;
|
| 265 |
+
if (configState.foundation === "plate") baseRate += 60;
|
| 266 |
+
if (configState.foundation === "pile") baseRate += 90;
|
| 267 |
+
if (configState.wall === "clt") baseRate += 220;
|
| 268 |
+
if (configState.wall === "wood") baseRate += 50;
|
| 269 |
+
if (configState.heating === "geo") baseRate += 80;
|
| 270 |
+
if (configState.heating === "air-water") baseRate += 50;
|
| 271 |
+
if (configState.finish === "shell") baseRate -= 350;
|
| 272 |
+
if (configState.finish === "turnkey") baseRate += 250;
|
| 273 |
+
if (configState.region === "harju") baseRate *= 1.04;
|
| 274 |
+
|
| 275 |
+
let midpoint;
|
| 276 |
+
let lowFactor;
|
| 277 |
+
let highFactor;
|
| 278 |
+
if (appState.package === "concept") {
|
| 279 |
+
midpoint = 850 + area * 8.5 + (configState.floors > 1 ? 350 : 0);
|
| 280 |
+
lowFactor = 0.88;
|
| 281 |
+
highFactor = 1.18;
|
| 282 |
+
} else if (appState.package === "project") {
|
| 283 |
+
midpoint = 2600 + area * 31 + (configState.floors > 1 ? 800 : 0) + (configState.wall === "clt" ? 500 : 0);
|
| 284 |
+
lowFactor = 0.86;
|
| 285 |
+
highFactor = 1.22;
|
| 286 |
+
} else {
|
| 287 |
+
midpoint = area * baseRate + 6200;
|
| 288 |
+
lowFactor = 0.9;
|
| 289 |
+
highFactor = 1.13;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
const low = Math.round(midpoint * lowFactor / 50) * 50;
|
| 293 |
+
const high = Math.round(midpoint * highFactor / 50) * 50;
|
| 294 |
+
const meta = packageMeta[appState.package];
|
| 295 |
+
const selectedProject = projects.find(p => p.id === appState.selectedProjectId);
|
| 296 |
+
const readiness = Math.min(95, 45 + (selectedProject ? 15 : 0) + (document.getElementById("ai-description").value.trim() ? 15 : 0) + (configState.region ? 10 : 0));
|
| 297 |
+
|
| 298 |
+
appState.estimate = { midpoint, low, high, baseRate, readiness };
|
| 299 |
+
document.getElementById("total-price").textContent = `${formatCurrency(low)} – ${formatCurrency(high)}`;
|
| 300 |
+
document.getElementById("price-per-m2").textContent = appState.package === "turnkey"
|
| 301 |
+
? `hinnanguliselt ${Math.round(baseRate)} € / m²`
|
| 302 |
+
: `${meta.name.toLowerCase()} valitud mahu põhjal`;
|
| 303 |
+
|
| 304 |
+
meta.shares.forEach((share, index) => {
|
| 305 |
+
document.getElementById(`stage-label-${index + 1}`).textContent = `${index + 1}. ${meta.stages[index]}`;
|
| 306 |
+
document.getElementById(`stage-desc-${index + 1}`).textContent = meta.descriptions[index];
|
| 307 |
+
document.getElementById(["cost-foundation","cost-walls","cost-roof","cost-utility","cost-finish"][index]).textContent = formatCurrency(midpoint * share);
|
| 308 |
+
});
|
| 309 |
+
meta.deliverables.forEach((item, index) => {
|
| 310 |
+
document.getElementById(`deliverable-${index + 1}`).textContent = item;
|
| 311 |
+
});
|
| 312 |
+
|
| 313 |
+
const useLabels = {home:"eramu", summer:"puhkemaja", office:"ärihoone", outbuilding:"abihoone"};
|
| 314 |
+
document.getElementById("brief-title").textContent = `${selectedProject ? selectedProject.name + " põhjal" : "Uus"} ${area} m² ${useLabels[configState.use]} – ${meta.name.toLowerCase()}`;
|
| 315 |
+
document.getElementById("brief-package").textContent = meta.name;
|
| 316 |
+
document.getElementById("brief-range").textContent = `${formatCurrency(low)}–${formatCurrency(high)}`;
|
| 317 |
+
document.getElementById("brief-duration").textContent = meta.duration;
|
| 318 |
+
document.getElementById("brief-readiness").textContent = `${readiness}%`;
|
| 319 |
+
};
|
| 320 |
+
|
| 321 |
+
function getBriefText() {
|
| 322 |
+
const selected = projects.find(p => p.id === appState.selectedProjectId);
|
| 323 |
+
const meta = packageMeta[appState.package];
|
| 324 |
+
const regionLabels = {harju:"Harjumaa / Tallinn", tartu:"Tartumaa", parnu:"Pärnumaa", rapla:"Raplamaa", other:"Muu Eesti piirkond"};
|
| 325 |
+
const useLabels = {home:"Eramu", summer:"Suvila / puhkemaja", office:"Äri- või büroohoone", outbuilding:"Abihoone / stuudio"};
|
| 326 |
+
const estimate = appState.estimate || {};
|
| 327 |
+
return `MTM EHITUS – PROJEKTI LÄHTEÜLESANNE
|
| 328 |
+
Koostatud: ${new Date().toLocaleDateString("et-EE")}
|
| 329 |
+
|
| 330 |
+
VALITUD LAHENDUS
|
| 331 |
+
Pakett: ${meta.name}
|
| 332 |
+
Lähteprojekt: ${selected ? selected.name : "Personaalne uus lahendus"}
|
| 333 |
+
Hoone: ${useLabels[configState.use]}
|
| 334 |
+
Piirkond: ${regionLabels[configState.region]}
|
| 335 |
+
Pindala: ${configState.area} m²
|
| 336 |
+
Korruseid: ${configState.floors}
|
| 337 |
+
|
| 338 |
+
TEHNILISED EELISTUSED
|
| 339 |
+
Vundament: ${configState.foundation}
|
| 340 |
+
Konstruktsioon: ${configState.wall}
|
| 341 |
+
Katus: ${configState.roof}
|
| 342 |
+
Küte: ${configState.heating}
|
| 343 |
+
Viimistlus: ${configState.finish}
|
| 344 |
+
|
| 345 |
+
IDEE JA ERISOOVID
|
| 346 |
+
${document.getElementById("ai-description").value.trim() || "Täpsustatakse konsultatsioonil."}
|
| 347 |
+
|
| 348 |
+
ESIALGNE PLANEERING
|
| 349 |
+
Hinnavahemik: ${formatCurrency(estimate.low || 0)} – ${formatCurrency(estimate.high || 0)}
|
| 350 |
+
Ajakava: ${meta.duration}
|
| 351 |
+
Lähteandmete valmidus: ${estimate.readiness || 0}%
|
| 352 |
+
|
| 353 |
+
Märkus: hinnavahemik on planeerimise lähtehinnang, mitte siduv pakkumine.`;
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
window.downloadBrief = function() {
|
| 357 |
+
const blob = new Blob([getBriefText()], {type: "text/plain;charset=utf-8"});
|
| 358 |
+
const url = URL.createObjectURL(blob);
|
| 359 |
+
const link = document.createElement("a");
|
| 360 |
+
link.href = url;
|
| 361 |
+
link.download = `MTM-projekti-lahteulesanne-${new Date().toISOString().slice(0,10)}.txt`;
|
| 362 |
+
link.click();
|
| 363 |
+
URL.revokeObjectURL(url);
|
| 364 |
+
showToast("Lähteülesanne laaditi alla.");
|
| 365 |
+
};
|
| 366 |
+
|
| 367 |
+
window.copyBrief = async function() {
|
| 368 |
+
try {
|
| 369 |
+
await navigator.clipboard.writeText(getBriefText());
|
| 370 |
+
showToast("Projektikokkuvõte kopeeriti.");
|
| 371 |
+
} catch {
|
| 372 |
+
showToast("Kopeerimine ei õnnestunud. Laadi fail alla.");
|
| 373 |
+
}
|
| 374 |
+
};
|
| 375 |
+
|
| 376 |
+
window.shareByEmail = function() {
|
| 377 |
+
const subject = encodeURIComponent(`MTM projektipäring – ${packageMeta[appState.package].name}`);
|
| 378 |
+
const body = encodeURIComponent(getBriefText());
|
| 379 |
+
window.location.href = `mailto:${MTM_EMAIL}?subject=${subject}&body=${body}`;
|
| 380 |
+
};
|
| 381 |
+
|
| 382 |
+
window.shareByWhatsApp = function() {
|
| 383 |
+
const text = encodeURIComponent(getBriefText());
|
| 384 |
+
window.open(`https://wa.me/${MTM_PHONE}?text=${text}`, "_blank", "noopener");
|
| 385 |
+
};
|
| 386 |
+
|
| 387 |
+
window.saveDraft = function() {
|
| 388 |
+
const draft = {
|
| 389 |
+
config: {...configState},
|
| 390 |
+
app: {package: appState.package, selectedProjectId: appState.selectedProjectId},
|
| 391 |
+
description: document.getElementById("ai-description").value,
|
| 392 |
+
savedAt: new Date().toISOString()
|
| 393 |
+
};
|
| 394 |
+
if (saveJson(storageKeys.draft, draft)) showToast("Projekt salvestati selles brauseris.");
|
| 395 |
+
else showToast("Brauser ei lubanud mustandit salvestada.");
|
| 396 |
+
};
|
| 397 |
+
|
| 398 |
+
function restoreDraft() {
|
| 399 |
+
const draft = safeJsonRead(storageKeys.draft, null);
|
| 400 |
+
if (!draft?.config) return;
|
| 401 |
+
Object.assign(configState, draft.config);
|
| 402 |
+
appState.package = draft.app?.package || "project";
|
| 403 |
+
appState.selectedProjectId = draft.app?.selectedProjectId || null;
|
| 404 |
+
document.getElementById("house-area").value = configState.area || 120;
|
| 405 |
+
["foundation","wall","roof","heating"].forEach(key => {
|
| 406 |
+
const ids = {foundation:"foundation-type",wall:"wall-type",roof:"roof-type",heating:"heating-type"};
|
| 407 |
+
if (configState[key]) document.getElementById(ids[key]).value = configState[key];
|
| 408 |
+
});
|
| 409 |
+
if (configState.region) document.getElementById("project-region").value = configState.region;
|
| 410 |
+
if (configState.use) document.getElementById("building-use").value = configState.use;
|
| 411 |
+
document.getElementById("ai-description").value = draft.description || "";
|
| 412 |
+
selectPackage(appState.package);
|
| 413 |
+
if (appState.selectedProjectId) updateStickyProject();
|
| 414 |
+
showToast("Varasem projektimustand taastati.");
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
window.analyzeProjectIdea = function() {
|
| 418 |
+
const desc = document.getElementById("ai-description").value.trim();
|
| 419 |
+
const file = document.getElementById("ai-image-upload").files[0];
|
| 420 |
+
if (!desc && !file) {
|
| 421 |
+
showToast("Lisa kirjeldus või inspiratsioonipilt.");
|
| 422 |
+
return;
|
| 423 |
+
}
|
| 424 |
+
const lower = desc.toLowerCase();
|
| 425 |
+
const recommendations = [];
|
| 426 |
+
if (/modern|minimal|lamekatus/.test(lower)) recommendations.push("Kontseptsioonis tasub kontrollida suure klaaspinna, varjestuse ja lamekatuse tehnilist sobivust.");
|
| 427 |
+
if (/puit|clt|loodus/.test(lower)) recommendations.push("Puitlahenduse puhul lisa lähteülesandesse fassaadi hooldusklass, tulepüsivus ja niiskusrežiim.");
|
| 428 |
+
if (/saun|spaa|bassein/.test(lower)) recommendations.push("Märgruumid vajavad varases etapis ventilatsiooni, äravoolude ja niiskustehnilise lahenduse koordineerimist.");
|
| 429 |
+
if (/äri|büroo|klient|teenindus/.test(lower)) recommendations.push("Ärihoone puhul tuleb kohe määrata kasutajate arv, ligipääsetavus, evakuatsioon ja parkimisvajadus.");
|
| 430 |
+
if (/odav|eelarve|soodne/.test(lower)) recommendations.push("Eelarve hoidmiseks eelista kompaktset mahtu, korduvaid avatäiteid ja lihtsat katusegeomeetriat.");
|
| 431 |
+
if (!recommendations.length) recommendations.push("Järgmine mõistlik samm on ruumiprogrammi, krundi piirangute ja soovitud eelarve ühine kinnitamine.");
|
| 432 |
+
if (file) recommendations.push(`Inspiratsioonipilt „${file.name}” lisatakse konsultatsiooni lähteinfo hulka.`);
|
| 433 |
+
const selected = projects.find(p => p.id === appState.selectedProjectId);
|
| 434 |
+
const intro = selected ? `Valitud ${selected.name} annab hea lähtekoha.` : "Kirjelduse põhjal soovitame alustada ruumilise kontseptsiooniga.";
|
| 435 |
+
document.getElementById("ai-text-output").textContent = `${intro} ${recommendations.join(" ")}`;
|
| 436 |
+
document.getElementById("ai-analysis-result").style.display = "block";
|
| 437 |
+
updateCalculator();
|
| 438 |
+
};
|
| 439 |
+
|
| 440 |
+
function getFavorites() {
|
| 441 |
+
return safeJsonRead(storageKeys.favorites, []);
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
window.toggleFavorite = function(id, event) {
|
| 445 |
+
event?.stopPropagation();
|
| 446 |
+
const favorites = getFavorites();
|
| 447 |
+
const next = favorites.includes(id) ? favorites.filter(item => item !== id) : [...favorites, id];
|
| 448 |
+
saveJson(storageKeys.favorites, next);
|
| 449 |
+
renderProjects();
|
| 450 |
+
showToast(next.includes(id) ? "Projekt lisati lemmikutesse." : "Projekt eemaldati lemmikutest.");
|
| 451 |
+
};
|
| 452 |
+
|
| 453 |
+
window.toggleFavoritesOnly = function() {
|
| 454 |
+
appState.favoritesOnly = !appState.favoritesOnly;
|
| 455 |
+
renderProjects();
|
| 456 |
+
};
|
| 457 |
+
|
| 458 |
+
window.renderProjects = function() {
|
| 459 |
+
const query = document.getElementById("catalog-search").value.trim().toLowerCase();
|
| 460 |
+
const type = document.getElementById("catalog-type").value;
|
| 461 |
+
const area = document.getElementById("catalog-area").value;
|
| 462 |
+
const sort = document.getElementById("catalog-sort").value;
|
| 463 |
+
const favorites = getFavorites();
|
| 464 |
+
|
| 465 |
+
let filtered = projects.filter(project => {
|
| 466 |
+
const searchable = `${project.name} ${project.typeLabel} ${project.style} ${project.material} ${project.description}`.toLowerCase();
|
| 467 |
+
const areaMatch = area === "all" || (area === "small" && project.area <= 100) || (area === "medium" && project.area > 100 && project.area <= 160) || (area === "large" && project.area > 160);
|
| 468 |
+
return (!query || searchable.includes(query)) && (type === "all" || project.type === type) && areaMatch && (!appState.favoritesOnly || favorites.includes(project.id));
|
| 469 |
+
});
|
| 470 |
+
|
| 471 |
+
if (sort === "area-asc") filtered.sort((a,b) => a.area - b.area);
|
| 472 |
+
if (sort === "area-desc") filtered.sort((a,b) => b.area - a.area);
|
| 473 |
+
if (sort === "price-asc") filtered.sort((a,b) => a.designPrice - b.designPrice);
|
| 474 |
+
|
| 475 |
+
document.getElementById("catalog-count").textContent = `${filtered.length} lahendust`;
|
| 476 |
+
document.getElementById("favorite-count").textContent = favorites.length;
|
| 477 |
+
const grid = document.getElementById("project-grid");
|
| 478 |
+
if (!filtered.length) {
|
| 479 |
+
grid.innerHTML = `<div class="empty-state"><i class="fas fa-magnifying-glass" style="font-size:2.4rem;margin-bottom:12px;"></i><h3>Sobivaid lahendusi ei leitud</h3><p>Muuda filtreid või koosta planeerijas personaalne projekt.</p></div>`;
|
| 480 |
+
return;
|
| 481 |
+
}
|
| 482 |
+
grid.innerHTML = filtered.map(project => `
|
| 483 |
+
<article class="project-card">
|
| 484 |
+
<div class="project-img" style="background:${project.gradient}">
|
| 485 |
+
<button class="favorite-btn ${favorites.includes(project.id) ? "active" : ""}" onclick="toggleFavorite('${project.id}', event)" aria-label="Lisa lemmikuks"><i class="${favorites.includes(project.id) ? "fas" : "far"} fa-heart"></i></button>
|
| 486 |
+
<i class="fas ${project.icon}"></i>
|
| 487 |
+
</div>
|
| 488 |
+
<div class="project-info">
|
| 489 |
+
<span class="tag">${project.package}</span>
|
| 490 |
+
<h3 style="margin-top:12px;">${project.name}</h3>
|
| 491 |
+
<p class="location"><i class="fas fa-circle-check"></i> ${project.status}</p>
|
| 492 |
+
<div class="project-specs">
|
| 493 |
+
<span>${project.area} m²</span><span>${project.rooms} ruumi</span><span>${project.floors} korrus${project.floors > 1 ? "t" : ""}</span><span>${project.style}</span>
|
| 494 |
+
</div>
|
| 495 |
+
<p style="font-size:.88rem;color:var(--gray-text);">${project.description}</p>
|
| 496 |
+
<p class="package-price">3D-disain alates ${formatCurrency(project.designPrice)}</p>
|
| 497 |
+
<div class="project-actions">
|
| 498 |
+
<button class="btn btn-secondary btn-small" onclick="openProjectModal('${project.id}')">Vaata detaile</button>
|
| 499 |
+
<button class="btn btn-primary btn-small" onclick="useProject('${project.id}')">Kasuta lähtepunktina</button>
|
| 500 |
+
</div>
|
| 501 |
+
</div>
|
| 502 |
+
</article>`).join("");
|
| 503 |
+
};
|
| 504 |
+
|
| 505 |
+
window.openProjectModal = function(id) {
|
| 506 |
+
const project = projects.find(item => item.id === id);
|
| 507 |
+
if (!project) return;
|
| 508 |
+
document.getElementById("modal-tag").textContent = project.package;
|
| 509 |
+
document.getElementById("modal-title").textContent = project.name;
|
| 510 |
+
document.getElementById("modal-description").textContent = project.description;
|
| 511 |
+
document.getElementById("modal-customize").textContent = project.customize;
|
| 512 |
+
document.getElementById("modal-hero").style.background = project.gradient;
|
| 513 |
+
document.getElementById("modal-hero").innerHTML = `<i class="fas ${project.icon}"></i>`;
|
| 514 |
+
document.getElementById("modal-specs").innerHTML = `<span>${project.area} m²</span><span>${project.rooms} ruumi</span><span>${project.floors} korrus${project.floors > 1 ? "t" : ""}</span><span>${project.material}</span>`;
|
| 515 |
+
document.getElementById("modal-use-btn").onclick = () => useProject(id);
|
| 516 |
+
document.getElementById("modal-quote-btn").onclick = () => chooseDesign(project.name);
|
| 517 |
+
document.getElementById("project-modal").classList.add("open");
|
| 518 |
+
document.body.style.overflow = "hidden";
|
| 519 |
+
};
|
| 520 |
+
|
| 521 |
+
window.closeProjectModal = function() {
|
| 522 |
+
document.getElementById("project-modal").classList.remove("open");
|
| 523 |
+
document.body.style.overflow = "";
|
| 524 |
+
};
|
| 525 |
+
|
| 526 |
+
window.closeModalFromBackdrop = function(event) {
|
| 527 |
+
if (event.target.id === "project-modal") closeProjectModal();
|
| 528 |
+
};
|
| 529 |
+
|
| 530 |
+
window.useProject = function(id) {
|
| 531 |
+
const project = projects.find(item => item.id === id);
|
| 532 |
+
if (!project) return;
|
| 533 |
+
appState.selectedProjectId = id;
|
| 534 |
+
document.getElementById("house-area").value = project.area;
|
| 535 |
+
document.getElementById("building-use").value = project.type;
|
| 536 |
+
configState.floors = project.floors;
|
| 537 |
+
updateStickyProject();
|
| 538 |
+
closeProjectModal();
|
| 539 |
+
updateCalculator();
|
| 540 |
+
document.querySelector("#kalkulaator").scrollIntoView({behavior:"smooth"});
|
| 541 |
+
showToast(`${project.name} valiti projekti lähtepunktiks.`);
|
| 542 |
+
};
|
| 543 |
+
|
| 544 |
+
function updateStickyProject() {
|
| 545 |
+
const project = projects.find(item => item.id === appState.selectedProjectId);
|
| 546 |
+
const bar = document.getElementById("sticky-project-bar");
|
| 547 |
+
if (!project) {
|
| 548 |
+
bar.classList.remove("show");
|
| 549 |
+
return;
|
| 550 |
+
}
|
| 551 |
+
document.getElementById("sticky-project-name").textContent = project.name;
|
| 552 |
+
bar.classList.add("show");
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
window.chooseDesign = function(designName) {
|
| 556 |
+
const project = projects.find(item => item.name === designName);
|
| 557 |
+
if (project) appState.selectedProjectId = project.id;
|
| 558 |
+
document.getElementById("form-message").value = `${getBriefText()}\n\nSoovin selle lahenduse kohta personaalset pakkumist.`;
|
| 559 |
+
document.getElementById("form-package").value = packageValueToForm(appState.package);
|
| 560 |
+
closeProjectModal();
|
| 561 |
+
document.querySelector("#kontakt").scrollIntoView({behavior:"smooth"});
|
| 562 |
+
};
|
| 563 |
+
|
| 564 |
+
window.sendProjectToForm = function() {
|
| 565 |
+
document.getElementById("form-message").value = `${getBriefText()}\n\nPalun võtke minuga ühendust lahenduse täpsustamiseks.`;
|
| 566 |
+
document.getElementById("form-package").value = packageValueToForm(appState.package);
|
| 567 |
+
document.querySelector("#kontakt").scrollIntoView({behavior:"smooth"});
|
| 568 |
+
};
|
| 569 |
+
|
| 570 |
+
window.handleFormSubmit = function(event) {
|
| 571 |
+
event.preventDefault();
|
| 572 |
+
if (document.getElementById("form-company").value) return;
|
| 573 |
+
const form = event.currentTarget;
|
| 574 |
+
if (!form.checkValidity()) {
|
| 575 |
+
form.reportValidity();
|
| 576 |
+
return;
|
| 577 |
+
}
|
| 578 |
+
const reference = `MTM-${new Date().toISOString().slice(0,10).replaceAll("-","")}-${String(Date.now()).slice(-4)}`;
|
| 579 |
+
const lead = {
|
| 580 |
+
reference,
|
| 581 |
+
name: document.getElementById("form-name").value.trim(),
|
| 582 |
+
email: document.getElementById("form-email").value.trim(),
|
| 583 |
+
phone: document.getElementById("form-phone").value.trim(),
|
| 584 |
+
package: document.getElementById("form-package").value,
|
| 585 |
+
message: document.getElementById("form-message").value.trim(),
|
| 586 |
+
createdAt: new Date().toISOString()
|
| 587 |
+
};
|
| 588 |
+
const leads = safeJsonRead(storageKeys.leads, []);
|
| 589 |
+
saveJson(storageKeys.leads, [...leads.slice(-9), lead]);
|
| 590 |
+
const status = document.getElementById("form-status");
|
| 591 |
+
status.className = "form-status success";
|
| 592 |
+
status.textContent = `Päring ${reference} koostati ja salvestati selles brauseris. Avame nüüd sinu e-posti rakenduse, et saaksid selle MTM Ehitusele saata.`;
|
| 593 |
+
const subject = encodeURIComponent(`Projektipäring ${reference} – ${lead.package}`);
|
| 594 |
+
const body = encodeURIComponent(`Nimi: ${lead.name}\nE-post: ${lead.email}\nTelefon: ${lead.phone || "-"}\n\n${lead.message}`);
|
| 595 |
+
setTimeout(() => { window.location.href = `mailto:${MTM_EMAIL}?subject=${subject}&body=${body}`; }, 350);
|
| 596 |
+
};
|
| 597 |
+
|
| 598 |
+
document.addEventListener("keydown", event => {
|
| 599 |
+
if (event.key === "Escape") closeProjectModal();
|
| 600 |
+
});
|
| 601 |
+
|
| 602 |
+
document.getElementById("menuToggle").addEventListener("click", () => {
|
| 603 |
+
document.getElementById("navMenu").classList.toggle("open");
|
| 604 |
+
});
|
| 605 |
+
|
| 606 |
+
document.getElementById("ai-image-upload").addEventListener("change", () => updateCalculator());
|
| 607 |
+
document.getElementById("ai-description").addEventListener("input", () => updateCalculator());
|
| 608 |
+
|
| 609 |
+
renderProjects();
|
| 610 |
+
updateCalculator();
|
| 611 |
+
restoreDraft();
|
index.html
CHANGED
|
@@ -1,19 +1,2010 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="et">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>MTMehitus – 3D disainid, ehitusprojektid ja täislahendused</title>
|
| 7 |
+
|
| 8 |
+
<!-- Font Awesome 6 Icons -->
|
| 9 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 10 |
+
|
| 11 |
+
<!-- Google Fonts -->
|
| 12 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 14 |
+
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 15 |
+
|
| 16 |
+
<style>
|
| 17 |
+
/* RESET & PÕHISTIILID */
|
| 18 |
+
* {
|
| 19 |
+
margin: 0;
|
| 20 |
+
padding: 0;
|
| 21 |
+
box-sizing: border-box;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
:root {
|
| 25 |
+
--primary-dark: #0b1a2e;
|
| 26 |
+
--primary-blue: #1e3a5f;
|
| 27 |
+
--accent-gold: #f5b042;
|
| 28 |
+
--accent-gold-hover: #e09e2f;
|
| 29 |
+
--accent-gold-light: #fef4e2;
|
| 30 |
+
--light-bg: #f4f7fa;
|
| 31 |
+
--white: #ffffff;
|
| 32 |
+
--gray-text: #5a6b7c;
|
| 33 |
+
--gray-light: #e2e8f0;
|
| 34 |
+
--card-shadow: 0 10px 30px rgba(11, 26, 46, 0.08);
|
| 35 |
+
--radius-lg: 16px;
|
| 36 |
+
--radius-sm: 8px;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
body {
|
| 40 |
+
font-family: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
|
| 41 |
+
background-color: var(--light-bg);
|
| 42 |
+
color: #1e2a3a;
|
| 43 |
+
line-height: 1.6;
|
| 44 |
+
scroll-behavior: smooth;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
.container {
|
| 48 |
+
max-width: 1240px;
|
| 49 |
+
margin: 0 auto;
|
| 50 |
+
padding: 0 20px;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
/* TÜPOGRAAFIA JA NUPUD */
|
| 54 |
+
h1, h2, h3, h4 {
|
| 55 |
+
font-weight: 700;
|
| 56 |
+
letter-spacing: -0.02em;
|
| 57 |
+
color: var(--primary-dark);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
h2 {
|
| 61 |
+
font-size: 2.2rem;
|
| 62 |
+
margin-bottom: 0.8rem;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.section-title {
|
| 66 |
+
text-align: center;
|
| 67 |
+
margin-bottom: 3rem;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
.section-title span {
|
| 71 |
+
display: block;
|
| 72 |
+
background: var(--accent-gold);
|
| 73 |
+
width: 70px;
|
| 74 |
+
height: 4px;
|
| 75 |
+
border-radius: 4px;
|
| 76 |
+
margin: 10px auto 0;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.btn {
|
| 80 |
+
display: inline-flex;
|
| 81 |
+
align-items: center;
|
| 82 |
+
justify-content: center;
|
| 83 |
+
gap: 10px;
|
| 84 |
+
padding: 14px 32px;
|
| 85 |
+
border-radius: 50px;
|
| 86 |
+
font-weight: 700;
|
| 87 |
+
text-decoration: none;
|
| 88 |
+
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
| 89 |
+
border: none;
|
| 90 |
+
cursor: pointer;
|
| 91 |
+
font-size: 1rem;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.btn-primary {
|
| 95 |
+
background: var(--accent-gold);
|
| 96 |
+
color: var(--primary-dark);
|
| 97 |
+
box-shadow: 0 4px 18px rgba(245, 176, 66, 0.35);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.btn-primary:hover {
|
| 101 |
+
background: var(--accent-gold-hover);
|
| 102 |
+
transform: translateY(-2px);
|
| 103 |
+
box-shadow: 0 8px 25px rgba(245, 176, 66, 0.45);
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
.btn-secondary {
|
| 107 |
+
background: var(--primary-blue);
|
| 108 |
+
color: var(--white);
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.btn-secondary:hover {
|
| 112 |
+
background: var(--primary-dark);
|
| 113 |
+
transform: translateY(-2px);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
.btn-outline {
|
| 117 |
+
background: transparent;
|
| 118 |
+
color: var(--white);
|
| 119 |
+
border: 2px solid var(--white);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.btn-outline:hover {
|
| 123 |
+
background: var(--white);
|
| 124 |
+
color: var(--primary-dark);
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
/* HEADER & NAVIGATSIOON */
|
| 128 |
+
header {
|
| 129 |
+
background: var(--primary-dark);
|
| 130 |
+
padding: 16px 0;
|
| 131 |
+
position: sticky;
|
| 132 |
+
top: 0;
|
| 133 |
+
z-index: 1000;
|
| 134 |
+
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
.header-flex {
|
| 138 |
+
display: flex;
|
| 139 |
+
align-items: center;
|
| 140 |
+
justify-content: space-between;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
.logo a {
|
| 144 |
+
font-size: 1.6rem;
|
| 145 |
+
font-weight: 800;
|
| 146 |
+
color: var(--white);
|
| 147 |
+
text-decoration: none;
|
| 148 |
+
display: flex;
|
| 149 |
+
align-items: center;
|
| 150 |
+
gap: 8px;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
.logo a span {
|
| 154 |
+
color: var(--accent-gold);
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
nav ul {
|
| 158 |
+
display: flex;
|
| 159 |
+
list-style: none;
|
| 160 |
+
gap: 28px;
|
| 161 |
+
align-items: center;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
nav ul li a {
|
| 165 |
+
color: rgba(255,255,255,0.85);
|
| 166 |
+
text-decoration: none;
|
| 167 |
+
font-weight: 600;
|
| 168 |
+
transition: 0.2s;
|
| 169 |
+
font-size: 0.95rem;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
nav ul li a:hover {
|
| 173 |
+
color: var(--accent-gold);
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
.nav-cta {
|
| 177 |
+
background: var(--accent-gold);
|
| 178 |
+
color: var(--primary-dark) !important;
|
| 179 |
+
padding: 10px 24px;
|
| 180 |
+
border-radius: 40px;
|
| 181 |
+
font-weight: 700;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.nav-cta:hover {
|
| 185 |
+
background: var(--accent-gold-hover) !important;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
.menu-toggle {
|
| 189 |
+
display: none;
|
| 190 |
+
color: var(--white);
|
| 191 |
+
font-size: 1.8rem;
|
| 192 |
+
cursor: pointer;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
/* HERO SEKTSIOON */
|
| 196 |
+
.hero {
|
| 197 |
+
background: linear-gradient(135deg, var(--primary-dark) 0%, #10263f 100%);
|
| 198 |
+
color: var(--white);
|
| 199 |
+
padding: 90px 0 100px;
|
| 200 |
+
position: relative;
|
| 201 |
+
overflow: hidden;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
.hero-grid {
|
| 205 |
+
display: grid;
|
| 206 |
+
grid-template-columns: 1.1fr 0.9fr;
|
| 207 |
+
gap: 50px;
|
| 208 |
+
align-items: center;
|
| 209 |
+
position: relative;
|
| 210 |
+
z-index: 2;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.hero h1 {
|
| 214 |
+
font-size: 3.5rem;
|
| 215 |
+
color: var(--white);
|
| 216 |
+
line-height: 1.15;
|
| 217 |
+
margin-bottom: 1.2rem;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
.hero h1 span {
|
| 221 |
+
color: var(--accent-gold);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
.hero p {
|
| 225 |
+
font-size: 1.2rem;
|
| 226 |
+
opacity: 0.9;
|
| 227 |
+
margin-bottom: 2rem;
|
| 228 |
+
max-width: 540px;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
.hero-badges {
|
| 232 |
+
display: flex;
|
| 233 |
+
flex-wrap: wrap;
|
| 234 |
+
gap: 24px;
|
| 235 |
+
margin-top: 2.5rem;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
.badge-item {
|
| 239 |
+
display: flex;
|
| 240 |
+
align-items: center;
|
| 241 |
+
gap: 10px;
|
| 242 |
+
font-size: 0.95rem;
|
| 243 |
+
font-weight: 600;
|
| 244 |
+
background: rgba(255,255,255,0.06);
|
| 245 |
+
padding: 8px 16px;
|
| 246 |
+
border-radius: 30px;
|
| 247 |
+
border: 1px solid rgba(255,255,255,0.1);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
.badge-item i {
|
| 251 |
+
color: var(--accent-gold);
|
| 252 |
+
font-size: 1.2rem;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
.hero-card-preview {
|
| 256 |
+
background: rgba(255,255,255,0.05);
|
| 257 |
+
border-radius: var(--radius-lg);
|
| 258 |
+
padding: 30px;
|
| 259 |
+
border: 1px solid rgba(255,255,255,0.15);
|
| 260 |
+
backdrop-filter: blur(10px);
|
| 261 |
+
text-align: center;
|
| 262 |
+
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.hero-card-preview i.main-icon {
|
| 266 |
+
font-size: 3.5rem;
|
| 267 |
+
color: var(--accent-gold);
|
| 268 |
+
margin-bottom: 15px;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
/* USALDUSRIBA */
|
| 272 |
+
.trust-bar {
|
| 273 |
+
background: var(--white);
|
| 274 |
+
padding: 30px 0;
|
| 275 |
+
box-shadow: 0 4px 20px rgba(0,0,0,0.03);
|
| 276 |
+
border-bottom: 1px solid var(--gray-light);
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
.trust-grid {
|
| 280 |
+
display: grid;
|
| 281 |
+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
| 282 |
+
gap: 20px;
|
| 283 |
+
text-align: center;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
.trust-item .number {
|
| 287 |
+
font-size: 1.8rem;
|
| 288 |
+
font-weight: 800;
|
| 289 |
+
color: var(--primary-blue);
|
| 290 |
+
display: block;
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
.trust-item .label {
|
| 294 |
+
font-size: 0.85rem;
|
| 295 |
+
color: var(--gray-text);
|
| 296 |
+
font-weight: 600;
|
| 297 |
+
text-transform: uppercase;
|
| 298 |
+
letter-spacing: 0.5px;
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
/* INTERAKTIIVNE KODU KONFIGURAATOR & AI SEKTSIOON */
|
| 302 |
+
.builder-section {
|
| 303 |
+
padding: 80px 0;
|
| 304 |
+
background: #eef3f8;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
.builder-card {
|
| 308 |
+
background: var(--white);
|
| 309 |
+
border-radius: 24px;
|
| 310 |
+
padding: 40px;
|
| 311 |
+
box-shadow: var(--card-shadow);
|
| 312 |
+
border: 1px solid var(--gray-light);
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
.tab-buttons {
|
| 316 |
+
display: flex;
|
| 317 |
+
gap: 12px;
|
| 318 |
+
margin-bottom: 30px;
|
| 319 |
+
border-bottom: 2px solid var(--gray-light);
|
| 320 |
+
padding-bottom: 12px;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.tab-btn {
|
| 324 |
+
background: transparent;
|
| 325 |
+
border: none;
|
| 326 |
+
padding: 12px 24px;
|
| 327 |
+
font-weight: 700;
|
| 328 |
+
font-size: 1rem;
|
| 329 |
+
color: var(--gray-text);
|
| 330 |
+
cursor: pointer;
|
| 331 |
+
border-radius: 30px;
|
| 332 |
+
transition: all 0.2s;
|
| 333 |
+
display: flex;
|
| 334 |
+
align-items: center;
|
| 335 |
+
gap: 8px;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.tab-btn.active {
|
| 339 |
+
background: var(--primary-blue);
|
| 340 |
+
color: var(--white);
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
.tab-content {
|
| 344 |
+
display: none;
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
.tab-content.active {
|
| 348 |
+
display: block;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
/* MUDELI FORMID & VALIKUD */
|
| 352 |
+
.form-grid-2 {
|
| 353 |
+
display: grid;
|
| 354 |
+
grid-template-columns: 1fr 1fr;
|
| 355 |
+
gap: 30px;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
.form-group {
|
| 359 |
+
margin-bottom: 20px;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
.form-group label {
|
| 363 |
+
display: block;
|
| 364 |
+
font-weight: 700;
|
| 365 |
+
margin-bottom: 8px;
|
| 366 |
+
color: var(--primary-dark);
|
| 367 |
+
font-size: 0.95rem;
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
.form-control, select, textarea {
|
| 371 |
+
width: 100%;
|
| 372 |
+
padding: 12px 16px;
|
| 373 |
+
border-radius: 12px;
|
| 374 |
+
border: 1px solid #cbd5e1;
|
| 375 |
+
font-family: inherit;
|
| 376 |
+
font-size: 0.95rem;
|
| 377 |
+
background: var(--white);
|
| 378 |
+
transition: all 0.2s;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
.form-control:focus, select:focus, textarea:focus {
|
| 382 |
+
outline: none;
|
| 383 |
+
border-color: var(--primary-blue);
|
| 384 |
+
box-shadow: 0 0 0 3px rgba(30, 58, 95, 0.15);
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
.option-cards {
|
| 388 |
+
display: grid;
|
| 389 |
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
| 390 |
+
gap: 12px;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
.option-card {
|
| 394 |
+
border: 2px solid var(--gray-light);
|
| 395 |
+
border-radius: 12px;
|
| 396 |
+
padding: 14px 10px;
|
| 397 |
+
text-align: center;
|
| 398 |
+
cursor: pointer;
|
| 399 |
+
transition: all 0.2s;
|
| 400 |
+
background: var(--white);
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
.option-card:hover {
|
| 404 |
+
border-color: var(--primary-blue);
|
| 405 |
+
background: #f8fafc;
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
.option-card.selected {
|
| 409 |
+
border-color: var(--primary-blue);
|
| 410 |
+
background: #eef6ff;
|
| 411 |
+
color: var(--primary-blue);
|
| 412 |
+
font-weight: 700;
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
.option-card i {
|
| 416 |
+
font-size: 1.5rem;
|
| 417 |
+
display: block;
|
| 418 |
+
margin-bottom: 6px;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
/* TULEMUSTE PANEEL & KULUDE JAOTUS */
|
| 422 |
+
.results-panel {
|
| 423 |
+
background: #0b1a2e;
|
| 424 |
+
color: var(--white);
|
| 425 |
+
border-radius: 20px;
|
| 426 |
+
padding: 30px;
|
| 427 |
+
margin-top: 30px;
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
.results-header {
|
| 431 |
+
display: flex;
|
| 432 |
+
justify-content: space-between;
|
| 433 |
+
align-items: center;
|
| 434 |
+
border-bottom: 1px solid rgba(255,255,255,0.15);
|
| 435 |
+
padding-bottom: 20px;
|
| 436 |
+
margin-bottom: 20px;
|
| 437 |
+
flex-wrap: wrap;
|
| 438 |
+
gap: 15px;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
.total-price-box {
|
| 442 |
+
text-align: right;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
.total-price-box .price {
|
| 446 |
+
font-size: 2.5rem;
|
| 447 |
+
font-weight: 800;
|
| 448 |
+
color: var(--accent-gold);
|
| 449 |
+
line-height: 1;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
.stage-breakdown {
|
| 453 |
+
display: grid;
|
| 454 |
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
| 455 |
+
gap: 16px;
|
| 456 |
+
margin-bottom: 25px;
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
.stage-card {
|
| 460 |
+
background: rgba(255,255,255,0.06);
|
| 461 |
+
border-radius: 12px;
|
| 462 |
+
padding: 16px;
|
| 463 |
+
border: 1px solid rgba(255,255,255,0.08);
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
.stage-card h4 {
|
| 467 |
+
color: var(--white);
|
| 468 |
+
font-size: 0.95rem;
|
| 469 |
+
margin-bottom: 6px;
|
| 470 |
+
display: flex;
|
| 471 |
+
justify-content: space-between;
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
.stage-card .stage-price {
|
| 475 |
+
color: var(--accent-gold);
|
| 476 |
+
font-weight: 700;
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
.materials-list {
|
| 480 |
+
background: rgba(255,255,255,0.04);
|
| 481 |
+
border-radius: 12px;
|
| 482 |
+
padding: 20px;
|
| 483 |
+
margin-top: 20px;
|
| 484 |
+
font-size: 0.9rem;
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
.materials-list ul {
|
| 488 |
+
display: grid;
|
| 489 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 490 |
+
gap: 10px;
|
| 491 |
+
list-style: none;
|
| 492 |
+
margin-top: 10px;
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
.materials-list li {
|
| 496 |
+
display: flex;
|
| 497 |
+
align-items: center;
|
| 498 |
+
gap: 8px;
|
| 499 |
+
color: #cbd5e1;
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
.materials-list li i {
|
| 503 |
+
color: var(--accent-gold);
|
| 504 |
+
font-size: 0.8rem;
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
/* AI VISUAAL */
|
| 508 |
+
.ai-image-container {
|
| 509 |
+
width: 100%;
|
| 510 |
+
height: 320px;
|
| 511 |
+
background: rgba(255,255,255,0.05);
|
| 512 |
+
border: 2px dashed rgba(255,255,255,0.2);
|
| 513 |
+
border-radius: 16px;
|
| 514 |
+
display: flex;
|
| 515 |
+
flex-direction: column;
|
| 516 |
+
align-items: center;
|
| 517 |
+
justify-content: center;
|
| 518 |
+
overflow: hidden;
|
| 519 |
+
position: relative;
|
| 520 |
+
margin-top: 20px;
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
.ai-image-container img {
|
| 524 |
+
width: 100%;
|
| 525 |
+
height: 100%;
|
| 526 |
+
object-fit: cover;
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
.loading-spinner {
|
| 530 |
+
display: none;
|
| 531 |
+
text-align: center;
|
| 532 |
+
color: var(--accent-gold);
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
.loading-spinner i {
|
| 536 |
+
font-size: 2.5rem;
|
| 537 |
+
animation: spin 1s linear infinite;
|
| 538 |
+
margin-bottom: 10px;
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
@keyframes spin {
|
| 542 |
+
100% { transform: rotate(360deg); }
|
| 543 |
+
}
|
| 544 |
+
|
| 545 |
+
/* TEENUSED & PROJEKTID */
|
| 546 |
+
.services {
|
| 547 |
+
padding: 80px 0;
|
| 548 |
+
background: var(--white);
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
.services-grid {
|
| 552 |
+
display: grid;
|
| 553 |
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
| 554 |
+
gap: 30px;
|
| 555 |
+
}
|
| 556 |
+
|
| 557 |
+
.service-card {
|
| 558 |
+
background: var(--light-bg);
|
| 559 |
+
padding: 35px 28px;
|
| 560 |
+
border-radius: 20px;
|
| 561 |
+
transition: all 0.3s;
|
| 562 |
+
text-align: center;
|
| 563 |
+
border-bottom: 4px solid transparent;
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
.service-card:hover {
|
| 567 |
+
transform: translateY(-8px);
|
| 568 |
+
border-bottom-color: var(--accent-gold);
|
| 569 |
+
box-shadow: var(--card-shadow);
|
| 570 |
+
background: var(--white);
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
+
.service-card i {
|
| 574 |
+
font-size: 2.8rem;
|
| 575 |
+
color: var(--primary-blue);
|
| 576 |
+
margin-bottom: 20px;
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
.service-card h3 {
|
| 580 |
+
font-size: 1.3rem;
|
| 581 |
+
margin-bottom: 12px;
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
+
.service-card p {
|
| 585 |
+
color: var(--gray-text);
|
| 586 |
+
font-size: 0.95rem;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
/* PROJEKTID */
|
| 590 |
+
.projects {
|
| 591 |
+
padding: 80px 0;
|
| 592 |
+
background: #f1f5f9;
|
| 593 |
+
}
|
| 594 |
+
|
| 595 |
+
.project-grid {
|
| 596 |
+
display: grid;
|
| 597 |
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
| 598 |
+
gap: 30px;
|
| 599 |
+
}
|
| 600 |
+
|
| 601 |
+
.project-card {
|
| 602 |
+
background: var(--white);
|
| 603 |
+
border-radius: 20px;
|
| 604 |
+
overflow: hidden;
|
| 605 |
+
box-shadow: var(--card-shadow);
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
.project-img {
|
| 609 |
+
height: 220px;
|
| 610 |
+
background: #cbd5e1;
|
| 611 |
+
display: flex;
|
| 612 |
+
align-items: center;
|
| 613 |
+
justify-content: center;
|
| 614 |
+
color: var(--primary-blue);
|
| 615 |
+
font-size: 3rem;
|
| 616 |
+
background-image: linear-gradient(135deg, #cbd5e1 0%, #94a3b8 100%);
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
.project-info {
|
| 620 |
+
padding: 24px;
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
.project-info h3 {
|
| 624 |
+
font-size: 1.25rem;
|
| 625 |
+
margin-bottom: 6px;
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
.project-info .location {
|
| 629 |
+
font-size: 0.9rem;
|
| 630 |
+
color: var(--gray-text);
|
| 631 |
+
margin-bottom: 12px;
|
| 632 |
+
}
|
| 633 |
+
|
| 634 |
+
.tag {
|
| 635 |
+
background: var(--accent-gold-light);
|
| 636 |
+
color: var(--primary-dark);
|
| 637 |
+
padding: 4px 14px;
|
| 638 |
+
border-radius: 30px;
|
| 639 |
+
font-size: 0.8rem;
|
| 640 |
+
font-weight: 700;
|
| 641 |
+
display: inline-block;
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
/* MEIST */
|
| 645 |
+
.about {
|
| 646 |
+
padding: 80px 0;
|
| 647 |
+
background: var(--white);
|
| 648 |
+
}
|
| 649 |
+
|
| 650 |
+
.about-grid {
|
| 651 |
+
display: grid;
|
| 652 |
+
grid-template-columns: 1fr 1fr;
|
| 653 |
+
gap: 60px;
|
| 654 |
+
align-items: center;
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
.about-list {
|
| 658 |
+
list-style: none;
|
| 659 |
+
margin-top: 24px;
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
.about-list li {
|
| 663 |
+
margin-bottom: 14px;
|
| 664 |
+
display: flex;
|
| 665 |
+
align-items: center;
|
| 666 |
+
gap: 12px;
|
| 667 |
+
font-size: 1rem;
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
+
.about-list li i {
|
| 671 |
+
color: var(--accent-gold);
|
| 672 |
+
font-size: 1.2rem;
|
| 673 |
+
}
|
| 674 |
+
|
| 675 |
+
.about-box {
|
| 676 |
+
background: var(--light-bg);
|
| 677 |
+
border-radius: 24px;
|
| 678 |
+
padding: 40px;
|
| 679 |
+
border: 2px dashed #cbd5e1;
|
| 680 |
+
text-align: center;
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
.about-box i {
|
| 684 |
+
font-size: 4rem;
|
| 685 |
+
color: var(--primary-blue);
|
| 686 |
+
opacity: 0.4;
|
| 687 |
+
margin-bottom: 20px;
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
/* KONTAKT */
|
| 691 |
+
.contact {
|
| 692 |
+
padding: 80px 0;
|
| 693 |
+
background: var(--primary-dark);
|
| 694 |
+
color: var(--white);
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
.contact-grid {
|
| 698 |
+
display: grid;
|
| 699 |
+
grid-template-columns: 1fr 1fr;
|
| 700 |
+
gap: 60px;
|
| 701 |
+
}
|
| 702 |
+
|
| 703 |
+
.contact-info h2 {
|
| 704 |
+
color: var(--white);
|
| 705 |
+
}
|
| 706 |
+
|
| 707 |
+
.contact-detail {
|
| 708 |
+
display: flex;
|
| 709 |
+
align-items: center;
|
| 710 |
+
gap: 20px;
|
| 711 |
+
margin: 24px 0;
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
.contact-detail i {
|
| 715 |
+
font-size: 1.8rem;
|
| 716 |
+
color: var(--accent-gold);
|
| 717 |
+
width: 45px;
|
| 718 |
+
height: 45px;
|
| 719 |
+
background: rgba(255,255,255,0.06);
|
| 720 |
+
border-radius: 50%;
|
| 721 |
+
display: flex;
|
| 722 |
+
align-items: center;
|
| 723 |
+
justify-content: center;
|
| 724 |
+
}
|
| 725 |
+
|
| 726 |
+
.contact-form input,
|
| 727 |
+
.contact-form textarea,
|
| 728 |
+
.contact-form select {
|
| 729 |
+
width: 100%;
|
| 730 |
+
padding: 16px 20px;
|
| 731 |
+
margin-bottom: 18px;
|
| 732 |
+
border-radius: 12px;
|
| 733 |
+
background: rgba(255,255,255,0.06);
|
| 734 |
+
color: var(--white);
|
| 735 |
+
font-size: 1rem;
|
| 736 |
+
border: 1px solid rgba(255,255,255,0.1);
|
| 737 |
+
}
|
| 738 |
+
|
| 739 |
+
.contact-form input::placeholder,
|
| 740 |
+
.contact-form textarea::placeholder {
|
| 741 |
+
color: rgba(255,255,255,0.5);
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
.contact-form input:focus,
|
| 745 |
+
.contact-form textarea:focus {
|
| 746 |
+
outline: none;
|
| 747 |
+
background: rgba(255,255,255,0.12);
|
| 748 |
+
border-color: var(--accent-gold);
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
/* JALUS */
|
| 752 |
+
footer {
|
| 753 |
+
background: #060f1c;
|
| 754 |
+
color: rgba(255,255,255,0.6);
|
| 755 |
+
padding: 35px 0;
|
| 756 |
+
text-align: center;
|
| 757 |
+
font-size: 0.9rem;
|
| 758 |
+
border-top: 1px solid rgba(255,255,255,0.05);
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
footer a {
|
| 762 |
+
color: rgba(255,255,255,0.7);
|
| 763 |
+
text-decoration: none;
|
| 764 |
+
margin: 0 15px;
|
| 765 |
+
}
|
| 766 |
+
|
| 767 |
+
footer a:hover {
|
| 768 |
+
color: var(--accent-gold);
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
/* MÜÜGIRAKENDUSE KOMPONENDID */
|
| 772 |
+
.eyebrow {
|
| 773 |
+
display: inline-flex;
|
| 774 |
+
align-items: center;
|
| 775 |
+
gap: 8px;
|
| 776 |
+
color: var(--accent-gold);
|
| 777 |
+
font-weight: 800;
|
| 778 |
+
text-transform: uppercase;
|
| 779 |
+
letter-spacing: 0.08em;
|
| 780 |
+
font-size: 0.78rem;
|
| 781 |
+
margin-bottom: 14px;
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
.journey {
|
| 785 |
+
background: var(--white);
|
| 786 |
+
padding: 34px 0;
|
| 787 |
+
border-bottom: 1px solid var(--gray-light);
|
| 788 |
+
}
|
| 789 |
+
|
| 790 |
+
.journey-grid {
|
| 791 |
+
display: grid;
|
| 792 |
+
grid-template-columns: repeat(5, 1fr);
|
| 793 |
+
gap: 12px;
|
| 794 |
+
}
|
| 795 |
+
|
| 796 |
+
.journey-step {
|
| 797 |
+
display: flex;
|
| 798 |
+
gap: 12px;
|
| 799 |
+
align-items: center;
|
| 800 |
+
padding: 14px;
|
| 801 |
+
border-radius: 14px;
|
| 802 |
+
background: var(--light-bg);
|
| 803 |
+
font-size: 0.86rem;
|
| 804 |
+
font-weight: 700;
|
| 805 |
+
}
|
| 806 |
+
|
| 807 |
+
.journey-step span {
|
| 808 |
+
width: 30px;
|
| 809 |
+
height: 30px;
|
| 810 |
+
border-radius: 50%;
|
| 811 |
+
flex: 0 0 30px;
|
| 812 |
+
display: grid;
|
| 813 |
+
place-items: center;
|
| 814 |
+
background: var(--primary-blue);
|
| 815 |
+
color: var(--white);
|
| 816 |
+
}
|
| 817 |
+
|
| 818 |
+
.package-selector {
|
| 819 |
+
display: grid;
|
| 820 |
+
grid-template-columns: repeat(3, 1fr);
|
| 821 |
+
gap: 16px;
|
| 822 |
+
margin: 25px 0;
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
.package-option {
|
| 826 |
+
position: relative;
|
| 827 |
+
border: 2px solid var(--gray-light);
|
| 828 |
+
background: var(--white);
|
| 829 |
+
border-radius: 16px;
|
| 830 |
+
padding: 22px;
|
| 831 |
+
cursor: pointer;
|
| 832 |
+
transition: 0.2s ease;
|
| 833 |
+
}
|
| 834 |
+
|
| 835 |
+
.package-option:hover,
|
| 836 |
+
.package-option.selected {
|
| 837 |
+
border-color: var(--accent-gold);
|
| 838 |
+
box-shadow: 0 10px 24px rgba(11, 26, 46, 0.1);
|
| 839 |
+
transform: translateY(-2px);
|
| 840 |
+
}
|
| 841 |
+
|
| 842 |
+
.package-option.selected::after {
|
| 843 |
+
content: "Valitud";
|
| 844 |
+
position: absolute;
|
| 845 |
+
top: 12px;
|
| 846 |
+
right: 12px;
|
| 847 |
+
background: var(--accent-gold);
|
| 848 |
+
color: var(--primary-dark);
|
| 849 |
+
border-radius: 20px;
|
| 850 |
+
padding: 4px 10px;
|
| 851 |
+
font-size: 0.72rem;
|
| 852 |
+
font-weight: 800;
|
| 853 |
+
}
|
| 854 |
+
|
| 855 |
+
.package-option h3 { margin-bottom: 6px; }
|
| 856 |
+
.package-option p { color: var(--gray-text); font-size: 0.88rem; }
|
| 857 |
+
.package-price { color: var(--primary-blue); font-size: 1.15rem; font-weight: 800; margin-top: 12px; }
|
| 858 |
+
|
| 859 |
+
.brief-panel {
|
| 860 |
+
margin-top: 22px;
|
| 861 |
+
background: linear-gradient(135deg, #eef6ff, #fff8eb);
|
| 862 |
+
border: 1px solid #cbd5e1;
|
| 863 |
+
border-radius: 18px;
|
| 864 |
+
padding: 24px;
|
| 865 |
+
}
|
| 866 |
+
|
| 867 |
+
.brief-grid {
|
| 868 |
+
display: grid;
|
| 869 |
+
grid-template-columns: repeat(4, 1fr);
|
| 870 |
+
gap: 12px;
|
| 871 |
+
margin-top: 15px;
|
| 872 |
+
}
|
| 873 |
+
|
| 874 |
+
.brief-stat {
|
| 875 |
+
background: rgba(255,255,255,0.85);
|
| 876 |
+
border-radius: 12px;
|
| 877 |
+
padding: 14px;
|
| 878 |
+
border: 1px solid rgba(30,58,95,0.08);
|
| 879 |
+
}
|
| 880 |
+
|
| 881 |
+
.brief-stat span { display: block; color: var(--gray-text); font-size: 0.75rem; }
|
| 882 |
+
.brief-stat strong { color: var(--primary-dark); font-size: 1rem; }
|
| 883 |
+
|
| 884 |
+
.action-row {
|
| 885 |
+
display: flex;
|
| 886 |
+
flex-wrap: wrap;
|
| 887 |
+
gap: 10px;
|
| 888 |
+
margin-top: 20px;
|
| 889 |
+
}
|
| 890 |
+
|
| 891 |
+
.btn-small {
|
| 892 |
+
padding: 10px 17px;
|
| 893 |
+
font-size: 0.86rem;
|
| 894 |
+
}
|
| 895 |
+
|
| 896 |
+
.catalog-toolbar {
|
| 897 |
+
display: grid;
|
| 898 |
+
grid-template-columns: 1.5fr repeat(3, 1fr);
|
| 899 |
+
gap: 12px;
|
| 900 |
+
background: var(--white);
|
| 901 |
+
border: 1px solid var(--gray-light);
|
| 902 |
+
padding: 16px;
|
| 903 |
+
border-radius: 16px;
|
| 904 |
+
margin-bottom: 24px;
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
.catalog-toolbar input,
|
| 908 |
+
.catalog-toolbar select {
|
| 909 |
+
margin: 0;
|
| 910 |
+
min-height: 48px;
|
| 911 |
+
}
|
| 912 |
+
|
| 913 |
+
.catalog-meta {
|
| 914 |
+
display: flex;
|
| 915 |
+
justify-content: space-between;
|
| 916 |
+
align-items: center;
|
| 917 |
+
gap: 15px;
|
| 918 |
+
margin-bottom: 18px;
|
| 919 |
+
color: var(--gray-text);
|
| 920 |
+
font-size: 0.9rem;
|
| 921 |
+
}
|
| 922 |
+
|
| 923 |
+
.favorite-counter {
|
| 924 |
+
border: 0;
|
| 925 |
+
background: var(--accent-gold-light);
|
| 926 |
+
color: var(--primary-dark);
|
| 927 |
+
border-radius: 30px;
|
| 928 |
+
padding: 8px 13px;
|
| 929 |
+
font-weight: 800;
|
| 930 |
+
cursor: pointer;
|
| 931 |
+
}
|
| 932 |
+
|
| 933 |
+
.project-card {
|
| 934 |
+
position: relative;
|
| 935 |
+
display: flex;
|
| 936 |
+
flex-direction: column;
|
| 937 |
+
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
| 938 |
+
}
|
| 939 |
+
|
| 940 |
+
.project-card:hover {
|
| 941 |
+
transform: translateY(-5px);
|
| 942 |
+
box-shadow: 0 18px 36px rgba(11,26,46,0.14);
|
| 943 |
+
}
|
| 944 |
+
|
| 945 |
+
.project-img {
|
| 946 |
+
position: relative;
|
| 947 |
+
overflow: hidden;
|
| 948 |
+
}
|
| 949 |
+
|
| 950 |
+
.project-img::after {
|
| 951 |
+
content: "";
|
| 952 |
+
position: absolute;
|
| 953 |
+
inset: 0;
|
| 954 |
+
background: linear-gradient(to top, rgba(11,26,46,0.5), transparent 60%);
|
| 955 |
+
}
|
| 956 |
+
|
| 957 |
+
.project-img i { position: relative; z-index: 1; color: rgba(255,255,255,0.86); }
|
| 958 |
+
|
| 959 |
+
.favorite-btn {
|
| 960 |
+
position: absolute;
|
| 961 |
+
z-index: 3;
|
| 962 |
+
top: 14px;
|
| 963 |
+
right: 14px;
|
| 964 |
+
width: 42px;
|
| 965 |
+
height: 42px;
|
| 966 |
+
border: 0;
|
| 967 |
+
border-radius: 50%;
|
| 968 |
+
background: rgba(255,255,255,0.92);
|
| 969 |
+
color: var(--primary-blue);
|
| 970 |
+
cursor: pointer;
|
| 971 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.18);
|
| 972 |
+
}
|
| 973 |
+
|
| 974 |
+
.favorite-btn.active { background: var(--accent-gold); color: var(--primary-dark); }
|
| 975 |
+
.project-info { flex: 1; display: flex; flex-direction: column; }
|
| 976 |
+
.project-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: auto; padding-top: 18px; }
|
| 977 |
+
.project-specs { display: flex; flex-wrap: wrap; gap: 7px; margin: 13px 0; }
|
| 978 |
+
.project-specs span { background: var(--light-bg); padding: 5px 9px; border-radius: 8px; font-size: 0.76rem; font-weight: 700; color: var(--primary-blue); }
|
| 979 |
+
|
| 980 |
+
.empty-state {
|
| 981 |
+
grid-column: 1 / -1;
|
| 982 |
+
text-align: center;
|
| 983 |
+
padding: 50px 20px;
|
| 984 |
+
color: var(--gray-text);
|
| 985 |
+
background: var(--white);
|
| 986 |
+
border-radius: 18px;
|
| 987 |
+
}
|
| 988 |
+
|
| 989 |
+
.compare-section {
|
| 990 |
+
padding: 80px 0;
|
| 991 |
+
background: var(--white);
|
| 992 |
+
}
|
| 993 |
+
|
| 994 |
+
.compare-grid {
|
| 995 |
+
display: grid;
|
| 996 |
+
grid-template-columns: repeat(3, 1fr);
|
| 997 |
+
gap: 20px;
|
| 998 |
+
}
|
| 999 |
+
|
| 1000 |
+
.compare-card {
|
| 1001 |
+
border: 1px solid var(--gray-light);
|
| 1002 |
+
border-radius: 20px;
|
| 1003 |
+
padding: 28px;
|
| 1004 |
+
position: relative;
|
| 1005 |
+
display: flex;
|
| 1006 |
+
flex-direction: column;
|
| 1007 |
+
}
|
| 1008 |
+
|
| 1009 |
+
.compare-card.featured {
|
| 1010 |
+
border: 2px solid var(--accent-gold);
|
| 1011 |
+
box-shadow: var(--card-shadow);
|
| 1012 |
+
}
|
| 1013 |
+
|
| 1014 |
+
.compare-card .ribbon {
|
| 1015 |
+
position: absolute;
|
| 1016 |
+
right: 16px;
|
| 1017 |
+
top: 16px;
|
| 1018 |
+
background: var(--accent-gold);
|
| 1019 |
+
padding: 5px 10px;
|
| 1020 |
+
border-radius: 20px;
|
| 1021 |
+
font-size: 0.72rem;
|
| 1022 |
+
font-weight: 800;
|
| 1023 |
+
}
|
| 1024 |
+
|
| 1025 |
+
.compare-card ul { list-style: none; margin: 18px 0 25px; flex: 1; }
|
| 1026 |
+
.compare-card li { margin: 10px 0; display: flex; gap: 9px; font-size: 0.9rem; }
|
| 1027 |
+
.compare-card li i { color: #159957; margin-top: 4px; }
|
| 1028 |
+
|
| 1029 |
+
.modal {
|
| 1030 |
+
position: fixed;
|
| 1031 |
+
inset: 0;
|
| 1032 |
+
background: rgba(6,15,28,0.76);
|
| 1033 |
+
z-index: 3000;
|
| 1034 |
+
display: none;
|
| 1035 |
+
align-items: center;
|
| 1036 |
+
justify-content: center;
|
| 1037 |
+
padding: 20px;
|
| 1038 |
+
}
|
| 1039 |
+
|
| 1040 |
+
.modal.open { display: flex; }
|
| 1041 |
+
|
| 1042 |
+
.modal-card {
|
| 1043 |
+
width: min(760px, 100%);
|
| 1044 |
+
max-height: 90vh;
|
| 1045 |
+
overflow: auto;
|
| 1046 |
+
background: var(--white);
|
| 1047 |
+
border-radius: 22px;
|
| 1048 |
+
padding: 30px;
|
| 1049 |
+
position: relative;
|
| 1050 |
+
}
|
| 1051 |
+
|
| 1052 |
+
.modal-close {
|
| 1053 |
+
position: absolute;
|
| 1054 |
+
right: 18px;
|
| 1055 |
+
top: 18px;
|
| 1056 |
+
border: 0;
|
| 1057 |
+
background: var(--light-bg);
|
| 1058 |
+
border-radius: 50%;
|
| 1059 |
+
width: 40px;
|
| 1060 |
+
height: 40px;
|
| 1061 |
+
cursor: pointer;
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
.modal-hero {
|
| 1065 |
+
min-height: 180px;
|
| 1066 |
+
border-radius: 16px;
|
| 1067 |
+
margin: 18px 0;
|
| 1068 |
+
display: grid;
|
| 1069 |
+
place-items: center;
|
| 1070 |
+
font-size: 4rem;
|
| 1071 |
+
color: rgba(255,255,255,0.86);
|
| 1072 |
+
}
|
| 1073 |
+
|
| 1074 |
+
.toast {
|
| 1075 |
+
position: fixed;
|
| 1076 |
+
right: 20px;
|
| 1077 |
+
bottom: 20px;
|
| 1078 |
+
z-index: 4000;
|
| 1079 |
+
max-width: 360px;
|
| 1080 |
+
background: var(--primary-dark);
|
| 1081 |
+
color: var(--white);
|
| 1082 |
+
padding: 15px 18px;
|
| 1083 |
+
border-radius: 12px;
|
| 1084 |
+
box-shadow: 0 14px 30px rgba(0,0,0,0.25);
|
| 1085 |
+
transform: translateY(120px);
|
| 1086 |
+
opacity: 0;
|
| 1087 |
+
transition: 0.25s ease;
|
| 1088 |
+
}
|
| 1089 |
+
|
| 1090 |
+
.toast.show { transform: translateY(0); opacity: 1; }
|
| 1091 |
+
|
| 1092 |
+
.consent {
|
| 1093 |
+
display: flex;
|
| 1094 |
+
gap: 10px;
|
| 1095 |
+
align-items: flex-start;
|
| 1096 |
+
color: rgba(255,255,255,0.72);
|
| 1097 |
+
font-size: 0.82rem;
|
| 1098 |
+
margin: -3px 0 18px;
|
| 1099 |
+
}
|
| 1100 |
+
|
| 1101 |
+
.consent input { width: auto; margin: 4px 0 0; }
|
| 1102 |
+
.form-status { display: none; padding: 13px; border-radius: 10px; margin-bottom: 15px; font-size: 0.9rem; }
|
| 1103 |
+
.form-status.success { display: block; background: rgba(21,153,87,0.18); border: 1px solid rgba(21,153,87,0.5); }
|
| 1104 |
+
|
| 1105 |
+
.sticky-project-bar {
|
| 1106 |
+
position: fixed;
|
| 1107 |
+
left: 50%;
|
| 1108 |
+
bottom: 16px;
|
| 1109 |
+
transform: translateX(-50%);
|
| 1110 |
+
z-index: 1200;
|
| 1111 |
+
width: min(720px, calc(100% - 24px));
|
| 1112 |
+
background: rgba(11,26,46,0.96);
|
| 1113 |
+
color: var(--white);
|
| 1114 |
+
border: 1px solid rgba(255,255,255,0.12);
|
| 1115 |
+
border-radius: 18px;
|
| 1116 |
+
padding: 12px 16px;
|
| 1117 |
+
display: none;
|
| 1118 |
+
justify-content: space-between;
|
| 1119 |
+
align-items: center;
|
| 1120 |
+
gap: 12px;
|
| 1121 |
+
box-shadow: 0 14px 34px rgba(0,0,0,0.28);
|
| 1122 |
+
backdrop-filter: blur(12px);
|
| 1123 |
+
}
|
| 1124 |
+
|
| 1125 |
+
.sticky-project-bar.show { display: flex; }
|
| 1126 |
+
.sticky-project-bar small { display: block; opacity: 0.68; }
|
| 1127 |
+
|
| 1128 |
+
/* RESPONSIIVSUS */
|
| 1129 |
+
@media (max-width: 992px) {
|
| 1130 |
+
.hero-grid, .about-grid, .contact-grid, .form-grid-2 {
|
| 1131 |
+
grid-template-columns: 1fr;
|
| 1132 |
+
}
|
| 1133 |
+
.hero { text-align: center; }
|
| 1134 |
+
.hero p { margin-left: auto; margin-right: auto; }
|
| 1135 |
+
.hero-badges { justify-content: center; }
|
| 1136 |
+
.hero h1 { font-size: 2.8rem; }
|
| 1137 |
+
.journey-grid { grid-template-columns: repeat(3, 1fr); }
|
| 1138 |
+
.package-selector, .compare-grid { grid-template-columns: 1fr; }
|
| 1139 |
+
.brief-grid { grid-template-columns: repeat(2, 1fr); }
|
| 1140 |
+
.catalog-toolbar { grid-template-columns: 1fr 1fr; }
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
+
@media (max-width: 768px) {
|
| 1144 |
+
nav ul {
|
| 1145 |
+
display: none;
|
| 1146 |
+
flex-direction: column;
|
| 1147 |
+
position: absolute;
|
| 1148 |
+
top: 100%;
|
| 1149 |
+
left: 0;
|
| 1150 |
+
width: 100%;
|
| 1151 |
+
background: var(--primary-dark);
|
| 1152 |
+
padding: 25px 0;
|
| 1153 |
+
box-shadow: 0 10px 20px rgba(0,0,0,0.3);
|
| 1154 |
+
}
|
| 1155 |
+
nav ul.open { display: flex; }
|
| 1156 |
+
.menu-toggle { display: block; }
|
| 1157 |
+
.builder-card { padding: 20px; }
|
| 1158 |
+
.hero h1 { font-size: 2.2rem; }
|
| 1159 |
+
.total-price-box { text-align: left; margin-top: 10px; }
|
| 1160 |
+
.journey-grid, .catalog-toolbar, .brief-grid { grid-template-columns: 1fr; }
|
| 1161 |
+
.project-actions { grid-template-columns: 1fr; }
|
| 1162 |
+
.sticky-project-bar { align-items: stretch; }
|
| 1163 |
+
}
|
| 1164 |
+
|
| 1165 |
+
@media print {
|
| 1166 |
+
header, .hero, .trust-bar, .journey, .services, .projects, .about, .contact, footer,
|
| 1167 |
+
.tab-buttons, .package-selector, .action-row, .sticky-project-bar, .toast, .ai-image-container { display: none !important; }
|
| 1168 |
+
body, .builder-section { background: #fff; }
|
| 1169 |
+
.builder-section { padding: 0; }
|
| 1170 |
+
.builder-card { box-shadow: none; border: 0; padding: 0; }
|
| 1171 |
+
.results-panel { color: #111; background: #fff; border: 1px solid #ddd; }
|
| 1172 |
+
.results-panel h3, .results-panel h4, .results-panel strong { color: #111 !important; }
|
| 1173 |
+
.stage-card, .materials-list { border: 1px solid #ddd; background: #fff; color: #111; }
|
| 1174 |
+
.stage-card p, .materials-list li { color: #333; }
|
| 1175 |
+
}
|
| 1176 |
+
</style>
|
| 1177 |
+
</head>
|
| 1178 |
+
<body>
|
| 1179 |
+
|
| 1180 |
+
<!-- ====== HEADER ====== -->
|
| 1181 |
+
<header>
|
| 1182 |
+
<div class="container header-flex">
|
| 1183 |
+
<div class="logo">
|
| 1184 |
+
<a href="#"><i class="fas fa-hard-hat" style="color: var(--accent-gold);"></i> MTM <span>Ehitus</span> OÜ</a>
|
| 1185 |
+
</div>
|
| 1186 |
+
<div class="menu-toggle" id="menuToggle">
|
| 1187 |
+
<i class="fas fa-bars"></i>
|
| 1188 |
+
</div>
|
| 1189 |
+
<nav>
|
| 1190 |
+
<ul id="navMenu">
|
| 1191 |
+
<li><a href="#kalkulaator">Lahenduse planeerija</a></li>
|
| 1192 |
+
<li><a href="#paketid">Paketid</a></li>
|
| 1193 |
+
<li><a href="#teenused">Mida saab tellida?</a></li>
|
| 1194 |
+
<li><a href="#projektid">Valitud disainid</a></li>
|
| 1195 |
+
<li><a href="#meist">Meist</a></li>
|
| 1196 |
+
<li><a href="#kontakt" class="nav-cta">Küsi pakkumist</a></li>
|
| 1197 |
+
</ul>
|
| 1198 |
+
</nav>
|
| 1199 |
+
</div>
|
| 1200 |
+
</header>
|
| 1201 |
+
|
| 1202 |
+
<!-- ====== HERO ====== -->
|
| 1203 |
+
<section class="hero">
|
| 1204 |
+
<div class="container hero-grid">
|
| 1205 |
+
<div>
|
| 1206 |
+
<h1>Ideest <span>3D disainini</span>, projektist <span>valmis lahenduseni</span></h1>
|
| 1207 |
+
<p>MTM Ehitus OÜ loob ja müüb läbimõeldud 3D-disainilahendusi, ehitusprojekte ning terviklikke võtmed-kätte lahendusi. Vali olemasolev kontseptsioon või telli täiesti personaalne projekt.</p>
|
| 1208 |
+
<div style="display: flex; gap: 15px; flex-wrap: wrap;">
|
| 1209 |
+
<a href="#projektid" class="btn btn-primary"><i class="fas fa-cubes"></i> Vaata valitud disaine</a>
|
| 1210 |
+
<a href="#kalkulaator" class="btn btn-outline"><i class="fas fa-drafting-compass"></i> Koosta oma lähteülesanne</a>
|
| 1211 |
+
</div>
|
| 1212 |
+
<div class="hero-badges">
|
| 1213 |
+
<div class="badge-item"><i class="fas fa-award"></i> 5+ aastat kogemust</div>
|
| 1214 |
+
<div class="badge-item"><i class="fas fa-building"></i> 100+ edukat objekti</div>
|
| 1215 |
+
<div class="badge-item"><i class="fas fa-shield-alt"></i> Usaldusväärne partner</div>
|
| 1216 |
+
</div>
|
| 1217 |
+
</div>
|
| 1218 |
+
<div class="hero-card-preview">
|
| 1219 |
+
<i class="fas fa-home main-icon"></i>
|
| 1220 |
+
<h3>Vali täpselt selline lahendus, nagu vajad</h3>
|
| 1221 |
+
<p style="font-size: 0.95rem; opacity: 0.8; margin-top: 10px;">Osta 3D-disain, telli selle põhjal terviklik ehitusprojekt või anna kogu teostus meie juhtida.</p>
|
| 1222 |
+
</div>
|
| 1223 |
+
</div>
|
| 1224 |
+
</section>
|
| 1225 |
+
|
| 1226 |
+
<!-- ====== USALDUSRIBA ====== -->
|
| 1227 |
+
<section class="trust-bar">
|
| 1228 |
+
<div class="container trust-grid">
|
| 1229 |
+
<div class="trust-item">
|
| 1230 |
+
<span class="number">16263144</span>
|
| 1231 |
+
<span class="label">Registrikood</span>
|
| 1232 |
+
</div>
|
| 1233 |
+
<div class="trust-item">
|
| 1234 |
+
<span class="number">2021</span>
|
| 1235 |
+
<span class="label">Asutatud</span>
|
| 1236 |
+
</div>
|
| 1237 |
+
<div class="trust-item">
|
| 1238 |
+
<span class="number">EE102412910</span>
|
| 1239 |
+
<span class="label">KMKR number</span>
|
| 1240 |
+
</div>
|
| 1241 |
+
<div class="trust-item">
|
| 1242 |
+
<span class="number">0 €</span>
|
| 1243 |
+
<span class="label">Maksuvõlg</span>
|
| 1244 |
+
</div>
|
| 1245 |
+
<div class="trust-item">
|
| 1246 |
+
<span class="number">Tallinn</span>
|
| 1247 |
+
<span class="label">Rävala pst 8</span>
|
| 1248 |
+
</div>
|
| 1249 |
+
</div>
|
| 1250 |
+
</section>
|
| 1251 |
+
|
| 1252 |
+
<!-- ====== KLIENDITEEKOND ====== -->
|
| 1253 |
+
<section class="journey" aria-label="Projekti tellimise etapid">
|
| 1254 |
+
<div class="container journey-grid">
|
| 1255 |
+
<div class="journey-step"><span>1</span> Vali näidis või oma idee</div>
|
| 1256 |
+
<div class="journey-step"><span>2</span> Kohanda lähteandmed</div>
|
| 1257 |
+
<div class="journey-step"><span>3</span> Võrdle teenusepakette</div>
|
| 1258 |
+
<div class="journey-step"><span>4</span> Saa eelarve ja ajakava</div>
|
| 1259 |
+
<div class="journey-step"><span>5</span> Saada valmis lähteülesanne</div>
|
| 1260 |
+
</div>
|
| 1261 |
+
</section>
|
| 1262 |
+
|
| 1263 |
+
<!-- ====== PROJEKTI AI-PLANEERIJA & EELARVEKALKULAATOR ====== -->
|
| 1264 |
+
<section class="builder-section" id="kalkulaator">
|
| 1265 |
+
<div class="container">
|
| 1266 |
+
<div class="section-title">
|
| 1267 |
+
<span class="eyebrow"><i class="fas fa-wand-magic-sparkles"></i> Toimiv projektiplaneerija</span>
|
| 1268 |
+
<h2>Koosta lähteülesanne, eelarve ja ajakava</h2>
|
| 1269 |
+
<p style="color: var(--gray-text); max-width: 720px; margin: 10px auto 0;">Vali disain, teenusemaht ja tehnilised lähteandmed. Planeerija koostab kohe jagatava projektikokkuvõtte ning realistliku hinnavahemiku.</p>
|
| 1270 |
+
<span></span>
|
| 1271 |
+
</div>
|
| 1272 |
+
|
| 1273 |
+
<div class="builder-card">
|
| 1274 |
+
<!-- TAB NUPUD -->
|
| 1275 |
+
<div class="tab-buttons">
|
| 1276 |
+
<button class="tab-btn active" onclick="switchTab('customizer')"><i class="fas fa-sliders-h"></i> 1. Lahenduse lähteandmed</button>
|
| 1277 |
+
<button class="tab-btn" onclick="switchTab('ai-prompt')"><i class="fas fa-magic"></i> 2. Kirjelda oma ideed</button>
|
| 1278 |
+
</div>
|
| 1279 |
+
|
| 1280 |
+
<h3>Vali tellitav lahendus</h3>
|
| 1281 |
+
<div class="package-selector" id="package-selector">
|
| 1282 |
+
<div class="package-option" data-package="concept" onclick="selectPackage('concept', this)">
|
| 1283 |
+
<i class="fas fa-cube" style="color: var(--primary-blue); font-size: 1.7rem;"></i>
|
| 1284 |
+
<h3>3D-kontseptsioon</h3>
|
| 1285 |
+
<p>Välisilme, mahud, materjalid ja esitlusvisuaalid.</p>
|
| 1286 |
+
<div class="package-price">alates 1 450 €</div>
|
| 1287 |
+
</div>
|
| 1288 |
+
<div class="package-option selected" data-package="project" onclick="selectPackage('project', this)">
|
| 1289 |
+
<i class="fas fa-drafting-compass" style="color: var(--primary-blue); font-size: 1.7rem;"></i>
|
| 1290 |
+
<h3>Ehitusprojekt</h3>
|
| 1291 |
+
<p>3D-kontseptsioon, joonised ja tehnilised põhilahendused.</p>
|
| 1292 |
+
<div class="package-price">alates 4 900 €</div>
|
| 1293 |
+
</div>
|
| 1294 |
+
<div class="package-option" data-package="turnkey" onclick="selectPackage('turnkey', this)">
|
| 1295 |
+
<i class="fas fa-key" style="color: var(--primary-blue); font-size: 1.7rem;"></i>
|
| 1296 |
+
<h3>Täislahendus</h3>
|
| 1297 |
+
<p>Disain, projekt, juhtimine ja valmis teostus.</p>
|
| 1298 |
+
<div class="package-price">personaalne eelarve</div>
|
| 1299 |
+
</div>
|
| 1300 |
+
</div>
|
| 1301 |
+
|
| 1302 |
+
<!-- TAHVEL 1: PARAMEETRITE PÕHJAL KONFIGURAATOR -->
|
| 1303 |
+
<div id="tab-customizer" class="tab-content active">
|
| 1304 |
+
<div class="form-grid-2">
|
| 1305 |
+
<div>
|
| 1306 |
+
<div class="form-group">
|
| 1307 |
+
<label><i class="fas fa-map-location-dot"></i> Projekti asukoht / piirkond</label>
|
| 1308 |
+
<select id="project-region" onchange="updateCalculator()">
|
| 1309 |
+
<option value="harju">Harjumaa / Tallinn</option>
|
| 1310 |
+
<option value="tartu">Tartumaa</option>
|
| 1311 |
+
<option value="parnu">Pärnumaa</option>
|
| 1312 |
+
<option value="rapla">Raplamaa</option>
|
| 1313 |
+
<option value="other">Muu Eesti piirkond</option>
|
| 1314 |
+
</select>
|
| 1315 |
+
</div>
|
| 1316 |
+
|
| 1317 |
+
<div class="form-group">
|
| 1318 |
+
<label><i class="fas fa-building-user"></i> Hoone kasutusotstarve</label>
|
| 1319 |
+
<select id="building-use" onchange="updateCalculator()">
|
| 1320 |
+
<option value="home">Eramu</option>
|
| 1321 |
+
<option value="summer">Suvila / puhkemaja</option>
|
| 1322 |
+
<option value="office">Äri- või büroohoone</option>
|
| 1323 |
+
<option value="outbuilding">Abihoone / stuudio</option>
|
| 1324 |
+
</select>
|
| 1325 |
+
</div>
|
| 1326 |
+
|
| 1327 |
+
<div class="form-group">
|
| 1328 |
+
<label><i class="fas fa-ruler-combined"></i> Kavandatava hoone üldpindala: <span id="area-val" style="color: var(--primary-blue); font-weight: 800;">120</span> m²</label>
|
| 1329 |
+
<input type="range" id="house-area" min="40" max="350" value="120" step="5" class="form-control" oninput="updateCalculator()">
|
| 1330 |
+
</div>
|
| 1331 |
+
|
| 1332 |
+
<div class="form-group">
|
| 1333 |
+
<label><i class="fas fa-layer-group"></i> Korruste arv</label>
|
| 1334 |
+
<div class="option-cards" id="floors-options">
|
| 1335 |
+
<div class="option-card selected" onclick="selectOption('floors', 1, this)"><i class="fas fa-align-justify"></i> 1 Korrus</div>
|
| 1336 |
+
<div class="option-card" onclick="selectOption('floors', 1.5, this)"><i class="fas fa-cubes"></i> 1.5 Korrust (katusekorrus)</div>
|
| 1337 |
+
<div class="option-card" onclick="selectOption('floors', 2, this)"><i class="fas fa-building"></i> 2 Täiskorrust</div>
|
| 1338 |
+
</div>
|
| 1339 |
+
</div>
|
| 1340 |
+
|
| 1341 |
+
<div class="form-group">
|
| 1342 |
+
<label><i class="fas fa-cubes"></i> Vundamendi tüüp</label>
|
| 1343 |
+
<select id="foundation-type" onchange="updateCalculator()">
|
| 1344 |
+
<option value="plate">Plaatvundament (Parim energiatõhusus & põrandaküte)</option>
|
| 1345 |
+
<option value="strip">Lintvundament (Klassikaline ja tugev)</option>
|
| 1346 |
+
<option value="pile">Vaiavundament (Nõudlikumale pinnasele)</option>
|
| 1347 |
+
</select>
|
| 1348 |
+
</div>
|
| 1349 |
+
|
| 1350 |
+
<div class="form-group">
|
| 1351 |
+
<label><i class="fas fa-border-all"></i> Seinte konstruktsioon</label>
|
| 1352 |
+
<select id="wall-type" onchange="updateCalculator()">
|
| 1353 |
+
<option value="block">Kergplokk (Bauroc / Leca) + krohv/vooder</option>
|
| 1354 |
+
<option value="wood">Puitkarkass / Elementmaja (Kiire paigaldus)</option>
|
| 1355 |
+
<option value="clt">CLT Ristkihtpuit (Ökoloogiline & eksklusiivne)</option>
|
| 1356 |
+
</select>
|
| 1357 |
+
</div>
|
| 1358 |
+
</div>
|
| 1359 |
+
|
| 1360 |
+
<div>
|
| 1361 |
+
<div class="form-group">
|
| 1362 |
+
<label><i class="fas fa-home"></i> Katuse tüüp ja kattematerjal</label>
|
| 1363 |
+
<select id="roof-type" onchange="updateCalculator()">
|
| 1364 |
+
<option value="gable-tin">Viilkatus - Profiilplekk</option>
|
| 1365 |
+
<option value="gable-tile">Viilkatus - Betoon/Keraamiline kivi</option>
|
| 1366 |
+
<option value="flat">Lamekatus - SBS rullmaterjal</option>
|
| 1367 |
+
</select>
|
| 1368 |
+
</div>
|
| 1369 |
+
|
| 1370 |
+
<div class="form-group">
|
| 1371 |
+
<label><i class="fas fa-fire"></i> Küttesüsteem ja energiaklass</label>
|
| 1372 |
+
<select id="heating-type" onchange="updateCalculator()">
|
| 1373 |
+
<option value="geo">Maaküte + Soojustagastusega ventilatsioon (A-klass)</option>
|
| 1374 |
+
<option value="air-water">Õhk-vesi soojuspump + Ventilatsioon (A-klass)</option>
|
| 1375 |
+
<option value="air-air">Õhksoojuspump + Elektripõrandaküte (B-klass)</option>
|
| 1376 |
+
</select>
|
| 1377 |
+
</div>
|
| 1378 |
+
|
| 1379 |
+
<div class="form-group">
|
| 1380 |
+
<label><i class="fas fa-paint-roller"></i> Viimistlusaste</label>
|
| 1381 |
+
<div class="option-cards" id="finish-options">
|
| 1382 |
+
<div class="option-card" onclick="selectOption('finish', 'shell', this)"><i class="fas fa-square"></i> Kinnine karp</div>
|
| 1383 |
+
<div class="option-card" onclick="selectOption('finish', 'white', this)"><i class="fas fa-brush"></i> Valge karp</div>
|
| 1384 |
+
<div class="option-card selected" onclick="selectOption('finish', 'turnkey', this)"><i class="fas fa-key"></i> Võtmed kätte</div>
|
| 1385 |
+
</div>
|
| 1386 |
+
</div>
|
| 1387 |
+
|
| 1388 |
+
<button class="btn btn-secondary" style="width: 100%; margin-top: 10px;" onclick="saveDraft()">
|
| 1389 |
+
<i class="fas fa-floppy-disk"></i> Salvesta projekt mustandina
|
| 1390 |
+
</button>
|
| 1391 |
+
</div>
|
| 1392 |
+
</div>
|
| 1393 |
+
</div>
|
| 1394 |
+
|
| 1395 |
+
<!-- TAHVEL 2: AI KIRJELDUS JA PILDI ANALÜÜS -->
|
| 1396 |
+
<div id="tab-ai-prompt" class="tab-content">
|
| 1397 |
+
<div class="form-group">
|
| 1398 |
+
<label><i class="fas fa-comment-dots"></i> Kirjelda soovitud disaini, ruumiprogrammi ja kasutusotstarvet:</label>
|
| 1399 |
+
<textarea id="ai-description" rows="4" placeholder="Nt. Soovin 140 m² modernset ühekorruselist hoonet. Avatud eluruum, 3 magamistuba, maaküte, puitfassaad tumedate detailidega. Esmalt vajan 3D-disaini ja seejärel ehitusprojekti..."></textarea>
|
| 1400 |
+
</div>
|
| 1401 |
+
|
| 1402 |
+
<div class="form-group">
|
| 1403 |
+
<label><i class="fas fa-camera"></i> Või laadi üles krundifoto / inspiratsioonipilt (Valikuline):</label>
|
| 1404 |
+
<input type="file" id="ai-image-upload" accept="image/*" class="form-control">
|
| 1405 |
+
</div>
|
| 1406 |
+
|
| 1407 |
+
<button class="btn btn-primary" onclick="analyzeProjectIdea()"><i class="fas fa-list-check"></i> Analüüsi ideed ja koosta projektisuund</button>
|
| 1408 |
+
|
| 1409 |
+
<div id="ai-analysis-result" style="display: none; margin-top: 20px; padding: 20px; background: var(--light-bg); border-radius: 12px; border-left: 4px solid var(--primary-blue);">
|
| 1410 |
+
<h4><i class="fas fa-clipboard-check"></i> Projektianalüüs ja soovitused:</h4>
|
| 1411 |
+
<p id="ai-text-output" style="margin-top: 10px; color: var(--gray-text); font-size: 0.95rem;"></p>
|
| 1412 |
+
</div>
|
| 1413 |
+
</div>
|
| 1414 |
+
|
| 1415 |
+
<!-- AUTOMAATNE PROJEKTIKAART -->
|
| 1416 |
+
<div class="brief-panel" id="project-brief">
|
| 1417 |
+
<h3><i class="fas fa-file-signature"></i> Sinu projekti hetkeseis</h3>
|
| 1418 |
+
<p id="brief-title" style="color: var(--gray-text);">Uus 120 m² eramu – ehitusprojekti pakett</p>
|
| 1419 |
+
<div class="brief-grid">
|
| 1420 |
+
<div class="brief-stat"><span>Valitud lahendus</span><strong id="brief-package">Ehitusprojekt</strong></div>
|
| 1421 |
+
<div class="brief-stat"><span>Hinnavahemik</span><strong id="brief-range">4 900–7 900 €</strong></div>
|
| 1422 |
+
<div class="brief-stat"><span>Eeldatav kestus</span><strong id="brief-duration">8–14 nädalat</strong></div>
|
| 1423 |
+
<div class="brief-stat"><span>Valmidusaste</span><strong id="brief-readiness">60%</strong></div>
|
| 1424 |
+
</div>
|
| 1425 |
+
<div class="action-row">
|
| 1426 |
+
<button class="btn btn-secondary btn-small" onclick="downloadBrief()"><i class="fas fa-download"></i> Laadi lähteülesanne</button>
|
| 1427 |
+
<button class="btn btn-secondary btn-small" onclick="window.print()"><i class="fas fa-print"></i> Prindi / salvesta PDF</button>
|
| 1428 |
+
<button class="btn btn-secondary btn-small" onclick="copyBrief()"><i class="fas fa-copy"></i> Kopeeri kokkuvõte</button>
|
| 1429 |
+
</div>
|
| 1430 |
+
</div>
|
| 1431 |
+
|
| 1432 |
+
<!-- TULEMUSTE JA KULUDE DYNAMILINE PANEEL -->
|
| 1433 |
+
<div class="results-panel">
|
| 1434 |
+
<div class="results-header">
|
| 1435 |
+
<div>
|
| 1436 |
+
<h3>Täislahenduse esialgne teostuseelarve</h3>
|
| 1437 |
+
<p style="opacity: 0.8; font-size: 0.9rem;">Hinnanguline projekteerimise ja ehituse lähte-eelarve valitud parameetrite põhjal. See ei ole kinnisvara müügihind ega siduv pakkumine.</p>
|
| 1438 |
+
</div>
|
| 1439 |
+
<div class="total-price-box">
|
| 1440 |
+
<span style="font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; opacity: 0.8;">Täislahenduse lähte-eelarve:</span>
|
| 1441 |
+
<div class="price" id="total-price">180,000 €</div>
|
| 1442 |
+
<span style="font-size: 0.8rem; color: #cbd5e1;" id="price-per-m2">~1500 € / m²</span>
|
| 1443 |
+
</div>
|
| 1444 |
+
</div>
|
| 1445 |
+
|
| 1446 |
+
<h4 style="color: var(--accent-gold); margin-bottom: 15px;"><i class="fas fa-list-ol"></i> Paketi etapid ja eelarvejaotus:</h4>
|
| 1447 |
+
<div class="stage-breakdown">
|
| 1448 |
+
<div class="stage-card">
|
| 1449 |
+
<h4><span id="stage-label-1">1. Lähteülesanne</span> <span class="stage-price" id="cost-foundation">980 €</span></h4>
|
| 1450 |
+
<p id="stage-desc-1" style="font-size: 0.8rem; opacity: 0.7;">Vajadused, krundiinfo, ruumiprogramm ja eesmärgid.</p>
|
| 1451 |
+
</div>
|
| 1452 |
+
<div class="stage-card">
|
| 1453 |
+
<h4><span id="stage-label-2">2. Kontseptsioon</span> <span class="stage-price" id="cost-walls">1 960 €</span></h4>
|
| 1454 |
+
<p id="stage-desc-2" style="font-size: 0.8rem; opacity: 0.7;">Mahud, plaaniloogika, välisilme ja materjalid.</p>
|
| 1455 |
+
</div>
|
| 1456 |
+
<div class="stage-card">
|
| 1457 |
+
<h4><span id="stage-label-3">3. Põhijoonised</span> <span class="stage-price" id="cost-roof">1 470 €</span></h4>
|
| 1458 |
+
<p id="stage-desc-3" style="font-size: 0.8rem; opacity: 0.7;">Plaanid, vaated, lõiked ja põhikonstruktsioonid.</p>
|
| 1459 |
+
</div>
|
| 1460 |
+
<div class="stage-card">
|
| 1461 |
+
<h4><span id="stage-label-4">4. Tehniline kooskõla</span> <span class="stage-price" id="cost-utility">980 €</span></h4>
|
| 1462 |
+
<p id="stage-desc-4" style="font-size: 0.8rem; opacity: 0.7;">Konstruktsioonide ja tehnosüsteemide lähteinfo.</p>
|
| 1463 |
+
</div>
|
| 1464 |
+
<div class="stage-card">
|
| 1465 |
+
<h4><span id="stage-label-5">5. Üleandmine</span> <span class="stage-price" id="cost-finish">490 €</span></h4>
|
| 1466 |
+
<p id="stage-desc-5" style="font-size: 0.8rem; opacity: 0.7;">Failipakett, konsultatsioon ja järgmiste sammude plaan.</p>
|
| 1467 |
+
</div>
|
| 1468 |
+
</div>
|
| 1469 |
+
|
| 1470 |
+
<!-- VALITUD PAKETI PÕHISISU -->
|
| 1471 |
+
<div class="materials-list">
|
| 1472 |
+
<h4 style="color: var(--white);"><i class="fas fa-boxes"></i> Valitud paketi põhisisu:</h4>
|
| 1473 |
+
<ul>
|
| 1474 |
+
<li><i class="fas fa-check"></i> <strong id="deliverable-1" style="color:var(--white);">Lähteülesanne ja ruumiprogramm</strong></li>
|
| 1475 |
+
<li><i class="fas fa-check"></i> <strong id="deliverable-2" style="color:var(--white);">3D-kontseptsioon ja materjalid</strong></li>
|
| 1476 |
+
<li><i class="fas fa-check"></i> <strong id="deliverable-3" style="color:var(--white);">Plaanid, vaated ja lõiked</strong></li>
|
| 1477 |
+
<li><i class="fas fa-check"></i> <strong id="deliverable-4" style="color:var(--white);">Failipakett ja konsultatsioon</strong></li>
|
| 1478 |
+
</ul>
|
| 1479 |
+
</div>
|
| 1480 |
+
|
| 1481 |
+
<div class="action-row" style="justify-content: flex-end;">
|
| 1482 |
+
<button class="btn btn-secondary" onclick="shareByEmail()"><i class="fas fa-envelope"></i> Saada e-postiga</button>
|
| 1483 |
+
<button class="btn btn-secondary" onclick="shareByWhatsApp()"><i class="fab fa-whatsapp"></i> WhatsApp</button>
|
| 1484 |
+
<button class="btn btn-primary" onclick="sendProjectToForm()">
|
| 1485 |
+
<i class="fas fa-paper-plane"></i> Küsi selle lahenduse personaalset pakkumist
|
| 1486 |
+
</button>
|
| 1487 |
+
</div>
|
| 1488 |
+
</div>
|
| 1489 |
+
</div>
|
| 1490 |
+
</div>
|
| 1491 |
+
</section>
|
| 1492 |
+
|
| 1493 |
+
<!-- ====== PAKETTIDE VÕRDLUS ====== -->
|
| 1494 |
+
<section class="compare-section" id="paketid">
|
| 1495 |
+
<div class="container">
|
| 1496 |
+
<div class="section-title">
|
| 1497 |
+
<span class="eyebrow"><i class="fas fa-scale-balanced"></i> Läbipaistev teenusemaht</span>
|
| 1498 |
+
<h2>Kolm võimalust projektiga edasi minna</h2>
|
| 1499 |
+
<p style="color: var(--gray-text);">Alustada saab ainult visuaalsest kontseptsioonist ja liikuda hiljem sama lähteinfo põhjal järgmisse etappi.</p>
|
| 1500 |
+
<span></span>
|
| 1501 |
+
</div>
|
| 1502 |
+
<div class="compare-grid">
|
| 1503 |
+
<article class="compare-card">
|
| 1504 |
+
<h3>3D-kontseptsioon</h3>
|
| 1505 |
+
<p class="package-price">1 450–2 900 €</p>
|
| 1506 |
+
<p style="color: var(--gray-text);">Sobib idee, mahu ja välisilme kinnitamiseks.</p>
|
| 1507 |
+
<ul>
|
| 1508 |
+
<li><i class="fas fa-check-circle"></i> Lähteülesande töötuba</li>
|
| 1509 |
+
<li><i class="fas fa-check-circle"></i> 2 ruumilise lahenduse suunda</li>
|
| 1510 |
+
<li><i class="fas fa-check-circle"></i> 4 esitlusvisualiseeringut</li>
|
| 1511 |
+
<li><i class="fas fa-check-circle"></i> 2 parandusringi</li>
|
| 1512 |
+
<li><i class="fas fa-check-circle"></i> Materjali- ja värvikaart</li>
|
| 1513 |
+
</ul>
|
| 1514 |
+
<button class="btn btn-secondary" onclick="choosePackageAndScroll('concept')">Vali 3D-disain</button>
|
| 1515 |
+
</article>
|
| 1516 |
+
<article class="compare-card featured">
|
| 1517 |
+
<span class="ribbon">Enim valitud</span>
|
| 1518 |
+
<h3>Ehitusprojekt</h3>
|
| 1519 |
+
<p class="package-price">4 900–11 900 €</p>
|
| 1520 |
+
<p style="color: var(--gray-text);">Sobib ehitusloa ja hinnapakkumiste ettevalmistamiseks.</p>
|
| 1521 |
+
<ul>
|
| 1522 |
+
<li><i class="fas fa-check-circle"></i> Kõik 3D-kontseptsiooni osad</li>
|
| 1523 |
+
<li><i class="fas fa-check-circle"></i> Plaanid, vaated ja lõiked</li>
|
| 1524 |
+
<li><i class="fas fa-check-circle"></i> Põhikonstruktsioonide lahendus</li>
|
| 1525 |
+
<li><i class="fas fa-check-circle"></i> Tehnosüsteemide lähteinfo</li>
|
| 1526 |
+
<li><i class="fas fa-check-circle"></i> Projekti eelarve lähteandmed</li>
|
| 1527 |
+
</ul>
|
| 1528 |
+
<button class="btn btn-primary" onclick="choosePackageAndScroll('project')">Vali ehitusprojekt</button>
|
| 1529 |
+
</article>
|
| 1530 |
+
<article class="compare-card">
|
| 1531 |
+
<h3>Täislahendus</h3>
|
| 1532 |
+
<p class="package-price">alates 1 550 €/m²</p>
|
| 1533 |
+
<p style="color: var(--gray-text);">Sobib kliendile, kes soovib üht vastutavat partnerit.</p>
|
| 1534 |
+
<ul>
|
| 1535 |
+
<li><i class="fas fa-check-circle"></i> Disain ja ehitusprojekt</li>
|
| 1536 |
+
<li><i class="fas fa-check-circle"></i> Detailne kalkulatsioon</li>
|
| 1537 |
+
<li><i class="fas fa-check-circle"></i> Hanked ja partnerite juhtimine</li>
|
| 1538 |
+
<li><i class="fas fa-check-circle"></i> Ehituse teostus ja kvaliteedikontroll</li>
|
| 1539 |
+
<li><i class="fas fa-check-circle"></i> Üleandmisdokumentatsioon</li>
|
| 1540 |
+
</ul>
|
| 1541 |
+
<button class="btn btn-secondary" onclick="choosePackageAndScroll('turnkey')">Vali täislahendus</button>
|
| 1542 |
+
</article>
|
| 1543 |
+
</div>
|
| 1544 |
+
<p style="text-align: center; color: var(--gray-text); margin-top: 20px; font-size: 0.82rem;">Hinnad on esialgsed vahemikud. Lõplik hind sõltub mahust, asukohast, lähteandmete valmidusest ja eriosade vajadusest.</p>
|
| 1545 |
+
</div>
|
| 1546 |
+
</section>
|
| 1547 |
+
|
| 1548 |
+
<!-- ====== TEENUSED ====== -->
|
| 1549 |
+
<section class="services" id="teenused">
|
| 1550 |
+
<div class="container">
|
| 1551 |
+
<div class="section-title">
|
| 1552 |
+
<h2>Vali sobiv koostöömaht</h2>
|
| 1553 |
+
<p style="color: var(--gray-text);">Alusta ideest või vali terviklahendus – iga etappi saab tellida eraldi</p>
|
| 1554 |
+
<span></span>
|
| 1555 |
+
</div>
|
| 1556 |
+
<div class="services-grid">
|
| 1557 |
+
<div class="service-card">
|
| 1558 |
+
<i class="fas fa-cube"></i>
|
| 1559 |
+
<h3>3D-disain ja kontseptsioon</h3>
|
| 1560 |
+
<p>Hoone välisilme, mahu, materjalide ja ruumilise idee professionaalne visualiseerimine enne projekteerimist.</p>
|
| 1561 |
+
</div>
|
| 1562 |
+
<div class="service-card">
|
| 1563 |
+
<i class="fas fa-drafting-compass"></i>
|
| 1564 |
+
<h3>Ehitusprojekt</h3>
|
| 1565 |
+
<p>Valitud 3D-kontseptsiooni arendamine tehniliseks projektiks koos vajalike jooniste ja lahendustega.</p>
|
| 1566 |
+
</div>
|
| 1567 |
+
<div class="service-card">
|
| 1568 |
+
<i class="fas fa-key"></i>
|
| 1569 |
+
<h3>Võtmed-kätte täislahendus</h3>
|
| 1570 |
+
<p>Disain, projekteerimine, eelarvestamine, ehitusjuhtimine ja teostus ühe vastutava partneri kaudu.</p>
|
| 1571 |
+
</div>
|
| 1572 |
+
<div class="service-card">
|
| 1573 |
+
<i class="fas fa-pencil-ruler"></i>
|
| 1574 |
+
<h3>Personaalne erilahendus</h3>
|
| 1575 |
+
<p>Individuaalne arhitektuurne kontseptsioon kliendi krundi, vajaduste, stiili ja eelarve järgi.</p>
|
| 1576 |
+
</div>
|
| 1577 |
+
<div class="service-card">
|
| 1578 |
+
<i class="fas fa-sync-alt"></i>
|
| 1579 |
+
<h3>Olemasoleva disaini kohandamine</h3>
|
| 1580 |
+
<p>Klientide poolt valitud lahenduste kohandamine uuele krundile, ruumiprogrammile või viimistlusstiilile.</p>
|
| 1581 |
+
</div>
|
| 1582 |
+
<div class="service-card">
|
| 1583 |
+
<i class="fas fa-tasks"></i>
|
| 1584 |
+
<h3>Projekti- ja ehitusjuhtimine</h3>
|
| 1585 |
+
<p>Partnerite koordineerimine, ajakava, eelarve ja kvaliteedi juhtimine ideest valmimiseni.</p>
|
| 1586 |
+
</div>
|
| 1587 |
+
</div>
|
| 1588 |
+
</div>
|
| 1589 |
+
</section>
|
| 1590 |
+
|
| 1591 |
+
<!-- ====== PROJEKTID ====== -->
|
| 1592 |
+
<section class="projects" id="projektid">
|
| 1593 |
+
<div class="container">
|
| 1594 |
+
<div class="section-title">
|
| 1595 |
+
<h2>Klientide valitud 3D-disainid ja lahendused</h2>
|
| 1596 |
+
<p style="color: var(--gray-text);">Filtreeri näidiseid, ava detailvaade, salvesta lemmikud ja kasuta sobivat lahendust oma projekti lähtepunktina.</p>
|
| 1597 |
+
<span></span>
|
| 1598 |
+
</div>
|
| 1599 |
+
<div class="catalog-toolbar">
|
| 1600 |
+
<input type="search" id="catalog-search" placeholder="Otsi nime, stiili või materjali..." oninput="renderProjects()">
|
| 1601 |
+
<select id="catalog-type" onchange="renderProjects()">
|
| 1602 |
+
<option value="all">Kõik hoonetüübid</option>
|
| 1603 |
+
<option value="home">Eramud</option>
|
| 1604 |
+
<option value="summer">Puhkemajad</option>
|
| 1605 |
+
<option value="office">Ärihooned</option>
|
| 1606 |
+
<option value="outbuilding">Abihooned</option>
|
| 1607 |
+
</select>
|
| 1608 |
+
<select id="catalog-area" onchange="renderProjects()">
|
| 1609 |
+
<option value="all">Kõik suurused</option>
|
| 1610 |
+
<option value="small">Kuni 100 m²</option>
|
| 1611 |
+
<option value="medium">101–160 m²</option>
|
| 1612 |
+
<option value="large">Üle 160 m²</option>
|
| 1613 |
+
</select>
|
| 1614 |
+
<select id="catalog-sort" onchange="renderProjects()">
|
| 1615 |
+
<option value="featured">Soovitatud enne</option>
|
| 1616 |
+
<option value="area-asc">Pindala: väiksem enne</option>
|
| 1617 |
+
<option value="area-desc">Pindala: suurem enne</option>
|
| 1618 |
+
<option value="price-asc">Disainihind: väiksem enne</option>
|
| 1619 |
+
</select>
|
| 1620 |
+
</div>
|
| 1621 |
+
<div class="catalog-meta">
|
| 1622 |
+
<span id="catalog-count">6 lahendust</span>
|
| 1623 |
+
<button class="favorite-counter" onclick="toggleFavoritesOnly()"><i class="fas fa-heart"></i> Lemmikud <span id="favorite-count">0</span></button>
|
| 1624 |
+
</div>
|
| 1625 |
+
<div class="project-grid" id="project-grid"></div>
|
| 1626 |
+
</div>
|
| 1627 |
+
</section>
|
| 1628 |
+
|
| 1629 |
+
<!-- ====== MEIST ====== -->
|
| 1630 |
+
<section class="about" id="meist">
|
| 1631 |
+
<div class="container about-grid">
|
| 1632 |
+
<div>
|
| 1633 |
+
<h2>MTM Ehitus OÜ – üks partner kogu projektile</h2>
|
| 1634 |
+
<p style="color: var(--gray-text); margin-top: 12px;">Oleme Eesti projekteerimis- ja ehituspartner, kes ühendab 3D-disaini, tehnilise projekti ning teostuse üheks selgeks tervikuks. Klient võib tellida ühe etapi või kogu lahenduse ideest valmimiseni.</p>
|
| 1635 |
+
|
| 1636 |
+
<ul class="about-list">
|
| 1637 |
+
<li><i class="fas fa-check-circle"></i> <strong>Äriregistri kood:</strong> 16263144</li>
|
| 1638 |
+
<li><i class="fas fa-check-circle"></i> <strong>KMKR:</strong> EE102412910</li>
|
| 1639 |
+
<li><i class="fas fa-check-circle"></i> <strong>Asukoht:</strong> Rävala pst 8, Tallinn</li>
|
| 1640 |
+
<li><i class="fas fa-check-circle"></i> <strong>Juhatuse liikmed:</strong> Marek Telpt ja Külli Kuusaru</li>
|
| 1641 |
+
<li><i class="fas fa-check-circle"></i> <strong>Maksuvõlad:</strong> Puuduvad – puhas ajalugu</li>
|
| 1642 |
+
</ul>
|
| 1643 |
+
</div>
|
| 1644 |
+
<div class="about-box">
|
| 1645 |
+
<i class="fas fa-users-cog"></i>
|
| 1646 |
+
<h3>Pühendunud meeskond</h3>
|
| 1647 |
+
<p style="color: var(--gray-text); margin-top: 10px;">Iga projekt algab läbimõeldud ideest ning liigub kontrollitult disaini, tehnilise lahenduse ja kvaliteetse teostuseni.</p>
|
| 1648 |
+
</div>
|
| 1649 |
+
</div>
|
| 1650 |
+
</section>
|
| 1651 |
+
|
| 1652 |
+
<!-- ====== KONTAKT ====== -->
|
| 1653 |
+
<section class="contact" id="kontakt">
|
| 1654 |
+
<div class="container contact-grid">
|
| 1655 |
+
<div class="contact-info">
|
| 1656 |
+
<h2>Võta meiega ühendust</h2>
|
| 1657 |
+
<p style="opacity: 0.8; margin-top: 10px;">Kirjelda, kas vajad 3D-disaini, ehitusprojekti või täislahendust. Koostame sinu soovile vastava personaalse pakkumise.</p>
|
| 1658 |
+
|
| 1659 |
+
<div class="contact-detail">
|
| 1660 |
+
<i class="fas fa-phone-alt"></i>
|
| 1661 |
+
<div>
|
| 1662 |
+
<strong>Telefon:</strong><br>
|
| 1663 |
+
+372 507 1042
|
| 1664 |
+
</div>
|
| 1665 |
+
</div>
|
| 1666 |
+
|
| 1667 |
+
<div class="contact-detail">
|
| 1668 |
+
<i class="fas fa-envelope"></i>
|
| 1669 |
+
<div>
|
| 1670 |
+
<strong>E-post:</strong><br>
|
| 1671 |
+
info@mtmehitus.ee
|
| 1672 |
+
</div>
|
| 1673 |
+
</div>
|
| 1674 |
+
|
| 1675 |
+
<div class="contact-detail">
|
| 1676 |
+
<i class="fas fa-map-marker-alt"></i>
|
| 1677 |
+
<div>
|
| 1678 |
+
<strong>Aadress:</strong><br>
|
| 1679 |
+
Rävala pst 8, 10143 Tallinn
|
| 1680 |
+
</div>
|
| 1681 |
+
</div>
|
| 1682 |
+
</div>
|
| 1683 |
+
|
| 1684 |
+
<div class="contact-form">
|
| 1685 |
+
<form id="quote-form" onsubmit="handleFormSubmit(event)">
|
| 1686 |
+
<div id="form-status" class="form-status" role="status"></div>
|
| 1687 |
+
<input type="text" id="form-name" placeholder="Teie Nimi *" required>
|
| 1688 |
+
<input type="email" id="form-email" placeholder="Teie E-post *" required>
|
| 1689 |
+
<input type="tel" id="form-phone" placeholder="Telefoninumber">
|
| 1690 |
+
<select id="form-package" required>
|
| 1691 |
+
<option value="">Vali soovitud teenus *</option>
|
| 1692 |
+
<option value="3D-kontseptsioon">3D-kontseptsioon</option>
|
| 1693 |
+
<option value="Ehitusprojekt">Ehitusprojekt</option>
|
| 1694 |
+
<option value="Täislahendus">Täislahendus</option>
|
| 1695 |
+
<option value="Konsultatsioon">Vajan esmalt konsultatsiooni</option>
|
| 1696 |
+
</select>
|
| 1697 |
+
<textarea id="form-message" rows="5" placeholder="Kirjelda soovitud disaini, projekti või täislahendust..." required></textarea>
|
| 1698 |
+
<input type="text" id="form-company" tabindex="-1" autocomplete="off" style="position:absolute; left:-9999px;" aria-hidden="true">
|
| 1699 |
+
<label class="consent">
|
| 1700 |
+
<input type="checkbox" id="form-consent" required>
|
| 1701 |
+
<span>Nõustun, et MTM Ehitus OÜ kasutab sisestatud andmeid sellele päringule vastamiseks. Andmeid ei kasutata automaatselt turunduseks.</span>
|
| 1702 |
+
</label>
|
| 1703 |
+
<button type="submit" class="btn btn-primary" style="width: 100%;"><i class="fas fa-paper-plane"></i> Küsi personaalset pakkumist</button>
|
| 1704 |
+
</form>
|
| 1705 |
+
</div>
|
| 1706 |
+
</div>
|
| 1707 |
+
</section>
|
| 1708 |
+
|
| 1709 |
+
<!-- ====== PROJEKTI DETAILVAADE ====== -->
|
| 1710 |
+
<div class="modal" id="project-modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" onclick="closeModalFromBackdrop(event)">
|
| 1711 |
+
<div class="modal-card">
|
| 1712 |
+
<button class="modal-close" onclick="closeProjectModal()" aria-label="Sulge"><i class="fas fa-times"></i></button>
|
| 1713 |
+
<span class="tag" id="modal-tag">3D-disain</span>
|
| 1714 |
+
<h2 id="modal-title" style="margin-top: 10px;">Projekti nimi</h2>
|
| 1715 |
+
<div class="modal-hero" id="modal-hero"><i class="fas fa-house"></i></div>
|
| 1716 |
+
<p id="modal-description" style="color: var(--gray-text);"></p>
|
| 1717 |
+
<div class="project-specs" id="modal-specs"></div>
|
| 1718 |
+
<h3 style="margin-top: 20px;">Mida saab kohandada?</h3>
|
| 1719 |
+
<p id="modal-customize" style="color: var(--gray-text); margin-top: 8px;"></p>
|
| 1720 |
+
<div class="action-row">
|
| 1721 |
+
<button class="btn btn-primary" id="modal-use-btn"><i class="fas fa-sliders-h"></i> Kasuta lähtepunktina</button>
|
| 1722 |
+
<button class="btn btn-secondary" id="modal-quote-btn"><i class="fas fa-paper-plane"></i> Küsi pakkumist</button>
|
| 1723 |
+
</div>
|
| 1724 |
+
</div>
|
| 1725 |
+
</div>
|
| 1726 |
+
|
| 1727 |
+
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
| 1728 |
+
|
| 1729 |
+
<div class="sticky-project-bar" id="sticky-project-bar">
|
| 1730 |
+
<div>
|
| 1731 |
+
<small>Valitud lähteprojekt</small>
|
| 1732 |
+
<strong id="sticky-project-name">Projekt</strong>
|
| 1733 |
+
</div>
|
| 1734 |
+
<button class="btn btn-primary btn-small" onclick="document.querySelector('#kalkulaator').scrollIntoView({behavior:'smooth'})">Jätka planeerimist</button>
|
| 1735 |
+
</div>
|
| 1736 |
+
|
| 1737 |
+
<!-- ====== JALUS ====== -->
|
| 1738 |
+
<footer>
|
| 1739 |
+
<div class="container">
|
| 1740 |
+
<div style="margin-bottom: 15px;">
|
| 1741 |
+
<a href="#kalkulaator">Lahenduse planeerija</a>
|
| 1742 |
+
<a href="#teenused">Koostöövõimalused</a>
|
| 1743 |
+
<a href="#projektid">Valitud disainid</a>
|
| 1744 |
+
<a href="#meist">Meist</a>
|
| 1745 |
+
<a href="#kontakt">Kontakt</a>
|
| 1746 |
+
</div>
|
| 1747 |
+
<p>© 2026 MTM Ehitus OÜ (Reg. 16263144) – Kõik õigused kaitstud.</p>
|
| 1748 |
+
</div>
|
| 1749 |
+
</footer>
|
| 1750 |
+
|
| 1751 |
+
<!-- LOGIKA JA CALCULATOR JS -->
|
| 1752 |
+
<script type="application/legacy-disabled">
|
| 1753 |
+
// GEMINI API SEADISTUS
|
| 1754 |
+
const apiKey = ""; // API võti käitusaegsest keskkonnast
|
| 1755 |
+
|
| 1756 |
+
// MOBIILI MENÜÜ
|
| 1757 |
+
const menuToggle = document.getElementById('menuToggle');
|
| 1758 |
+
const navMenu = document.getElementById('navMenu');
|
| 1759 |
+
menuToggle.addEventListener('click', () => {
|
| 1760 |
+
navMenu.classList.toggle('open');
|
| 1761 |
+
});
|
| 1762 |
+
|
| 1763 |
+
// KONFIGURAATORI OLEK (STATE)
|
| 1764 |
+
let configState = {
|
| 1765 |
+
area: 120,
|
| 1766 |
+
floors: 1,
|
| 1767 |
+
foundation: 'plate',
|
| 1768 |
+
wall: 'block',
|
| 1769 |
+
roof: 'gable-tin',
|
| 1770 |
+
heating: 'geo',
|
| 1771 |
+
finish: 'turnkey'
|
| 1772 |
+
};
|
| 1773 |
+
|
| 1774 |
+
// VALIKU SEADISTAMINE (NUPUD)
|
| 1775 |
+
function selectOption(type, value, element) {
|
| 1776 |
+
configState[type] = value;
|
| 1777 |
+
const parent = element.parentElement;
|
| 1778 |
+
parent.querySelectorAll('.option-card').forEach(card => card.classList.remove('selected'));
|
| 1779 |
+
element.classList.add('selected');
|
| 1780 |
+
updateCalculator();
|
| 1781 |
+
}
|
| 1782 |
+
|
| 1783 |
+
// TAB MEELEOLU VAHETAMINE
|
| 1784 |
+
function switchTab(tabName) {
|
| 1785 |
+
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
| 1786 |
+
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
| 1787 |
+
|
| 1788 |
+
if(tabName === 'customizer') {
|
| 1789 |
+
document.querySelectorAll('.tab-btn')[0].classList.add('active');
|
| 1790 |
+
document.getElementById('tab-customizer').classList.add('active');
|
| 1791 |
+
} else {
|
| 1792 |
+
document.querySelectorAll('.tab-btn')[1].classList.add('active');
|
| 1793 |
+
document.getElementById('tab-ai-prompt').classList.add('active');
|
| 1794 |
+
}
|
| 1795 |
+
}
|
| 1796 |
+
|
| 1797 |
+
// KULUDE JA MATERJALIDE ARVUTAMINE
|
| 1798 |
+
function updateCalculator() {
|
| 1799 |
+
const area = parseInt(document.getElementById('house-area').value);
|
| 1800 |
+
configState.area = area;
|
| 1801 |
+
document.getElementById('area-val').innerText = area;
|
| 1802 |
+
|
| 1803 |
+
configState.foundation = document.getElementById('foundation-type').value;
|
| 1804 |
+
configState.wall = document.getElementById('wall-type').value;
|
| 1805 |
+
configState.roof = document.getElementById('roof-type').value;
|
| 1806 |
+
configState.heating = document.getElementById('heating-type').value;
|
| 1807 |
+
|
| 1808 |
+
// BAASHIND / M²
|
| 1809 |
+
let baseRate = 1200; // Valge karp base
|
| 1810 |
+
|
| 1811 |
+
// Korruste kordaja
|
| 1812 |
+
if(configState.floors === 1.5) baseRate += 100;
|
| 1813 |
+
if(configState.floors === 2) baseRate += 180;
|
| 1814 |
+
|
| 1815 |
+
// Vundament
|
| 1816 |
+
if(configState.foundation === 'plate') baseRate += 60;
|
| 1817 |
+
if(configState.foundation === 'pile') baseRate += 40;
|
| 1818 |
+
|
| 1819 |
+
// Seinad
|
| 1820 |
+
if(configState.wall === 'clt') baseRate += 220;
|
| 1821 |
+
if(configState.wall === 'wood') baseRate += 50;
|
| 1822 |
+
|
| 1823 |
+
// Küte
|
| 1824 |
+
if(configState.heating === 'geo') baseRate += 80;
|
| 1825 |
+
if(configState.heating === 'air-water') baseRate += 50;
|
| 1826 |
+
|
| 1827 |
+
// Viimistlusaste
|
| 1828 |
+
if(configState.finish === 'shell') baseRate -= 350;
|
| 1829 |
+
if(configState.finish === 'turnkey') baseRate += 250;
|
| 1830 |
+
|
| 1831 |
+
// KOGUSUMMA
|
| 1832 |
+
const totalPrice = area * baseRate;
|
| 1833 |
+
|
| 1834 |
+
// Etappide kulujaotus (protsendina)
|
| 1835 |
+
const foundationCost = Math.round(totalPrice * 0.12);
|
| 1836 |
+
const wallCost = Math.round(totalPrice * 0.30);
|
| 1837 |
+
const roofCost = Math.round(totalPrice * 0.18);
|
| 1838 |
+
const utilityCost = Math.round(totalPrice * 0.18);
|
| 1839 |
+
const finishCost = Math.round(totalPrice * 0.22);
|
| 1840 |
+
|
| 1841 |
+
// EEKUVA DYNAMIC UPDATE
|
| 1842 |
+
document.getElementById('total-price').innerText = totalPrice.toLocaleString('et-EE') + ' €';
|
| 1843 |
+
document.getElementById('price-per-m2').innerText = `~${Math.round(baseRate)} € / m²`;
|
| 1844 |
+
|
| 1845 |
+
document.getElementById('cost-foundation').innerText = foundationCost.toLocaleString('et-EE') + ' €';
|
| 1846 |
+
document.getElementById('cost-walls').innerText = wallCost.toLocaleString('et-EE') + ' €';
|
| 1847 |
+
document.getElementById('cost-roof').innerText = roofCost.toLocaleString('et-EE') + ' €';
|
| 1848 |
+
document.getElementById('cost-utility').innerText = utilityCost.toLocaleString('et-EE') + ' €';
|
| 1849 |
+
document.getElementById('cost-finish').innerText = finishCost.toLocaleString('et-EE') + ' €';
|
| 1850 |
+
|
| 1851 |
+
// MATERJALIKOGUSTE DÜNAAMILINE HINNANG
|
| 1852 |
+
const concreteM3 = Math.round(area * 0.22);
|
| 1853 |
+
const insulationM2 = Math.round(area * 1.6);
|
| 1854 |
+
const roofM2 = Math.round(area * (configState.floors > 1 ? 0.75 : 1.25));
|
| 1855 |
+
|
| 1856 |
+
document.getElementById('mat-concrete').innerText = `~${concreteM3} m³`;
|
| 1857 |
+
document.getElementById('mat-insulation').innerText = `~${insulationM2} m²`;
|
| 1858 |
+
document.getElementById('mat-walls').innerText = `~${area} m² (pindala alusel)`;
|
| 1859 |
+
document.getElementById('mat-roof').innerText = `~${roofM2} m²`;
|
| 1860 |
+
}
|
| 1861 |
+
|
| 1862 |
+
// LEGACY INITIALIZER DISABLED – enhanced app.js initializes the application.
|
| 1863 |
+
|
| 1864 |
+
// RETRY WITH EXPONENTIAL BACKOFF LOGIC
|
| 1865 |
+
async function fetchWithRetry(url, options, retries = 5) {
|
| 1866 |
+
const delays = [1000, 2000, 4000, 8000, 16000];
|
| 1867 |
+
for (let i = 0; i < retries; i++) {
|
| 1868 |
+
try {
|
| 1869 |
+
const response = await fetch(url, options);
|
| 1870 |
+
if (response.ok) return await response.json();
|
| 1871 |
+
} catch (err) {
|
| 1872 |
+
if (i === retries - 1) throw err;
|
| 1873 |
+
}
|
| 1874 |
+
await new Promise(res => setTimeout(res, delays[i]));
|
| 1875 |
+
}
|
| 1876 |
+
throw new Error("Päring ebaõnnestus pärast mitut katset.");
|
| 1877 |
+
}
|
| 1878 |
+
|
| 1879 |
+
// AI PILDI GENEREERIMINE (IMAGEN API)
|
| 1880 |
+
async function generateAiHouseImage() {
|
| 1881 |
+
const loading = document.getElementById('ai-loading');
|
| 1882 |
+
const imgPlaceholder = document.getElementById('ai-img-placeholder');
|
| 1883 |
+
const imgEl = document.getElementById('ai-generated-img');
|
| 1884 |
+
|
| 1885 |
+
loading.style.display = 'block';
|
| 1886 |
+
imgPlaceholder.style.display = 'none';
|
| 1887 |
+
imgEl.style.display = 'none';
|
| 1888 |
+
|
| 1889 |
+
const promptText = `Modern architecturally designed ${configState.area} sqm private home in Estonia, ${configState.floors} storeys, with ${configState.wall} wall texture, modern roof, beautifully landscaped backyard, professional architectural visualization 8k render daylight`;
|
| 1890 |
+
|
| 1891 |
+
try {
|
| 1892 |
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/imagen-4.0-generate-001:predict?key=${apiKey}`;
|
| 1893 |
+
const payload = {
|
| 1894 |
+
instances: [{ prompt: promptText }],
|
| 1895 |
+
parameters: { sampleCount: 1 }
|
| 1896 |
+
};
|
| 1897 |
+
|
| 1898 |
+
const result = await fetchWithRetry(url, {
|
| 1899 |
+
method: 'POST',
|
| 1900 |
+
headers: { 'Content-Type': 'application/json' },
|
| 1901 |
+
body: JSON.stringify(payload)
|
| 1902 |
+
});
|
| 1903 |
+
|
| 1904 |
+
if (result.predictions && result.predictions[0] && result.predictions[0].bytesBase64Encoded) {
|
| 1905 |
+
imgEl.src = `data:image/png;base64,${result.predictions[0].bytesBase64Encoded}`;
|
| 1906 |
+
imgEl.style.display = 'block';
|
| 1907 |
+
} else {
|
| 1908 |
+
alert('Pildi genereerimine ei õnnestunud, proovige uuesti.');
|
| 1909 |
+
imgPlaceholder.style.display = 'block';
|
| 1910 |
+
}
|
| 1911 |
+
} catch (err) {
|
| 1912 |
+
console.error(err);
|
| 1913 |
+
alert('Pildi genereerimisel tekkis viga.');
|
| 1914 |
+
imgPlaceholder.style.display = 'block';
|
| 1915 |
+
} finally {
|
| 1916 |
+
loading.style.display = 'none';
|
| 1917 |
+
}
|
| 1918 |
+
}
|
| 1919 |
+
|
| 1920 |
+
// AI TEKSTI JA PILDI ANALÜÜS (GEMINI 2.5 FLASH)
|
| 1921 |
+
async function analyzeAiPrompt() {
|
| 1922 |
+
const desc = document.getElementById('ai-description').value;
|
| 1923 |
+
const fileInput = document.getElementById('ai-image-upload');
|
| 1924 |
+
const resultBox = document.getElementById('ai-analysis-result');
|
| 1925 |
+
const textOutput = document.getElementById('ai-text-output');
|
| 1926 |
+
|
| 1927 |
+
if (!desc && fileInput.files.length === 0) {
|
| 1928 |
+
alert('Palun sisesta kirjeldus või laadi üles pilt!');
|
| 1929 |
+
return;
|
| 1930 |
+
}
|
| 1931 |
+
|
| 1932 |
+
textOutput.innerText = 'Analüüsin sinu ideed ja koostan MTM Ehituse projektisuunda...';
|
| 1933 |
+
resultBox.style.display = 'block';
|
| 1934 |
+
|
| 1935 |
+
let parts = [{ text: `Oled MTM Ehitus OÜ disaini- ja projektikonsultant. Kasutaja soovib 3D-disaini, ehitusprojekti või täislahendust. Kirjeldus: "${desc}". Anna lühike professionaalne soovitus sobiva teenusemahu, arhitektuurse suuna, peamiste tehniliste valikute ja järgmiste sammude kohta. Ära käsitle seda valmis maja või kinnisvara müügina.` }];
|
| 1936 |
+
|
| 1937 |
+
if (fileInput.files.length > 0) {
|
| 1938 |
+
const file = fileInput.files[0];
|
| 1939 |
+
const base64Data = await new Promise((res) => {
|
| 1940 |
+
const reader = new FileReader();
|
| 1941 |
+
reader.onload = e => res(e.target.result.split(',')[1]);
|
| 1942 |
+
reader.readAsDataURL(file);
|
| 1943 |
+
});
|
| 1944 |
+
parts.push({ inlineData: { mimeType: file.type, data: base64Data } });
|
| 1945 |
+
}
|
| 1946 |
+
|
| 1947 |
+
try {
|
| 1948 |
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`;
|
| 1949 |
+
const payload = { contents: [{ parts: parts }] };
|
| 1950 |
+
|
| 1951 |
+
const result = await fetchWithRetry(url, {
|
| 1952 |
+
method: 'POST',
|
| 1953 |
+
headers: { 'Content-Type': 'application/json' },
|
| 1954 |
+
body: JSON.stringify(payload)
|
| 1955 |
+
});
|
| 1956 |
+
|
| 1957 |
+
const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
|
| 1958 |
+
if (text) {
|
| 1959 |
+
textOutput.innerText = text;
|
| 1960 |
+
} else {
|
| 1961 |
+
textOutput.innerText = 'Analüüsi tegemine ebaõnnestus. Palun proovi uuesti.';
|
| 1962 |
+
}
|
| 1963 |
+
} catch (err) {
|
| 1964 |
+
console.error(err);
|
| 1965 |
+
textOutput.innerText = 'Viga tehisintellektiga ühenduse loomisel.';
|
| 1966 |
+
}
|
| 1967 |
+
}
|
| 1968 |
+
|
| 1969 |
+
// PROJEKTI EDASISAATMINE KONTAKTIVORMIGA
|
| 1970 |
+
function sendProjectToForm() {
|
| 1971 |
+
const price = document.getElementById('total-price').innerText;
|
| 1972 |
+
const details = `PÄRING PROJEKTI PLANEERIJAST:
|
| 1973 |
+
- Kavandatav üldpindala: ${configState.area} m²
|
| 1974 |
+
- Korruseid: ${configState.floors}
|
| 1975 |
+
- Vundament: ${configState.foundation}
|
| 1976 |
+
- Seinad: ${configState.wall}
|
| 1977 |
+
- Katus: ${configState.roof}
|
| 1978 |
+
- Küte: ${configState.heating}
|
| 1979 |
+
- Viimistlusaste: ${configState.finish}
|
| 1980 |
+
- Täislahenduse esialgne lähte-eelarve: ${price}
|
| 1981 |
+
|
| 1982 |
+
Soovin personaalset pakkumist 3D-disainile, ehitusprojektile või täislahendusele.`;
|
| 1983 |
+
|
| 1984 |
+
document.getElementById('form-message').value = details;
|
| 1985 |
+
document.querySelector('#kontakt').scrollIntoView({ behavior: 'smooth' });
|
| 1986 |
+
}
|
| 1987 |
+
|
| 1988 |
+
// VALITUD DISAINI PÄRINGUSSE LISAMINE
|
| 1989 |
+
function chooseDesign(designName) {
|
| 1990 |
+
document.getElementById('form-message').value = `Soovin pakkumist lahendusele „${designName}“.
|
| 1991 |
+
|
| 1992 |
+
Mind huvitab:
|
| 1993 |
+
- 3D-disaini ostmine ja kohandamine
|
| 1994 |
+
- ehitusprojekti koostamine
|
| 1995 |
+
- täislahendus disainist teostuseni
|
| 1996 |
+
|
| 1997 |
+
Palun võtke minuga ühendust, et täpsustada sobiv maht ja hind.`;
|
| 1998 |
+
document.querySelector('#kontakt').scrollIntoView({ behavior: 'smooth' });
|
| 1999 |
+
}
|
| 2000 |
+
|
| 2001 |
+
// TEATED VORMI SAATMISEL
|
| 2002 |
+
function handleFormSubmit(e) {
|
| 2003 |
+
e.preventDefault();
|
| 2004 |
+
alert('Aitäh! Teie päring MTM Ehitus OÜ-le on edukalt saadetud. Võtame teiega ühendust 24h jooksul.');
|
| 2005 |
+
document.getElementById('quote-form').reset();
|
| 2006 |
+
}
|
| 2007 |
+
</script>
|
| 2008 |
+
<script src="app.js"></script>
|
| 2009 |
+
</body>
|
| 2010 |
</html>
|