techprotrade commited on
Commit
49a50d3
·
verified ·
1 Parent(s): 8001bf7

Initial MTMehitus Space

Browse files
Files changed (5) hide show
  1. ANTIGRAVITY_BUILDER_SPEC.md +398 -0
  2. ANTIGRAVITY_HANDOFF.md +96 -0
  3. README.md +48 -4
  4. app.js +611 -0
  5. 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: green
5
- colorTo: blue
6
  sdk: static
 
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>&copy; 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>