window.A3DATA={"lang":"da","t":{"clSym":"SÅDAN SER DET UD UDEFRA","clCheck":"TJEK PÅ 2 MINUTTER","clFix":"HVAD DU SKAL GØRE","clMore":"…og seks punkter mere nedenfor","clSending":"sender…","clErrFields":"Tjek navn og e-mail.","formOk":"✓ modtaget. jeg svarer personligt, som regel samme dag.","formErr":"gik ikke igennem. skriv direkte:","formEmpty":"Udfyld alle tre felter, tak.","formSending":"sender…","bootInit":"initialisering","bootProjects":"monterer /projects","bootGit":"læser git","bootField":"bygger partikel-felt","bootCvr":"kobler CVR til","bootEyes":"tjekker syn","bootEyesV":"øjne aktive","bootDone":"færdig","bootDoneV":"velkommen","bootRecords":"indlæg","bootRepos":"repositories","bootCommits":"commits","bootDots":"punkter","bootSkip":"spring over →","termTitle":"andrii.dk — konsol","termBuilt":"version 3.0 · bygget","termCmds":"kommandoer","termExit":"udgang — Esc","termNoCmd":"kommandoen «%s» findes ikke. prøv","termWho":"iværksætter · IT-praktiker · grundlægger · oplægsholder","termDays":"dage i Danmark","termRepos":"repositories","termPrivate":"private","termCommits":"commits","termProjects":"projekter","termCompanies":"virksomheder","termCommunities":"frivillige foreninger","termOrgs":"frivillige foreninger","termSince":"selvstændig siden","termTop":"top-repositorier efter commits:","termNothing":"fandt ingenting. domæner:","termTotal":"i alt","termDetails":"detaljer:","termCatalog":"katalog:","termNoSuch":"findes ikke. prøv","termStack":"stack:","termProof":"bevis:","termHire":"skriv:","termHire2":"eller udfyld formularen nederst på siden. jeg læser selv, og svarer som regel samme dag.","termHelp":"— hvem jeg er|— projektliste|— hvad jeg arbejder i|— tal|— oplæg-emner|— sådan kommer vi i gang|— kort bio|— åbn projekt|— ryd","timeIn":"København","timeHere":"på pletten","timeAsleep":"sover","seeAll":"alle","restCatalog":"resten af kataloget","moreProjects":"Endnu %s projekter","moreProjectsD":"Danske data-API’er, SaaS, bots, betalinger, scraping, infrastruktur — med filtre efter domæne og stack.","openCatalog":"Åbn katalog →","filterAll":"alle domæner","filterAnyState":"enhver status","filterSearch":"søgning","filterShown":"vist","filterOf":"ud af","legendProd":"● i prod","legendWip":"○ i gang / research","copy":"kopiér","copied":"kopieret","copyManual":"markér manuelt","nowUpdated":"opdateret","nowAgo":"dage siden","nowToday":"i dag","commitsWord":"commits","mailSubject":"Invitation til event","mailBody":"Hej Andrii,\n\nEvent: \nDato: \nBy eller platform: \nPublikum (hvem sidder i salen): \nFormat og varighed: \nEmne af interesse: \n\nAndet: \n","spWho":"til hvem","spLen":"varighed","spLang":"sprog"},"portfolio":{"meta":{"generated":"2026-09-29","source":"GitHub andrey-tut (88 власних репо) + claude-brain/PROJECTS.md + локальні репозиторії","note":"Факти з реальних репо і нотаток, не з назв папок."},"domains":[{"id":"ai","label":"AI og agenter","color":"#7c5cff"},{"id":"data","label":"Data og API’er i Danmark","color":"#00d4a0"},{"id":"saas","label":"SaaS-platforme","color":"#ff5c7c"},{"id":"web","label":"Hjemmesider og WordPress","color":"#ffb020"},{"id":"bots","label":"Bots og messengers","color":"#4cc3ff"},{"id":"fintech","label":"Betalinger og bogholderi","color":"#c0ff3e"},{"id":"scraping","label":"Dataindsamling","color":"#ff8a3d"},{"id":"infra","label":"Infrastruktur","color":"#9aa4b2"},{"id":"community","label":"Fællesskaber og events","color":"#ffd166"},{"id":"business","label":"Forretning (før IT)","color":"#e0e0e0"}],"statuses":{"prod":{"label":"I prod","dot":"#00d4a0"},"building":{"label":"I gang","dot":"#ffb020"},"research":{"label":"Research","dot":"#4cc3ff"},"archive":{"label":"Arkiv","dot":"#6b7280"}},"projects":[{"id":"virtual_pbx","name":"AI-PBX med realtidsstemme","domain":"ai","status":"prod","year":"2026","commits":null,"url":"https://lynbro.dk/da/products/lynbro-pbx/","one":"En telefonrobot, der taler ukrainsk og selv lægger på.","full":"Zadarma → OpenAI Realtime via native SIP. Agenten taler flydende ukrainsk, har adgang til vidensbase, CRM og booking af ydelser, og afslutter selv opkaldet. Plus egen CRM og opkaldsdashboard.","stack":["FastAPI","OpenAI Realtime","SIP","MySQL"],"proof":"Kører i prod · produkt: LynBro PBX"},{"id":"claude-brain","name":"claude-brain — fælles hukommelse","domain":"ai","status":"prod","year":"2026","commits":1822,"one":"Min AI husker alle 100+ projekter mellem sessioner.","full":"Et Git-videnslager om alle projekter: noter, læringer, mønstre, konventioner. FTS5-søgning på ~8 ms, auto-inject af kontekst i hver session via hooks, sync mellem maskiner. 1 822 commits — mit mest aktive repo.","stack":["Shell","SQLite FTS5","Node","Markdown"],"proof":"1 822 commits · daglig brug"},{"id":"danadresse","name":"danadresse.dk — adresse-API til Danmark","domain":"data","status":"prod","year":"2026","commits":409,"url":"https://danadresse.dk","one":"Drop-in erstatning for statens DAWA — med SDK til 4 sprog.","full":"Kompatibel med DAWA REST API til danske adresser. Bootstrap + trinvis synk. Rundt om det — et økosystem: SDK til Python, JS/TS, WordPress-plugin til Woo-checkout og CLI til automatisk migrering af kode fra DAWA.","stack":["FastAPI","PostgreSQL","ETL"],"proof":"Prod + 5 offentlige pakker på GitHub"},{"id":"phone_check","name":"nummeropslag.dk — nummeropslag","domain":"data","status":"prod","year":"2026","commits":496,"url":"https://nummeropslag.dk","one":"Hvem ringer, og er det spam — portal + iOS + Android.","full":"Dansk caller-ID- og spam-portal. Backend på egne DK-data, berigelse via CVR og operatører. iOS-app 1.2.x i App Store, Android — også. Offentligt Partner API + MCP-server til AI-agenter.","stack":["FastAPI","MySQL","Meilisearch","Swift","Kotlin"],"proof":"Prod + iOS og Android i store"},{"id":"soge","name":"Soge.dk — søgning efter biler og virksomheder","domain":"data","status":"building","year":"2026","commits":385,"url":"https://soge.dk","one":"130+ mio. poster: biler (DMR), virksomheder (CVR), adresser.","full":"Søgemaskine til danske offentlige registre: transport fra DMR (100+ GB XML), virksomheder fra CVR, adresser. Offentligt REST API + MCP-server, så AI-agenter kan bruge det. Dokumentation — andrey-tut.github.io/soge-api.","stack":["Python","SQL","XML ETL","MCP"],"proof":"130M+ poster · offentligt API"},{"id":"auctions_history","name":"Historik for bilauktioner i USA","domain":"scraping","status":"building","year":"2026","commits":469,"one":"Copart, IAAI, Manheim — katalog, VIN, fotos, bud, historik.","full":"Efterfølgeren til parsing_vin: indsamling fra Copart, IAAI, Manheim og aggregatorer. Fuld historik på lots pr. VIN, fotos, bud, buy-now. Separat auth-toolkit: 4 login-kanaler, mobile-API logger ind uden mennesker. Det er samme domæne, hvor jeg i 4 år drev en rigtig forretning med eksport af biler.","stack":["Python","curl_cffi","Camoufox","Playwright","residential proxy"],"proof":"469 commits · kortlægning af kilder er afsluttet"},{"id":"renet","name":"renet.dk — netværk på events","domain":"community","status":"building","year":"2026","commits":499,"url":"https://renet.dk","one":"Så du på et event finder de rigtige mennesker, ikke bare visitkort.","full":"Netværksplatform til deltagerne på det samme event: profiler, matching på interesser, beskeder. Mit produkt med næstflest commits.","stack":["Python","Web"],"proof":"499 commits"},{"id":"brella","name":"Brella-outreach til TechBBQ 2026","domain":"community","status":"prod","year":"2026","one":"5 290 beskeder, ~300 svar — på én konference.","full":"Mit eget værktøj til kolde introer og styring af indbakken på Brella-platformen. Kører via ActionCable-websockets. Brugt i drift 27.08.2026 på TechBBQ: 5 290 beskeder, cirka 300 svar.","stack":["Python","httpx","websockets"],"proof":"5 290 beskeder · 27.08.2026"},{"id":"bkg","name":"bkg.dk — multi-tenant booking","domain":"saas","status":"prod","year":"2026","commits":215,"url":"https://bkg.dk","one":"Online booking til servicevirksomheder, flere saloner på samme database.","full":"Booking-platform til servicevirksomheder: behandlere, ydelser, kalender, påmindelser. Multi-tenant arkitektur.","stack":["FastAPI","MySQL"],"proof":"215 commits · prod"},{"id":"lynbro","name":"lynbro.dk — sitet for mit eget IT-firma","domain":"web","status":"prod","year":"2026","commits":27,"url":"https://lynbro.dk","one":"366 sider på tre sprog + AI-chat.","full":"Site for LynBro ApS: 366 sider på dansk, engelsk og ukrainsk, blog, cases, AI-chat på n8n. Navn: Lyn (lyn) + Bro (bro).","stack":["Node","n8n","i18n"],"proof":"Prod · 366 sider · da/en/uk"},{"id":"ferrysport","name":"ferrysport.dk — genoplivning af webshoppen","domain":"web","status":"prod","year":"2026","commits":110,"url":"https://ferrysport.dk","one":"56% af produktvarianterne var døde. 69% — uden beskrivelse. Fik det fikset.","full":"Dansk webshop med arbejdstøj. Oversatte et papir-audit på 30 sider (da/en/uk) til noget brugbart og lagde en plan i 8 trin. Lavede: spam-registreringer 92→16, tynde sider 67→15, logo 65→41 KB, Google-skrifttyper helt væk (7→0 henvendelser), nyhedsbrev virker endelig. Kunden ser fremskridt i sit eget panel.","stack":["WordPress","WooCommerce","Python"],"proof":"Levende kundeprojekt siden september 2026"},{"id":"lynbro-projektportal","name":"Portal «kunde ↔ udfører»","domain":"saas","status":"prod","year":"2026","one":"Kunden klikker på et sted på sit site og skriver, hvad der er galt.","full":"Eget produkt: fremdrifts-portal inde i kundens site. Kommentar med valg af område direkte på siden, tidsregistrering, forklaring af «hvorfor» i kundens sprog, sammenligning «før/efter». v1.0 i kamp på ferrysport.dk.","stack":["Python","JS"],"proof":"v1.0 i kamp på en rigtig kunde"},{"id":"plugins_wp","name":"plugins.lynbro.dk — WP-plugin-studie","domain":"web","status":"prod","year":"2026","commits":266,"url":"https://plugins.lynbro.dk","one":"Egne WordPress-plugins til det danske marked.","full":"Portal og kildekode til plugins: søgning i danske registre direkte i WP, gratis flersproget GDPR/ePrivacy cookie-banner, adresse-autofuldførelse til WooCommerce. Oversættelser kører via translate.wordpress.org.","stack":["PHP","WordPress","FastAPI"],"proof":"3 offentlige plugins · oversættelser i WP.org"},{"id":"test_mobilepay","name":"MobilePay / Vipps — betalings-harness","domain":"fintech","status":"prod","year":"2026","one":"Gik API’et i Danmarks vigtigste betalingssystem helt til bunds.","full":"Research-harness til Vipps/MobilePay: egne og kundemoduler, e2e-kørsel 09.06.2026, FastAPI + PHP-bro, tjek af HMAC-webhooks. Recurring og Report venter på prod. Resultat — en færdig snydeguide til API’et.","stack":["FastAPI","PHP","HMAC webhooks"],"proof":"e2e-kørsel gennemført"},{"id":"skat_moms","name":"Moms API — momsindberetninger i Skat","domain":"fintech","status":"building","year":"2026","commits":6,"one":"SOAP-klient til den danske skat. WS-Security fra bunden på Python.","full":"NemVirksomhed / RSU B2B: indberetning af moms til Skattestyrelsen. Skrev WS-Security fra bunden, fordi der ikke fandtes noget færdigt til Python. Test er live, prod venter på OCES3-certifikat.","stack":["Python","lxml","WS-Security","SOAP"],"proof":"Testmiljø — live"},{"id":"regnskabhub","name":"RegnskabHub — AI-bogholder","domain":"fintech","status":"building","year":"2026","one":"Ét API oven på alle danske bogføringssystemer.","full":"Et samlet API til danske bogføringssystemer plus AI-bogholder: multi-tenant, planer og måling, 3 read-adaptere, sync-cache, detektion af afvigelser, berigelse via CVR. 54 tests, 20+ endpoints.","stack":["FastAPI","PostgreSQL","LLM"],"proof":"54 tests · 20+ endpoints"},{"id":"dinero","name":"Integration af Dinero (Visma)","domain":"fintech","status":"building","year":"2026","commits":5,"one":"148 endpoints i dansk bogføring er mappet.","full":"Offentlig Dinero-integration til LynBro: OAuth + personlig godkendelse, 148 endpoints mappet, e2e-læsning på en live Pro-organisation. Udbyder-agnostisk lag, så e-conomic kan komme ved siden af.","stack":["Python","OAuth"],"proof":"148 endpoints · e2e på live org."},{"id":"rykker","name":"rykkerbot.dk — betalingspåmindelser","domain":"fintech","status":"research","year":"2026","one":"Automatiske rykker'e efter dansk renteloven.","full":"Rykker-as-a-Service: automatisering af betalingspåmindelser efter dansk lov. Segmenterede satser, gebyrer efter §9b, tilstandsmaskine, eskalering til inkasso. Dansk landingpage med live beregner.","stack":["FastAPI","Python 3.12"],"proof":"Kerne + 37 tests"},{"id":"jakun","name":"JaKun — CRM fra metadata","domain":"saas","status":"building","year":"2026","commits":55,"one":"DB-skema, API og rettigheder bliver skabt ud fra metadata.","full":"CRM-platform: i stedet for at skrive tabeller og endpoints i hånden — beskriver du metadata, og så bliver DDL, API og rettighedsmatrix genereret. Multi-tenant SaaS + self-host i Docker. Der er en MCP-server, så en AI-agent kan arbejde direkte med CRM. 100 Python-filer.","stack":["FastAPI","PostgreSQL","React/TS","MCP"],"proof":"100 kernefiler · MCP-server"},{"id":"jadel","name":"JaDel — krydsposting til sociale medier","domain":"saas","status":"research","year":"2026","commits":29,"one":"Som Postiz/Mixpost. Først — fuld reverse af markedet.","full":"Planlægning, AI-generering og udgivelse til sociale medier. Før koden — gennemgang af Postiz (AGPL, 34.5k stjerner) og Mixpost, API-matrix til 20+ netværk, smertepunkter fra issues og anmeldelser. Hovedkonklusion: fordelen er ikke i koden, men i de gennemførte platform-audits.","stack":["FastAPI","TypeScript"],"proof":"Fuld markedsresearch 08.2026"},{"id":"imin","name":"imin.dk — site builder","domain":"saas","status":"building","year":"2026","commits":148,"url":"https://imin.dk","one":"Visuel editor, domæner, add-on-moduler.","full":"Multi-tenant SaaS-site builder: visuel editor, tilkobling af domæner, add-on-moduler. Idéen om en plugin-arkitektur, som jeg senere tog med i andre produkter.","stack":["FastAPI","MySQL","Stripe"],"proof":"148 commits"},{"id":"gyk","name":"gyk.dk — linkforkorter","domain":"saas","status":"building","year":"2026","commits":153,"url":"https://gyk.dk","one":"Korte links + QR + klikstatistik.","full":"Din egen bit.ly: korte links, QR-generering, klik-analyse.","stack":["FastAPI","MariaDB"],"proof":"153 commits"},{"id":"ommig","name":"ommig.dk — bio-sider","domain":"saas","status":"prod","year":"2026","commits":146,"url":"https://ommig.dk","one":"Linktree til Danmark, med respekt for privatliv.","full":"Personlige bio-sider på ommig.dk/username. Fokus på privatliv — ingen tredjeparts-trackere.","stack":["FastAPI async"],"proof":"146 commits · i drift"},{"id":"srvc","name":"srvc.dk — markedsplads for ydelser","domain":"saas","status":"building","year":"2026","commits":36,"one":"Escrow, tvister, udbetalinger, kontrakter.","full":"Service-markedsplads med fuld økonomisk cyklus: deponering af penge, tvistmekanisme, udbetaling til udførere, kontrakter.","stack":["FastAPI","Alembic"],"proof":"36 commits"},{"id":"jajob","name":"jajob.dk — AI-first jobplatform","domain":"saas","status":"building","year":"2026","commits":59,"one":"Stillinger, CV’er, ATS og matchning — med flersproget vektorsøgning.","full":"Jobplatform til Danmark: stillinger, CV’er, ATS, kandidatmatchning. Søgning — vektorbaseret og flersproget, fordi DK-markedet er tresproget.","stack":["FastAPI","MySQL","vector search"],"proof":"59 commits"},{"id":"jatid","name":"jatid.dk — tidsregistrering","domain":"saas","status":"building","year":"2026","url":"https://jatid.dk","one":"Modulær tidsstyring til erhverv.","full":"Platform til tidsregistrering: vagter, projekter, rapporter. Modulær arkitektur til forskellige typer virksomheder.","stack":["FastAPI","Alembic"],"proof":"Udvikling"},{"id":"games","name":"Party Game OS","domain":"saas","status":"building","year":"2026","commits":55,"one":"Jackbox på min måde: tv’et — skærmen, telefonerne — joysticks.","full":"Platform til festspil: fælles skærm på TV, gæsternes telefoner som controllere. 8 spil, speak via Piper TTS.","stack":["FastAPI","vanilla JS","SQLite","Piper TTS"],"proof":"8 spil · 55 commits"},{"id":"video_editor","name":"AI-video-pipeline uden betalte SaaS","domain":"ai","status":"building","year":"2026","one":"Hele videoværkstedet lokalt på Apple Silicon.","full":"Undersøgte 5 open-source-klynger: auto-klipning, reframe til vertikal, undertekster, stemmekloning, dubbing, avatar/generering. Orkestrator — Claude Code med skills og MCP. Pipeline A v0.1 er samlet.","stack":["ffmpeg","Whisper","MLX","Remotion"],"proof":"5-klynge-research + fungerende pipeline"},{"id":"localmind","name":"LocalMind — LLM-chat på iPhone","domain":"ai","status":"building","year":"2026","one":"Modellen kører på telefonen, ikke i andres sky.","full":"iOS-app til lokale sprogmodeller. Native Swift.","stack":["Swift","SwiftUI"],"proof":"Udvikling"},{"id":"cbot","name":"cbot.dk — samtalebaseret lead-gen","domain":"bots","status":"building","year":"2026","commits":57,"url":"https://cbot.dk","one":"Botten fanger et lead og lægger det direkte i CRM.","full":"Conversational lead-gen: botten tager samtalen, kvalificerer, sender videre til CRM. Web, backend og mobil-del.","stack":["Python","JS","mobile"],"proof":"57 commits"},{"id":"whatsapp_privat","name":"WhatsApp → kanonisk base → AI","domain":"bots","status":"building","year":"2026","commits":30,"one":"År med beskeder lavet om til en base, som AI taler med.","full":"Samling af egne WhatsApp-konti i én kanonisk base med efterfølgende AI-adgang. Arkitekturen er SaaS-ready fra start.","stack":["FastAPI","WAHA"],"proof":"30 commits"},{"id":"uaeuhub_bots","name":"Bots til ukrainske fællesskaber","domain":"bots","status":"prod","year":"2023–2026","one":"Moderation, udsendelser og anti-spam til grupper på tusindvis af mennesker.","full":"En familie af Telegram-bots til ukrainske fællesskaber i Europa: moderation, antispam, udsendelser, AI-svar. Fem iterationer af én bot — helt ærligt, de skal samles til én.","stack":["Python","aiogram","OpenAI"],"proof":"Levende grupper · ~300 brugere i Mini App"},{"id":"padel","name":"Spil-opstilling til Padel Copenhagen","domain":"bots","status":"prod","year":"2026","one":"Afstemning i gruppen → færdig roster og par.","full":"Læser en afstemning i Telegram-gruppen for padel-fællesskabet i Copenhagen og laver roster og opstilling på baner.","stack":["Python","Telethon"],"proof":"Bruges af fællesskabet"},{"id":"sms_gateway","name":"SMS-gateway","domain":"infra","status":"prod","year":"2026","commits":164,"one":"Fælles notifikationskanal til alle mine produkter.","full":"Egen SMS-gateway, som andre projekter bruger som notifikationsservice.","stack":["Python"],"proof":"164 commits · additive-prod"},{"id":"aapanel","name":"Styring af aaPanel-servere","domain":"infra","status":"prod","year":"2026","commits":147,"one":"Mine VPS styres med kode, ikke med klik i panelet.","full":"Automatisering af aaPanel: sites, BD, SSL, deploy. Separat fik jeg løst den smertefulde historie med mailserveren — SSL via DNS-01 i Simply.com og Postfix SNI.","stack":["Python","nginx","MySQL"],"proof":"147 commits · alle mine servere"},{"id":"vps_monitor","name":"Claude som vagthavende SRE","domain":"infra","status":"research","year":"2026","one":"AI holder øje med serverne og fikser selv de småting.","full":"Overvågning af VPS og projekter med rapporter i Telegram og på mail, automatiske sikre handlinger plus forespørgsel om bekræftelse ved risikable.","stack":["Claude Code","Python"],"proof":"Starter-kit"},{"id":"push_projects","name":"Motor til projekt-promovering","domain":"infra","status":"building","year":"2026","one":"Automatisk indsendelse af mine produkter til kataloger og fora.","full":"Promoveringssystem: kataloger, API-kataloger, GitHub, fora, email. Første bølge — danadresse, phone_check, soge.","stack":["Python"],"proof":"Bølge 1 er startet"},{"id":"ideamine","name":"ideamine — radar for idéer og smerter","domain":"ai","status":"building","year":"2026","one":"24 kilder med brugerklager → min daglige digest.","full":"Radar for produktsignaler: HN, feature-requests hos konkurrenter på GitHub, 1–3★ anmeldelser i App Store DK, anmeldelser af WP-plugins, RSS, danske medier. Deduplikering, scoring til min profil, ukrainsk digest og matching af signaler til de nuværende projekter.","stack":["Python","LLM"],"proof":"24 zero-auth kilder"},{"id":"freelancehunt","name":"Omgåelse af Cloudflare for freelancehunt","domain":"scraping","status":"prod","year":"2026","one":"API hvor det findes. Browser hvor API ikke findes.","full":"Autologin og læsning af konto: API v2 som hovedkanal, nodriver-browser til det, der ikke er i API — inkl. omgåelse af Cloudflare.","stack":["Python","httpx","nodriver"],"proof":"Virker"},{"id":"virkdata","name":"Scrape af danske virksomhedsdata","domain":"scraping","status":"building","year":"2026","one":"CVR og virk — ind i egen database.","full":"Indsamling af danske virksomhedsdata fra virk/CVR via proxy-rotation. Føder berigelse i andre produkter.","stack":["Python","proxy"],"proof":"Føder andre projekter"},{"id":"sifjakobs","name":"Scraper af Shopify-katalog","domain":"scraping","status":"prod","year":"2026","one":"Katalog + AI-fallback, når layoutet ændrer sig.","full":"Scraper af et komplet Shopify-katalog med proxy-rotation og AI-fallback, hvis layoutet ændrer sig.","stack":["Python","uv"],"proof":"Virker"},{"id":"offline_cph","name":"Research af offline-nicher i København","domain":"business","status":"research","year":"2026","one":"Hvor du kan investere 150 000 DKK i en rigtig forretning.","full":"Systematisk gennemgang af offline-forretningsnicher i København med budget 150k DKK. Top-kandidater: skin-studio, keramik, cykelværksted.","stack":["Research"],"proof":"Komplet RESEARCH.md"},{"id":"make_money","name":"System til at få ordre i hus","domain":"business","status":"research","year":"2026","commits":33,"one":"11 agenter har gennemgået det danske freelance-marked.","full":"Research af ordrekilder i Danmark: mæglere tager 1400–1750 DKK/time, ApS — nøglen til erhvervskunder, budplatforme er døde, email-aggregering — en lovlig kanal.","stack":["Research","FastAPI"],"proof":"Research fra 11 agenter"},{"id":"mitid","name":"Research af MitID-mæglere","domain":"data","status":"research","year":"2026","one":"Hvordan du lovligt logger danskere ind via statens eID.","full":"Gennemgang af MitID-mæglere til login via dansk eID. Anbefaling: Criipto/Idura — €67/måned + €0.037 pr. autentificering, OIDC, sandbox uden kort.","stack":["OIDC","Research"],"proof":"Beslutning taget"},{"id":"carsexport","name":"Cars Export LLC","domain":"business","status":"archive","year":"2018–2022","one":"Eksport af biler fra USA og Kina. ×10 på tre år.","full":"CEO. Indkøb af biler på Copart, IAAI, Manheim, ADESA, sølogistik, brokerarbejde, told. Ruter UA/GE/USA/EU/CN. Vækst på over 1000% på tre år, millionomsætning pr. måned. Det var her, jeg lærte, hvordan kontrakter, risiko og penge faktisk fungerer.","stack":["Logistik","Told","International handel"],"proof":"4 år · +1000% på 3 år"},{"id":"lintek","name":"Lintek","domain":"business","status":"archive","year":"2008–2012","one":"CEO som 25-årig. Kabelprodukter og internet-distribution, Kharkiv.","full":"Kabelprodukter, internet-branchen, distribution. Første erfaring med at drive en virksomhed.","stack":["Distribution","Salg"],"proof":"4 år"},{"id":"ltv","name":"LTV — internationale indkøb","domain":"business","status":"archive","year":"2013–2017","one":"Cykler og reservedele. Indkøb gennem en halv verden.","full":"Indkøbschef til udenrigshandel: cykler og reservedele, international handel, leverandører fra Asien og Europa.","stack":["Udenrigshandel","Indkøb"],"proof":"5 år"},{"id":"bestchoice","name":"Best Choice","domain":"business","status":"prod","year":"2022–","url":"https://best-choice.dk","one":"Her startede min IT-praksis i Danmark. CVR 44079844.","full":"IT-ydelser: automatisering, AI-værktøjer, CRM, Telegram-bots, SEO, hosting. Lead Consultant & Solution Architect.","stack":["Rådgivning","Udvikling","CVR 44079844"],"proof":"Aktiv praksis siden 2022"},{"id":"lynbro_aps","name":"LynBro ApS","domain":"business","status":"prod","year":"2025–","url":"https://lynbro.dk","one":"Dansk IT-virksomhed. CVR 46321499.","full":"Lyn — lyn, fart. Bro — bro. Hjemmesider, AI-agenter, automatisering. Registreret i Rødovre.","stack":["ApS","CVR 46321499"],"proof":"Aktivt selskab"},{"id":"aktive","name":"Aktive Ukrainere","domain":"community","status":"prod","year":"2023–","url":"https://aktive-ukrainere.dk","one":"Frivillig forening for ukrainere i Danmark. CVR 43922939.","full":"Stifter og arrangør af foreningen: hjælp, frivilligt arbejde, møder, AI-workshops og arrangementer for ukrainere i Danmark.","stack":["Frivillig forening","CVR 43922939"],"proof":"Regelmæssige events siden 2023"},{"id":"uahub","name":"UaHub","domain":"community","status":"prod","year":"2025–","url":"https://uahub.dk","one":"Forening for ukrainske iværksættere og virksomheder (frivillig forening). CVR 45599841.","full":"Stifter af foreningen. Workshops, fora, networking og et katalog over ukrainske virksomheder på uahub.dk/katalog.","stack":["Frivillig forening","CVR 45599841"],"proof":"Aktiv forening · virksomhedskatalog"},{"id":"oksana","name":"oksana.dk — hjemmeside for en PMU-studie","domain":"web","status":"building","year":"2026","commits":39,"url":"https://oksana.dk","one":"Redesign af en skønhedsstudies hjemmeside.","full":"Fuld redesign af permanent makeup-studiets hjemmeside: WordPress + Node-del.","stack":["WordPress","Node","PHP"],"proof":"39 commits"},{"id":"msdf","name":"msdf.dk — møbelmontering","domain":"web","status":"building","year":"2026","url":"https://msdf.dk","one":"Blive nr. 1 på møbelmontering i København.","full":"Service til samling og montering af møbler i Storkøbenhavn. SEO + leadgen, gennemsigtig fast pris med beregner, programmatisk SEO «møbler × lokation», scraping af kataloger fra JYSK og IKEA.","stack":["WordPress","pSEO"],"proof":"I gang"},{"id":"nailshub","name":"nailshub.dk — automatisering af salon","domain":"web","status":"prod","year":"2026","commits":17,"url":"https://nailshub.dk","one":"Salon, der ikke længere svarer manuelt på bookinger.","full":"Support og automatisering af en salon på WordPress + Amelia: online booking, bekræftelse via WhatsApp Cloud API, autorapporter med AI-audit, migrering fra det gamle system.","stack":["WordPress","Amelia","n8n","WhatsApp API"],"proof":"Prod · live salon"},{"id":"zhanna","name":"zhanna.dk — site for en negleartist og coach","domain":"web","status":"prod","year":"2026","url":"https://zhanna.dk","one":"Personligt brand for en negleartist: fem sprog, booking via NailsHub.","full":"Kundesite fra Best Choice til Zhanna Osaulenko — negleartist, coach og medejer af NailsHub. Fem sprog (UK/EN/DA/PL/RU), lyst og mørkt tema, booking går til NailsHub-salonen.","stack":["Web","i18n"],"proof":"Levende kundesite"},{"id":"bilafgift","name":"Beregner til registreringsafgift på bil","domain":"data","status":"building","year":"2026","one":"Hvad koster det at hente en bil til Danmark. PWA uden afhængigheder.","full":"Beregner til dansk registreringsafgift. Ren HTML/CSS/JS, virker offline.","stack":["vanilla JS","PWA"],"proof":"Virker"},{"id":"speed_cam","name":"Fart-radar fra video","domain":"ai","status":"research","year":"2026","one":"Telefonen måler bilens hastighed ud fra video.","full":"Måling af objekters hastighed fra video: YOLO + tracking + nummerpladegenkendelse, skalaen beregnes ud fra nummerpladen eller akselafstand. Rapport-«bevis», rankings, deling. Positionering — et universelt værktøj, ikke kun til kontrol.","stack":["YOLO","iOS/Android","FastAPI","GPU"],"proof":"Undersøgelse afsluttet"},{"id":"runners","name":"Løbeapp med erobring af territorier","domain":"saas","status":"research","year":"2026","one":"Du løber — du erobrer bydele.","full":"Gamificeret løbeapp: territorieerobring, liga, web-del. Startmarked — København og Skandinavien.","stack":["greenfield"],"proof":"Koncept"},{"id":"looi","name":"LOOI Robot BLE toolkit","domain":"ai","status":"prod","year":"2025","commits":4,"url":"https://github.com/andrey-tut/LOOI-Robot","one":"Jeg har gennemgået robotens BLE-protokol. 12 stjerner på GitHub.","full":"Uofficiel toolkit til LOOI-robotten: styring fra tastatur, dekodning af sensorer, telemetri, roadmap for en Flutter-companion. Offentlig, med rigtige stjerner fra fremmede.","stack":["Python","BLE","reverse engineering"],"proof":"12 stjerner · offentligt repo"},{"id":"azbyg","name":"Arkiv af korrespondance som bevis for arbejdet","domain":"business","status":"building","year":"2026","url":"https://azbyg.dk","one":"Arbejdschat i Telegram → juridisk bevis på udført arbejde.","full":"Arkiv af arbejdschat til kunden (objekt «Otell Hornbek», AZ Byg ApS): Telegram-lignende web-viewer, PDF, oversættelse DA/EN/UA, dagsoversigt med holdenes timer.","stack":["Python","Web","PDF"],"proof":"Vieweren er klar"},{"id":"hub_pools","name":"Automatisering af TEUH","domain":"community","status":"prod","year":"2026","one":"Tilmelding, spørgeskema og materialer — uden manuelt arbejde.","full":"Automatisering af TEUH-platformen: tilmelding af deltagere, spørgeskemaer, uddeling af materialer fra Google Drive.","stack":["Playwright","httpx"],"proof":"Automatisering i arbejdet"},{"id":"job_search","name":"Systematisk jobsøgning som en ingeniøropgave","domain":"business","status":"prod","year":"2026","one":"Ikke en bunke CV’er, men en styret pipeline.","full":"Jobsøgning i Hvidovre/Rødovre gjort til et system: tracker i et regneark, automatiske kladder i Gmail, kilder fra CVR. En ikke-IT opgave løst med en IT-metode.","stack":["Python","Gmail API","CVR"],"proof":"Virker"}]},"stats":{"asOf":"2026-09-14","repos":88,"reposPrivate":75,"commits":6119,"projects":62,"companies":2,"communities":2,"inDenmarkSince":"2022-04-01","businessSince":1996,"topRepos":[["claude-brain",1822],["renet.dk",499],["phone_check",496],["auctions_history",469],["danadresse",409],["soge",385]]},"faq":[{"q":"Hvad laver du, helt kort?","a":"Jeg bygger systemer til forretninger: hjemmesider, automatisering, AI-agenter, CRM, API og datasystemer. Plus rådgiver jeg om drift og synlighed online. Jeg gør det gennem mit eget IT-selskab LynBro ApS og min praksis Best Choice."},{"q":"Er du udvikler eller iværksætter?","a":"Begge, og i den rækkefølge: først tyve år i forretning, så kode. Det er hovedforskellen. Jeg spørger ikke «hvilken feature skal vi lave» — jeg spørger, hvor din margin lækker, og om det overhovedet er værd at automatisere. Ofte er det bedste svar — at bygge ingenting."},{"q":"Hvad koster det at få lavet en hjemmeside eller en automatisering?","a":"Ærligt: før vi har talt sammen — ved jeg det ikke. Spændet er alt for stort: en landingpage på en uge og en multi-tenant platform på et halvt år — det er vidt forskellige størrelsesordener. Første snak er gratis, og bagefter får du enten et prisinterval med begrundelse eller et råd om ikke at gøre det."},{"q":"Arbejder du kun i Danmark?","a":"Jeg bor i Danmark og arbejder fra hvor som helst. Det danske marked kender jeg bedre end de fleste: CVR, MitID, MobilePay, DAWA, Skat, e-conomic og Dinero — det har jeg været igennem før og har færdige integrationer."},{"q":"Hvilke sprog taler du?","a":"Ukrainsk og russisk flydende, engelsk på arbejdsniveau, dansk — undervejs; jeg forstår mere, end jeg taler. Jeg kan holde oplæg på ukrainsk eller engelsk."},{"q":"Har du virkelig lavet 62 projekter?","a":"Dokumenterede — 62, og det er kun det, jeg selv mener er værd at vise. Repositories 88, commits 6.119. Noget kører i prod og tjener penge, noget er undervejs, og noget — ærligt talt — er research, der ikke kom videre. Det viser jeg også: kom det ikke videre — er det også et resultat."},{"q":"Hvordan ser et samarbejde med dig ud?","a":"Først en snak, hvor det mest er dig, der taler. Så en kort research: hvad findes der allerede på markedet, og hvad behøver man ikke at kode. Så den korteste vej til at teste idéen, ikke en flot mockup. Så — i prod, med logs og overvågning. Du ser fremskridt i dit eget dashboard, ikke i chatten."},{"q":"Tager du små opgaver?","a":"Ja, hvis de faktisk er små. Fikse en formular, sætte en Google-profil op, koble online booking på — det er helt fint. Værre er det, når en “lille opgave” i virkeligheden er stor, men budgettet er til en lille — det siger jeg med det samme."},{"q":"Hvad skal der til for at invitere dig til et oplæg?","a":"Dato, by eller online, publikum og hvor meget tid jeg har. Jeg holder jævnligt oplæg i Copenhagen og online. Formater: oplæg 20–45 minutter, panel, workshop på 2 timer med laptops. Honorar aftales; for ukrainske fællesskaber og frivillige events — gratis."},{"q":"Hvorfor ser sitet sådan ud?","a":"Fordi det også er en del af porteføljen. Det tredimensionelle felt i baggrunden er 6.119 partikler, én for hver commit. Broen bygges af frames fra en genereret video, som kører med scroll. Øjnene på portrættet følger markøren. Alt er skrevet fra bunden, uden site builders og uden animationsbiblioteker. Tryk “/” — der er også en konsol."}],"speaking":{"formats":[{"t":"Oplæg","m":"20–45 min","d":"Klassisk slot på en konference eller et forum. Cases, tal, konklusioner. Jeg lader 10 minutter være til spørgsmål — som regel er der flere."},{"t":"Workshop","m":"2–3 timer","d":"Med laptops. Hver deltager går ikke hjem med noter, men med noget der virker: en Google-profil sat op, en færdig bot eller den første automatisering."},{"t":"Panel / rundbord","m":"efter programmet","d":"Moderator eller deltager. Jeg fungerer godt dér, hvor man skal tale konkret, ikke i luftige vendinger."},{"t":"Online","m":"valgfri","d":"Zoom, Teams, Meet, stream. Jeg har en ordentlig mikrofon, lys og en backup-forbindelse — der kommer ingen tekniske overraskelser."}],"topics":[{"t":"Kunden leder allerede efter dig. Hvorfor vælger de ikke dig?","d":"Den mest populære talk. Jeg gennemgår 12 punkter, hvor en lille virksomhed mister kunden allerede før første kontakt: Google-profil, anmeldelser, sidehastighed, hvor tydeligt tilbuddet er, kontaktmulighed, svar uden for åbningstid. Ikke teori — på levende eksempler fra danske og ukrainske virksomheder.","take":["Tjekliste med 12 punkter, som du kan komme igennem samme aften","Hvad koster 0 kr og giver effekt allerede i denne uge","Sådan måler du, om det faktisk er blevet bedre"],"aud":"Små virksomheder, selvstændige, eksperter, begyndere","len":"30–45 min · eller workshop 2 timer","lang":"UK / EN"},{"t":"AI uden magi: hvad virker reelt i en lille virksomhed","d":"Uden «AI ændrer alt». Helt konkret: hvilke opgaver automation løser i dag, hvad det koster pr. måned, hvor AI er dyrere end et menneske, og hvilke fejl jeg selv har lavet. Jeg viser mine egne systemer — en telefonagent, der taler ukrainsk, og en vidensbase, der husker 100 projekter.","take":["Liste over opgaver, der giver mest mening at automatisere først","Reelle tal på prisen, ikke marketing","Tre måder at tabe penge på AI — og hvordan du undgår dem"],"aud":"Virksomhedsejere, ledere, IT-fællesskaber","len":"30–45 min","lang":"UK / EN"},{"t":"Starte virksomhed i Danmark, når du ikke kender nogen her","d":"Min vej siden 2022 uden filter: CVR og ApS, skat, NemKonto, MitID, de første kunder, hvorfor dansk networking virker anderledes, og hvad jeg ville have gjort anderledes. Et emne, folk beder om oftest efter oplæg for et ukrainsk publikum.","take":["Rækkefølgen af skridt de første tre måneder","Hvad ApS reelt koster, og hvornår du har brug for den","Hvor du finder de første kunder, hvis du har 0 kontakter"],"aud":"Ukrainere i Danmark, relokanter, nybegyndere","len":"30–60 min","lang":"UK"},{"t":"Fra eksport af biler til statslige API","d":"En historie om, hvordan erfaring fra «tung» offline-forretning — told, kontrakter, søfragt — bliver en fordel i det digitale. Hvorfor en person, der har kørt biler over oceanet, designer systemer anderledes. Til dig, der skifter branche og tror, at erfaringen er tabt.","take":["Hvad fra offline-erfaring kan flyttes direkte over i IT 1 til 1","Sådan undgår du at starte din karriere fra 0 som 40-årig","Hvorfor brancheviden er mere værd end et programmeringssprog"],"aud":"Konferencer, fællesskaber, karriere-events","len":"20–40 min","lang":"UK / EN"},{"t":"Workshop: din AI-assistent på 2 timer","d":"Praksis. Hver deltager bygger på to timer sin egen hjælper til sin opgave — svar til kunder, behandling af henvendelser, klargøring af indhold. Vi arbejder i gratis værktøjer, så der ikke går noget i stykker efter workshoppen pga. abonnement.","take":["En fungerende hjælper på dine egne data","Forståelse for, hvor grænsen går for mulighederne","Hvad gør du så på egen hånd"],"aud":"Op til 25 personer med bærbare","len":"2–3 timer","lang":"UK / EN"}],"past":[{"y":"2026","e":"Woman's Day Copenhagen","d":"Beauty Business-blok — små virksomheders tilstedeværelse online"},{"y":"2026","e":"TechBBQ","d":"Netværkskampagne: 5.290 beskeder, ~300 samtaler"},{"y":"2026","e":"Business og skat i Danmark","d":"Rundbordssamtale for ukrainske iværksættere"},{"y":"2025","e":"EU Funding & Mentorship","d":"Zoku Copenhagen"},{"y":"2025","e":"Blockchain Impact Forum","d":"København"},{"y":"2023–","e":"AI-workshops i Aktive Ukrainere","d":"Regelmæssige, praktiske møder, som jeg selv arrangerer"}],"bioShort":"Andrii Lynnyk — ukrainsk iværksætter og IT-praktiker i Danmark. Stifter af IT-virksomheden LynBro ApS og af to frivillige foreninger — Aktive Ukrainere og UaHub. I business siden 1996: fra kabeldistribution og eksport af biler fra amerikanske auktioner til AI-agenter og offentlige data-API’er. 62 dokumenterede projekter, 88 repositories.","bioOneLine":"Andrii Lynnyk — iværksætter, IT-praktiker og stifter af LynBro ApS (Danmark). Bygger systemer, der fungerer uden ham."},"now":{"updated":"2026-09-14","intro":"Denne side handler — om det jeg faktisk er i gang med lige nu, ikke om alt det jeg kan generelt. Jeg opdaterer den, når prioriteterne ændrer sig. Idéen er lånt fra nownownow.com.","blocks":[{"t":"Hvad jeg arbejder på","items":[{"n":"ferrysport.dk","d":"Genoplivning af dansk webshop med arbejdstøj. Plan i 8 trin, kunden ser fremskridt i sit eget panel. Lavet: spam-registreringer 92→16, døde produktvarianter, hastighed, nyhedsbrev. Næste — online betalinger og produkttekster."},{"n":"RegnskabHub","d":"Samlet API til danske regnskabssystemer plus AI-bogholder. 54 tests, 20+ endpoints, tre read-adaptere. Det mest ambitiøse af de nuværende."},{"n":"ideamine","d":"Radar til produktidéer: 24 kilder med brugerklager og -ønsker, dedup, scoring efter min profil, dagligt digest på ukrainsk."},{"n":"soge.dk і danadresse.dk","d":"Danske data-API’er: transport, virksomheder, adresser. 130+ mio. poster. Offentlige API’er og MCP-servere, så AI-agenter kan bruge dem."},{"n":"andrii.dk","d":"Det her site. Skrevet om fra bunden i september 2026: WebGL-felt med 6.119 partikler, scroll-scrubbet video, katalog med 62 projekter."}]},{"t":"Hvad jeg lærer","items":[{"n":"Stemmebaserede AI-agenter i realtid","d":"Efter lanceringen af AI-PBX graver jeg dybere: afbrydelser, forsinkelser, adfærd i støj, hvornår agenten skal give op og hente et menneske."},{"n":"Dansk","d":"Jeg læser mere flydende, end jeg taler. Målet for året — at holde et oplæg på dansk uden at skifte til engelsk."},{"n":"Lokale modeller på Apple Silicon","d":"Hvor meget nyttigt kan man lave uden skyen: MLX, kvantisering, grænsen for kvalitet."}]},{"t":"Hvad jeg søger","items":[{"n":"Et-to kundeprojekter i Q4","d":"Det virker bedst med opgaver, hvor der er reel drift: regnskab, logistik, kundebooking, data. Jeg bryder mig ikke om «lav det pænt for os»."},{"n":"Oplæg og workshops","d":"Især der, hvor der sidder små virksomhedsejere i salen, og ikke udviklerkolleger."},{"n":"Partnere til danske dataprojekter","d":"Hvis du arbejder med CVR, DMR, adresser eller telefoni i Danmark — så skriv, vi har nok noget vi kan bytte."}]},{"t":"Uden for arbejdet","items":[{"n":"To børn","d":"Hovedprojektet, deadline — hele livet."},{"n":"Tennis, squash, padel","d":"Padel-fællesskabet i København; opstillinger til kampene laver min egen bot."},{"n":"3D-print og en lasergraveringsmaskine","d":"Garagen som anden server. Hvis en ting kan skilles ad — så skiller jeg den ad."}]}]},"checklist":{"slug":"cheklist","title":"12 steder, hvor små virksomheder mister kunder online","subtitle":"Tjekliste, der virker. Du kan komme igennem den på en aften, de fleste punkter koster nul kroner.","intro":"Det her er ikke teori fra et kursus. Det er det, jeg ser i hver anden virksomhed, der kommer til mig — i Danmark og ikke kun. Kunden leder allerede efter dig. Spørgsmålet er bare, på hvilket trin han går over til en anden.\n\nGå den igennem oppefra og ned. Ved hvert punkt er der en test på to minutter — tag den med det samme, ikke senere. Er punktet grønt, så gå videre. Hvis ikke — står der samme sted, hvad du skal gøre.","howto":"Giv dig selv en ærlig vurdering for hvert punkt: ✓ virker · ~ halvt · ✗ findes ikke. Tæl krydserne. Syv eller flere — så mister du de fleste af dem, der allerede har ledt efter dig.","points":[{"n":1,"t":"Du er ikke på Google Maps","sym":"Folk søger «[din ydelse] + by», ser konkurrenterne i kortene øverst, dig — ingen steder.","check":"Åbn google.com i inkognito og søg på dit firmanavn. Så — din ydelse og by. Ser du dig selv i de første tre kort?","fix":"Opret en Google Business Profile og bekræft den med postkort eller video. Det er gratis og giver den største stigning i opkald af alt på den her liste.","cost":"0 kr · 30 min + 2 uger til bekræftelse"},{"n":2,"t":"Du har en profil, men den er tom","sym":"Forkert kategori, ingen åbningstider, ét foto — og det er sløret. Google viser sådan en profil længere nede.","check":"Åbn din profil og tæl: kategori, åbningstider, telefon, website, serviceområde, beskrivelse, 10+ fotos. Hvor mange af de syv er udfyldt?","fix":"Udfyld det hele. Fotos — ikke stock: arbejdsplads, resultat af arbejdet, dig selv. Google giver forrang til kort, der bliver opdateret.","cost":"0 kr · 1 time"},{"n":3,"t":"Ingen anmeldelser — eller de står uden svar","sym":"0 anmeldelser bliver læst som «0 kunder». En anmeldelse uden svar — som «ejeren er ligeglad».","check":"Hvor mange anmeldelser har du, og hvor mange af dem har du svaret på? Også de negative?","fix":"Bed ti tilfredse kunder — personligt, med et direkte link. Svar på hver anmeldelse, også den dårlige: den kommende kunde læser netop dit svar, ikke klagen.","cost":"0 kr · 15 min om ugen fast"},{"n":4,"t":"Siden åbner langsommere end tre sekunder","sym":"Halvdelen af folk på telefonen smutter, før de ser første skærm. Du betaler for reklame, der ikke når frem.","check":"pagespeed.web.dev, indsæt adresse, kig på mobil-fanen. LCP over 2,5 sekunder — rød zone.","fix":"De mest typiske syndere: tunge fotos uden komprimering, et dusin plugins, skrifttyper fra et fremmed domæne, en slider på forsiden. Fjern slideren, komprimér fotos, slå det fra, du ikke bruger.","cost":"0–2.000 kr · alt efter hvor slemt det står til"},{"n":5,"t":"På telefonen er det besværligt at bruge siden","sym":"Teksten er lille, knapperne rammer man ikke med fingeren, menuen gemmer sig, formularen kan ikke være på skærmen.","check":"Åbn din side på din egen telefon og prøv at gøre det, du vil have kunden til — booke, bestille, ringe. Hvor mange tryk tog det?","fix":"Mere end tre tryk til målet — det er allerede for meget. Hovedknappen skal være på første skærm og ikke forsvinde ved scroll.","cost":"0 kr for tjek · derefter afhænger det"},{"n":6,"t":"På fem sekunder er det uklart, hvad du sælger","sym":"På første skærm står der «Velkommen» og et flot billede. Hvad præcis, til hvem, og hvad det koster — ligger længere nede eller ingen steder.","check":"Vis første skærm til en person, der ikke kender din forretning. Spørg: hvad sælger man her, og til hvem? Gættede de ikke på fem sekunder — så skriv om.","fix":"Formlen uden pynt: hvad du gør + til hvem + i hvilken by + hvad man skal gøre nu. «Vi samler møbler i København til fast pris. Beregn prisen».","cost":"0 kr · en halv time og ærlighed"},{"n":7,"t":"Der er ingen pris nogen steder, ikke engang cirka","sym":"«Pris på forespørgsel» bliver læst som «dyrt» eller «det kommer an på, hvor rig du er». De fleste skriver bare ikke.","check":"Kan man finde i det mindste størrelsesordenen, uden at tale med dig?","fix":"Giv et prisinterval, eksempler på typiske opgaver med beløb eller en beregner. Prisen sorterer dem fra, der alligevel ikke ville købe — og din tid bliver fri til resten.","cost":"0 kr · men kræver en beslutning"},{"n":8,"t":"Du kan kun kontakte via formularen","sym":"Folk vil skrive i en messenger, men skal udfylde felter og vente. Så lukker de bare fanen.","check":"Tæl kanalerne på sitet: telefon, der kan ringes op med ét tryk, messenger, mail, formular. Under tre — for lidt.","fix":"Tilføj det, din målgruppe faktisk bruger. I Danmark er det tit telefon og mail, i det ukrainske miljø — Telegram og WhatsApp. Sæt knapperne, så de kan ses på mobilen.","cost":"0 kr · time"},{"n":9,"t":"Uden for arbejdstid svarer ingen","sym":"De fleste henvendelser kommer om aftenen og i weekenden. Svar mandag kl. 10 — så er det allerede en anden leverandør.","check":"Kig i mail eller telefon, hvornår forespørgslerne kommer. Hvor mange fik svar samme dag?","fix":"Minimum — et autosvar, der ærligt siger, hvornår der kommer svar, og giver noget nyttigt allerede nu. Maksimum — en simpel bot eller AI-assistent, der svarer på de typiske spørgsmål og samler kontaktinfo.","cost":"0 kr til autosvar · bot — fra et par tusind"},{"n":10,"t":"Der er ingen online booking","sym":"For at booke skal man ringe. En del folk bryder sig slet ikke om at ringe — og det er ikke en lille del.","check":"Kan kunden vælge tid og booke selv, uden dig, kl. ti om aftenen?","fix":"Sæt et hvilket som helst online bookingsystem op og læg linket i dit Google-kort, i profilen og på sitet. Det virker, mens du sover.","cost":"0–300 kr om måneden"},{"n":11,"t":"Der er ingen beviser for, at du faktisk kan dit håndværk","sym":"Du har tekster om «individuel tilgang» og «kvalitet», men der er ingen billeder af arbejde, cases og «før/efter».","check":"Find tre konkrete beviser på din egen side: billede af dit arbejde, en kundehistorie med et tal, en anmeldelse med navn. Fandt du dem?","fix":"Tag billeder af hvert eneste job, også med telefonen. Én case om måneden — det er tolv om året. Et tal i en case vejer mere end alle flotte ord.","cost":"0 kr · disciplin"},{"n":12,"t":"Kunden er væk — og så gør du ikke mere","sym":"En person kiggede, skrev ikke, og gik for altid. En ny skal du købe igen for penge.","check":"Har du overhovedet én måde at få personen tilbage på: mailingliste, kanal, retargeting, påmindelser til faste kunder?","fix":"Start med det nemmeste — saml e-mails ind mod noget nyttigt og skriv én gang om måneden. Næste nemme skridt — påmind dem, der allerede har købt: dem er det ti gange billigere at sælge til end nye.","cost":"0 kr at starte med"}],"after":{"t":"Talt krydserne?","items":[{"r":"0–2 ✗","d":"Det går fint. Tag fat i punkt 12 — tilbagevendende kunder, der ligger de fleste penge."},{"r":"3–6 ✗","d":"Helt klassisk. Vælg tre af de billigste punkter på listen og få dem lukket i denne uge — så kan du se resultatet om en måned."},{"r":"7+ ✗","d":"Du betaler for reklame og netværk, og så mister du folk i døren. Start med punkterne 1, 2, 6 — de er gratis og giver mest."}]},"author":"Andrii Lynnyk · LynBro ApS · andrii.dk"},"seq":{"count":95,"base":"","poster":"https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/hero-poster-1.jpg","urls":["https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_001.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_002.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_003.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_004.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_005.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_006.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_007.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_008.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_009.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_010.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_011.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_012.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_013.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_014.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_015.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_016.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_017.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_018.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_019.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_020.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_021.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_022.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_023.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_024.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_025.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_026.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_027.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_028.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_029.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_030.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_031.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_032.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_033.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_034.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_035.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_036.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_037.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_038.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_039.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_040.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_041.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_042.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_043.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_044.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_045.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_046.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_047.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_048.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_049.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_050.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_051.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_052.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_053.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_054.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_055.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_056.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_057.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_058.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_059.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_060.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_061.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_062.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_063.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_064.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_065.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_066.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_067.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_068.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_069.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_070.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_071.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_072.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_073.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_074.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_075.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_076.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_077.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_078.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_079.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_080.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_081.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_082.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_083.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_084.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_085.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_086.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_087.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_088.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_089.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_090.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_091.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_092.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_093.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_094.webp","https://andrii.best-choice.dk/wp-content/uploads/sites/3/2026/09/f_095.webp"]}}; ; window.A3URLS={"home": "/da/om-mig/", "projects": "/da/projekter/", "speaking": "/da/foredrag/", "now": "/da/nu/", "checklist": "/da/tjekliste/", "quiz": "/uk/womans-day/", "ommig": "https://ommig.dk/andrii", "email": "info@andrii.dk"}; ; window.A3TRACK={}; ; /* ══════════════════════════════════════════════════════════════════════ a3.ui — взаємодія: очі, курсор, скрол-кадри, термінал, лічильники. ══════════════════════════════════════════════════════════════════════ */ (function (A3) { 'use strict'; // курсор і термінал мусять жити ВСЕРЕДИНІ .a3 — інакше не бачать токенів теми var host = function () { return document.querySelector('.a3') || document.body; }; var reduced = matchMedia('(prefers-reduced-motion: reduce)').matches; var coarse = matchMedia('(pointer: coarse)').matches; var lerp = function (a, b, t) { return a + (b - a) * t; }; var clamp = function (v, a, b) { return v < a ? a : v > b ? b : v; }; /* ─────────────── ОЧІ, ЩО СТЕЖАТЬ ЗА КУРСОРОМ ─────────────── */ A3.eyes = function (root) { if (!root) return; var geo = JSON.parse(root.dataset.eyes || '{}'); if (!geo.eyes) return; var irises = [], lids = []; geo.eyes.forEach(function (e, i) { var wrap = document.createElement('span'); wrap.className = 'a3-eye'; wrap.style.cssText = 'position:absolute;left:' + (e.x * 100) + '%;top:' + (e.y * 100) + '%;' + 'width:' + (e.r * 2 * geo.irisSprite.scale * 100) + '%;aspect-ratio:1;' + 'transform:translate(-50%,-50%);pointer-events:none;will-change:transform'; var img = document.createElement('img'); img.src = root.dataset.iris; img.alt = ''; img.decoding = 'async'; img.style.cssText = 'width:100%;height:100%;display:block'; wrap.appendChild(img); root.appendChild(wrap); irises.push(wrap); // повіка — вузька смужка кольору шкіри, що падає вниз під час моргання var lid = document.createElement('span'); lid.className = 'a3-lid'; var lc = (geo.lid && geo.lid[i]) || [201, 162, 136]; var lc2 = lc.map(function (v) { return Math.max(0, v - 26); }); lid.style.cssText = 'position:absolute;left:' + (e.x * 100) + '%;top:' + ((e.y - e.r * 1.55) * 100) + '%;' + 'width:' + (e.r * 3.6 * 100) + '%;height:' + (e.r * 3.3 * 100) + '%;' + 'transform:translate(-50%,0) scaleY(0);transform-origin:top center;' + 'background:linear-gradient(180deg,rgb(' + lc.join(',') + ') 0%,rgb(' + lc2.join(',') + ') 100%);' + 'border-radius:0 0 50% 50%/0 0 46% 46%;pointer-events:none;filter:blur(.6px)'; root.appendChild(lid); lids.push(lid); }); // зміщення задано в частках ширини портрета, а transform рахується // у відсотках ВІД САМОЇ райдужки — переводимо var eyeW = geo.eyes[0].r * 2 * (geo.irisSprite.scale || 1.15); var max = (geo.maxOffset || 0.005) / eyeW * 100; var tx = 0, ty = 0, cx = 0, cy = 0; function aim(px, py) { var r = root.getBoundingClientRect(); var ox = (px - (r.left + r.width * 0.5)) / (r.width * 1.6); var oy = (py - (r.top + r.height * 0.42)) / (r.height * 1.6); var d = Math.hypot(ox, oy) || 1; var k = Math.min(d, 1) / d; tx = clamp(ox * k, -1, 1); ty = clamp(oy * k, -1, 1); } if (!coarse) { addEventListener('pointermove', function (e) { aim(e.clientX, e.clientY); }, { passive: true }); } else { // на тачі очі стежать за прокруткою addEventListener('scroll', function () { var r = root.getBoundingClientRect(); ty = clamp((innerHeight * 0.5 - (r.top + r.height / 2)) / innerHeight, -1, 1); tx = Math.sin(scrollY / 420) * 0.6; }, { passive: true }); } // моргання + «озирання» в простої var idle = 0, lastMove = Date.now(); addEventListener('pointermove', function () { lastMove = Date.now(); }, { passive: true }); function blink() { lids.forEach(function (l) { l.style.transition = 'transform .09s ease-in'; l.style.transform = 'translate(-50%,0) scaleY(1)'; setTimeout(function () { l.style.transition = 'transform .16s ease-out'; l.style.transform = 'translate(-50%,0) scaleY(0)'; }, 95); }); setTimeout(blink, 2600 + Math.random() * 4800); } if (!reduced) setTimeout(blink, 1800); (function loop() { requestAnimationFrame(loop); // у простої — повільно озирається if (Date.now() - lastMove > 4000 && !coarse) { idle += 0.006; tx = Math.sin(idle) * 0.75; ty = Math.sin(idle * 0.62) * 0.4 - 0.1; } cx = lerp(cx, tx, 0.085); cy = lerp(cy, ty, 0.085); var t = 'translate(calc(-50% + ' + (cx * max).toFixed(3) + '%), calc(-50% + ' + (cy * max * 0.72).toFixed(3) + '%))'; for (var i = 0; i < irises.length; i++) irises[i].style.transform = t; })(); }; /* ─────────────── МАГНІТНИЙ КУРСОР ─────────────── */ A3.cursor = function () { if (coarse || reduced) return; var el = document.createElement('div'); el.className = 'a3-cur'; el.innerHTML = ''; host().appendChild(el); var lab = el.firstChild; var x = innerWidth / 2, y = innerHeight / 2, tx = x, ty = y; addEventListener('pointermove', function (e) { tx = e.clientX; ty = e.clientY; }, { passive: true }); (function loop() { requestAnimationFrame(loop); x = lerp(x, tx, 0.22); y = lerp(y, ty, 0.22); el.style.transform = 'translate(' + (x - 4) + 'px,' + (y - 4) + 'px) translate(-50%,-50%)'; })(); document.addEventListener('pointerover', function (e) { var m = e.target.closest('[data-cur]'); if (m) { el.classList.add('mag'); lab.textContent = m.dataset.cur || ''; el.classList.remove('txt'); } else if (e.target.closest('p,li,h1,h2,h3,input,textarea')) { el.classList.add('txt'); el.classList.remove('mag'); } else { el.classList.remove('mag', 'txt'); } }); document.documentElement.style.cursor = 'none'; }; /* ─────────────── СКРОЛ-СКРАБ КАДРІВ ─────────────── */ A3.sequence = function (cfg) { var cv = document.getElementById(cfg.canvas); if (!cv) return; var ctx = cv.getContext('2d', { alpha: false }); var host = cv.closest('[data-seq-host]') || cv.parentElement; var n = cfg.count, imgs = new Array(n), loaded = 0, ready = false, cur = -1; var poster = new Image(); poster.src = cfg.poster; var status = document.querySelector(cfg.statusSel || '#seqStatus'); function fit() { var dpr = Math.min(devicePixelRatio || 1, 2); cv.width = cv.clientWidth * dpr | 0; cv.height = cv.clientHeight * dpr | 0; draw(cur < 0 ? 0 : cur, true); } function cover(img) { if (!img || !img.width) return; var cw = cv.width, ch = cv.height; var s = Math.max(cw / img.width, ch / img.height); var w = img.width * s, h = img.height * s; ctx.fillStyle = '#07080c'; ctx.fillRect(0, 0, cw, ch); ctx.drawImage(img, (cw - w) / 2, (ch - h) / 2, w, h); } function draw(i, force) { i = clamp(i | 0, 0, n - 1); if (i === cur && !force) return; cur = i; var img = imgs[i]; cover(img && img.complete && img.width ? img : (poster.complete ? poster : null)); } // прогресивне завантаження: спершу кожен 8-й, потім решта function load(order, done) { var idx = 0, finished = false; function next() { if (idx >= order.length) { if (!finished) { finished = true; if (done) done(); } return; } var k = order[idx++]; if (imgs[k]) return next(); var im = new Image(); im.decoding = 'async'; im.onload = im.onerror = function () { loaded++; if (status) status.textContent = Math.round(loaded / n * 100) + '%'; if (!ready && loaded > 8) { ready = true; onScroll(); } next(); }; im.src = cfg.url(k + 1); imgs[k] = im; } for (var p = 0; p < 4; p++) next(); // 4 паралельні } var coarseOrder = [], fineOrder = []; for (var i = 0; i < n; i++) (i % 8 === 0 ? coarseOrder : fineOrder).push(i); poster.onload = function () { draw(0, true); }; function onScroll() { var r = host.getBoundingClientRect(); var total = r.height - innerHeight; if (total <= 0) return; var p = clamp(-r.top / total, 0, 1); draw(Math.round(p * (n - 1))); if (cfg.onProgress) cfg.onProgress(p); } addEventListener('resize', fit, { passive: true }); addEventListener('scroll', function () { requestAnimationFrame(onScroll); }, { passive: true }); fit(); // вантажимо тільки коли секція наближається var io = new IntersectionObserver(function (es) { if (es[0].isIntersecting) { io.disconnect(); load(coarseOrder, function () { load(fineOrder); }); } }, { rootMargin: '900px' }); io.observe(host); return { refresh: onScroll }; }; /* ─────────────── ЛІЧИЛЬНИКИ ─────────────── */ A3.counters = function (sel) { var els = document.querySelectorAll(sel || '[data-count]'); var io = new IntersectionObserver(function (entries) { entries.forEach(function (en) { if (!en.isIntersecting) return; io.unobserve(en.target); var el = en.target, to = parseFloat(el.dataset.count), t0 = 0; var dec = (el.dataset.dec | 0), pre = el.dataset.pre || '', suf = el.dataset.suf || ''; if (reduced) { el.textContent = pre + to.toLocaleString('uk-UA') + suf; return; } function step(t) { if (!t0) t0 = t; var p = clamp((t - t0) / 1500, 0, 1); var e = 1 - Math.pow(1 - p, 3.2); var v = to * e; el.textContent = pre + (dec ? v.toFixed(dec) : Math.round(v).toLocaleString('uk-UA')) + suf; if (p < 1) requestAnimationFrame(step); } requestAnimationFrame(step); }); }, { threshold: 0.4 }); els.forEach(function (e) { io.observe(e); }); }; /* ─────────────── ПОЯВИ + КІНЕТИЧНА ВАГА ─────────────── */ A3.motion = function () { // появи рахуємо в тому самому rAF-циклі, що й решту: // IntersectionObserver тут виявився ненадійним, а невидимий текст — неприпустимий var pending = [].slice.call(document.querySelectorAll('[data-rise],.lines')); var fcount = 0; function sweep(force) { for (var i = pending.length - 1; i >= 0; i--) { var el = pending[i]; var r = el.getBoundingClientRect(); if (force || (r.top < innerHeight * 0.94 && r.bottom > 0)) { el.classList.add('in'); pending.splice(i, 1); } } } requestAnimationFrame(function () { sweep(false); }); var bar = document.querySelector('.a3-prog'), nav = document.querySelector('.a3-nav'); var navAlways = nav && nav.hasAttribute('data-always'); var lastY = scrollY, vel = 0, kin = document.querySelectorAll('[data-kin]'); function tick() { requestAnimationFrame(tick); if (pending.length && (++fcount & 3) === 0) sweep(false); var y = scrollY; var dy = y - lastY; lastY = y; vel = lerp(vel, Math.min(Math.abs(dy), 90), 0.12); var h = document.documentElement.scrollHeight - innerHeight; if (bar) bar.style.width = (h > 0 ? (y / h) * 100 : 0) + '%'; if (nav && !navAlways) nav.classList.toggle('on', y > innerHeight * 0.75); if (!reduced && kin.length) { // вага шрифту стискається від швидкості прокрутки + RGB-розшарування var w = Math.round(760 - vel * 4.6); var off = (vel * 0.045).toFixed(2); for (var i = 0; i < kin.length; i++) { kin[i].style.setProperty('--kin-w', clamp(w, 320, 800)); kin[i].style.textShadow = vel > 2 ? (-off + 'px 0 rgba(255,80,80,.6), ' + off + 'px 0 rgba(80,210,255,.6)') : 'none'; } } // активний пункт навігації var best = null, bd = 1e9; secs.forEach(function (s) { var d = Math.abs(s.el.getBoundingClientRect().top - innerHeight * 0.32); if (d < bd) { bd = d; best = s; } }); if (best && best.id !== activeId) { activeId = best.id; document.querySelectorAll('.a3-nav__links a').forEach(function (a) { a.toggleAttribute('aria-current', a.getAttribute('href') === '#' + activeId); }); if (A3.onSection) A3.onSection(activeId, best.el); } } var secs = [].map.call(document.querySelectorAll('[data-section]'), function (el) { return { id: el.id, el: el }; }); var activeId = null; tick(); }; /* ─────────────── ТЕРМІНАЛ (клавіша /) ─────────────── */ A3.terminal = function (commands) { var box = document.createElement('div'); box.className = 'a3-term'; box.setAttribute('role', 'dialog'); box.setAttribute('aria-label', 'Консоль сайту'); box.innerHTML = '
' + '
andrii@dk:~$' + '
'; host().appendChild(box); var out = box.querySelector('#a3TermOut'), inp = box.querySelector('#a3TermIn'); var hist = [], hi = -1, open = false; function print(html, cls) { var d = document.createElement('div'); if (cls) d.className = cls; d.innerHTML = html; out.appendChild(d); out.scrollTop = out.scrollHeight; } function toggle(v) { open = v === undefined ? !open : v; box.classList.toggle('on', open); if (open) { inp.focus(); if (!out.children.length) print(commands.__banner, 'b'); } else inp.blur(); } A3.termToggle = toggle; addEventListener('keydown', function (e) { if (e.key === '/' && !open && !/^(INPUT|TEXTAREA)$/.test(document.activeElement.tagName)) { e.preventDefault(); toggle(true); } else if (e.key === 'Escape' && open) { toggle(false); } }); inp.addEventListener('keydown', function (e) { if (e.key === 'Enter') { var v = inp.value.trim(); inp.value = ''; if (!v) return; hist.push(v); hi = hist.length; print('andrii@dk:~$ ' + v.replace(/' + ((window.A3DATA && A3DATA.t && A3DATA.t.termNoCmd) || 'команди «%s» немає. спробуй') .replace('%s', parts[0].replace(/help'); } else if (e.key === 'ArrowUp') { if (hi > 0) inp.value = hist[--hi]; e.preventDefault(); } else if (e.key === 'ArrowDown') { hi = Math.min(hi + 1, hist.length); inp.value = hist[hi] || ''; } }); box.addEventListener('click', function (e) { if (e.target === box) toggle(false); }); }; /* ─────────────── АКОРДЕОН ─────────────── */ A3.accordion = function (sel) { document.querySelectorAll(sel).forEach(function (item) { var btn = item.querySelector('button'), panel = item.querySelector('[data-panel]'); if (!btn || !panel) return; btn.addEventListener('click', function () { var isOpen = item.classList.toggle('on'); btn.setAttribute('aria-expanded', isOpen); panel.style.height = isOpen ? panel.scrollHeight + 'px' : '0px'; }); }); }; /* ─────────────── ПЕРЕМИКАЧ МОВ ─────────────── */ A3.langSwitch = function (cfg) { var host = document.getElementById('a3Lang'); if (!host || !cfg || !cfg.alt) return; host.innerHTML = cfg.order.map(function (l) { var cur = l === cfg.current; return '' + l + ''; }).join(''); }; A3.reduced = reduced; A3.coarse = coarse; })(window.A3 = window.A3 || {}); ; /* ══════════════════════════════════════════════════════════════════════ a3.track — легка cookieless-аналітика. Принцип: НІЧОГО не зберігаємо на пристрої відвідувача й не ставимо ідентифікаторів. Кожна подія самодостатня, агрегація — на приймачі. Через це cookie-банер не потрібен (ePrivacy), і в payload немає персональних даних. Приймач конфігурується одним обʼєктом: window.A3TRACK = { matomo: { url: 'https://matomo.best-choice.dk/', siteId: 1 }, // або endpoint:'https://collector.example/e', // свій debug: false } Якщо приймача не задано — трекер мовчки збирає контекст сесії (у памʼяті вкладки) і віддає його формам, щоб лід приходив із джерелом. ══════════════════════════════════════════════════════════════════════ */ (function (A3) { 'use strict'; var CFG = window.A3TRACK || {}; var dnt = navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.globalPrivacyControl === true; // ── контекст візиту (живе тільки в памʼяті вкладки) ──────────────── var q = new URLSearchParams(location.search); var ctx = { path: location.pathname, lang: (document.documentElement.lang || 'uk').slice(0, 2), ref: (function () { try { if (!document.referrer) return 'прямий'; var h = new URL(document.referrer).hostname.replace(/^www\./, ''); return h === location.hostname ? 'внутрішній' : h; } catch (e) { return 'невідомо'; } })(), utm: ['source', 'medium', 'campaign', 'content'].reduce(function (o, k) { var v = q.get('utm_' + k); if (v) o[k] = v.slice(0, 60); return o; }, {}), screen: innerWidth + '×' + innerHeight, device: innerWidth < 760 ? 'телефон' : innerWidth < 1100 ? 'планшет' : 'компʼютер', t0: Date.now(), depth: 0, events: [] }; // подорож між сторінками — sessionStorage лише якщо він доступний, // і тільки шляхи, без ідентифікаторів try { var prev = JSON.parse(sessionStorage.getItem('a3j') || '[]'); if (prev.indexOf(ctx.path) < 0) prev.push(ctx.path); ctx.journey = prev.slice(-12); sessionStorage.setItem('a3j', JSON.stringify(ctx.journey)); } catch (e) { ctx.journey = [ctx.path]; } // ── відправка ────────────────────────────────────────────────────── function beacon(url) { try { if (navigator.sendBeacon) navigator.sendBeacon(url); else { var i = new Image(); i.src = url; } } catch (e) { /* аналітика ніколи не ламає сторінку */ } } function send(name, extra) { if (dnt) return; ctx.events.push({ e: name, t: Math.round((Date.now() - ctx.t0) / 1000) }); if (CFG.debug) console.log('[a3.track]', name, extra || ''); if (CFG.matomo && CFG.matomo.url) { // Matomo HTTP Tracking API — без завантаження matomo.js var p = new URLSearchParams({ idsite: CFG.matomo.siteId || 1, rec: 1, apiv: 1, url: location.origin + location.pathname, action_name: name === 'pageview' ? (document.title || ctx.path) : name, urlref: document.referrer || '', res: screen.width + 'x' + screen.height, lang: ctx.lang, rand: Math.random().toString(36).slice(2), send_image: 0 }); if (name !== 'pageview') { p.set('e_c', 'site'); p.set('e_a', name); if (extra) p.set('e_n', String(extra).slice(0, 80)); } Object.keys(ctx.utm).forEach(function (k) { p.set('utm_' + k, ctx.utm[k]); }); beacon(CFG.matomo.url.replace(/\/?$/, '/') + 'matomo.php?' + p.toString()); } if (CFG.endpoint) { var d = new URLSearchParams({ s: 'andrii.dk', e: name, p: ctx.path, r: ctx.ref, l: ctx.lang, d: ctx.device, n: extra ? String(extra).slice(0, 80) : '' }); Object.keys(ctx.utm).forEach(function (k) { d.set('utm_' + k, ctx.utm[k]); }); beacon(CFG.endpoint + (CFG.endpoint.indexOf('?') < 0 ? '?' : '&') + d.toString()); } } // ── що міряємо ───────────────────────────────────────────────────── send('pageview'); var marks = [25, 50, 75, 100], hit = {}; addEventListener('scroll', function () { var h = document.documentElement.scrollHeight - innerHeight; if (h <= 0) return; var pct = Math.min(100, Math.round(scrollY / h * 100)); if (pct > ctx.depth) ctx.depth = pct; marks.forEach(function (m) { if (pct >= m && !hit[m]) { hit[m] = 1; send('scroll', m + '%'); } }); }, { passive: true }); document.addEventListener('click', function (ev) { var el = ev.target.closest('[data-ev]'); if (el) return send(el.dataset.ev, el.dataset.evName || el.textContent.trim().slice(0, 60)); var a = ev.target.closest('a[href]'); if (!a) return; var href = a.getAttribute('href'); if (/^mailto:/.test(href)) send('mailto'); else if (/^https?:/.test(href) && a.hostname !== location.hostname) send('outbound', a.hostname); }, true); addEventListener('pagehide', function () { send('leave', ctx.depth + '% · ' + Math.round((Date.now() - ctx.t0) / 1000) + 'с'); }); // ── контекст для форм (працює навіть без приймача) ───────────────── A3.track = { event: send, context: function () { return { source: ctx.ref, utm: Object.keys(ctx.utm).length ? Object.keys(ctx.utm).map(function (k) { return k + '=' + ctx.utm[k]; }).join(' · ') : '—', journey: (ctx.journey || []).join(' → '), onPage: Math.round((Date.now() - ctx.t0) / 1000) + ' с', depth: ctx.depth + '%', device: ctx.device + ' · ' + ctx.screen, lang: ctx.lang }; } }; })(window.A3 = window.A3 || {}); ; /* a3.speaking — сторінка спікера */ (function (A3) { 'use strict'; var D = window.A3DATA || {}, S = D.speaking, T = D.t || {}; var tr = function (k, d) { return T[k] || d || k; }; if (!S) return; var $ = function (s) { return document.querySelector(s); }; var esc = function (s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]; }); }; function topics() { $('#spTopics').innerHTML = S.topics.map(function (t, i) { return '
' + '
Т' + (i + 1) + '
' + '

' + esc(t.t) + '

' + '

' + esc(t.d) + '

' + '
' + '
' + tr('spWho', 'для кого') + '
' + esc(t.aud) + '

' + tr('spLen', 'тривалість') + '
' + esc(t.len) + '

' + tr('spLang', 'мова') + '
' + esc(t.lang) + '
' + '
'; }).join(''); } function formats() { $('#spFormats').innerHTML = S.formats.map(function (f) { return '
' + esc(f.t) + '' + esc(f.m) + '

' + esc(f.d) + '

'; }).join(''); } function past() { $('#spPast').innerHTML = S.past.map(function (p) { return '
' + esc(p.y) + '' + '' + esc(p.e) + '' + '' + esc(p.d) + '
'; }).join(''); } function kit() { $('#bioOne').textContent = S.bioOneLine; $('#bioShort').textContent = S.bioShort; [].forEach.call(document.querySelectorAll('[data-copy]'), function (el) { var b = document.createElement('button'); b.type = 'button'; b.textContent = tr('copy'); b.setAttribute('data-cur', tr('copy')); b.addEventListener('click', function () { var txt = el.textContent.replace(new RegExp(tr('copy') + '|' + tr('copied'), 'g'), '').trim(); (navigator.clipboard ? navigator.clipboard.writeText(txt) : Promise.reject()) .then(function () { b.textContent = 'скопійовано'; setTimeout(function () { b.textContent = tr('copy'); }, 1800); }) .catch(function () { b.textContent = 'виділи вручну'; setTimeout(function () { b.textContent = tr('copy'); }, 2200); }); }); el.appendChild(b); }); } function mail() { var a = $('#spMail'); if (!a) return; a.href = 'mailto:info@andrii.dk?subject=' + encodeURIComponent(tr('mailSubject')) + '&body=' + encodeURIComponent(tr('mailBody')); } function init() { topics(); formats(); past(); kit(); mail(); A3.motion(); A3.counters(); A3.cursor(); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); })(window.A3 = window.A3 || {}); ;A3.langSwitch({"current": "da", "order": ["uk", "en", "da"], "alt": {"uk": "/uk/vystupy/", "en": "/speaking/", "da": "/da/foredrag/"}});