' + esc(t.t) + '
' + '' + esc(t.d) + '
' + '- ' + t.take.map(function (x) { return '
- ' + esc(x) + ' '; }).join('') + '
' + esc(t.aud) + '
' + tr('spLen', 'тривалість') + '
' + esc(t.len) + '
' + tr('spLang', 'мова') + '
' + esc(t.lang) + '
window.A3DATA={"lang":"en","t":{"clSym":"HOW IT LOOKS FROM THE OUTSIDE","clCheck":"CHECK IN 2 MINUTES","clFix":"WHAT TO DO","clMore":"…and six more points below","clSending":"sending…","clErrFields":"Check your name and email.","formOk":"✓ received. I will reply personally, usually the same day.","formErr":"didn’t go through. write directly:","formEmpty":"Fill in all three fields, please.","formSending":"sending…","bootInit":"initialisation","bootProjects":"mounting /projects","bootGit":"reading git","bootField":"building a particle field","bootCvr":"connecting CVR","bootEyes":"checking eyesight","bootEyesV":"eyes active","bootDone":"done","bootDoneV":"welcome","bootRecords":"entries","bootRepos":"repositories","bootCommits":"commits","bootDots":"points","bootSkip":"skip →","termTitle":"andrii.dk — console","termBuilt":"version 3.0 · built","termCmds":"commands","termExit":"exit — Esc","termNoCmd":"no «%s» command. try","termWho":"entrepreneur · IT practitioner · founder · speaker","termDays":"days in Denmark","termRepos":"repositories","termPrivate":"private","termCommits":"commits","termProjects":"projects","termCompanies":"companies","termCommunities":"non-profit associations","termOrgs":"non-profit associations","termSince":"in business since","termTop":"top repositories by commits:","termNothing":"found nothing. domains:","termTotal":"total","termDetails":"details:","termCatalog":"catalog:","termNoSuch":"not found. try","termStack":"stack:","termProof":"proof:","termHire":"write:","termHire2":"or fill in the form below on the page. I read it myself, usually reply the same day.","termHelp":"— who I am|— project list|— what I work with|— numbers|— talk topics|— how to start working|— short biography|— open project|— clear","timeIn":"Copenhagen","timeHere":"around","timeAsleep":"asleep","seeAll":"all","restCatalog":"rest of the catalog","moreProjects":"%s more projects","moreProjectsD":"Danish data APIs, SaaS, bots, payments, scraping, infrastructure — with filters by domain and stack.","openCatalog":"Open catalog →","filterAll":"all domains","filterAnyState":"any status","filterSearch":"search","filterShown":"shown","filterOf":"of","legendProd":"● in production","legendWip":"○ in progress / research","copy":"copy","copied":"copied","copyManual":"select manually","nowUpdated":"updated","nowAgo":"days ago","nowToday":"today","commitsWord":"commits","mailSubject":"Event invitation","mailBody":"Hi Andrii,\n\nEvent: \nDate: \nCity or platform: \nAudience (who will be in the room): \nFormat and length: \nTopic of interest: \n\nAnything else: \n","spWho":"for whom","spLen":"duration","spLang":"language"},"portfolio":{"meta":{"generated":"2026-09-29","source":"GitHub andrey-tut (88 власних репо) + claude-brain/PROJECTS.md + локальні репозиторії","note":"Факти з реальних репо і нотаток, не з назв папок."},"domains":[{"id":"ai","label":"AI and agents","color":"#7c5cff"},{"id":"data","label":"Denmark’s data and API","color":"#00d4a0"},{"id":"saas","label":"SaaS platforms","color":"#ff5c7c"},{"id":"web","label":"Websites and WordPress","color":"#ffb020"},{"id":"bots","label":"Bots and messengers","color":"#4cc3ff"},{"id":"fintech","label":"Payments and accounting","color":"#c0ff3e"},{"id":"scraping","label":"Data collection","color":"#ff8a3d"},{"id":"infra","label":"Infrastructure","color":"#9aa4b2"},{"id":"community","label":"Communities and events","color":"#ffd166"},{"id":"business","label":"Business (before IT)","color":"#e0e0e0"}],"statuses":{"prod":{"label":"In production","dot":"#00d4a0"},"building":{"label":"In progress","dot":"#ffb020"},"research":{"label":"Research","dot":"#4cc3ff"},"archive":{"label":"Archive","dot":"#6b7280"}},"projects":[{"id":"virtual_pbx","name":"AI PBX with real-time voice","domain":"ai","status":"prod","year":"2026","commits":null,"url":"https://lynbro.dk/en/products/lynbro-pbx/","one":"A phone robot that speaks Ukrainian and hangs up on its own.","full":"Zadarma → OpenAI Realtime via native SIP. The agent speaks natural Ukrainian, has access to the knowledge base, CRM and service booking, and ends the call on its own. Plus its own CRM and a call dashboard.","stack":["FastAPI","OpenAI Realtime","SIP","MySQL"],"proof":"Live in production · product: LynBro PBX"},{"id":"claude-brain","name":"claude-brain — shared memory","domain":"ai","status":"prod","year":"2026","commits":1822,"one":"My AI remembers all 100+ projects between sessions.","full":"A Git knowledge repository for all projects: notes, lessons, patterns, conventions. FTS5 search in ~8 ms, auto-injects context into every session via hooks, sync between machines. 1 822 commits — my most active repository.","stack":["Shell","SQLite FTS5","Node","Markdown"],"proof":"1 822 commits · daily use"},{"id":"danadresse","name":"danadresse.dk — Denmark address API","domain":"data","status":"prod","year":"2026","commits":409,"url":"https://danadresse.dk","one":"A drop-in replacement for the government DAWA — with an SDK in 4 languages.","full":"Compatible with the DAWA REST API for Danish addresses. Bootstrap + incremental sync. Around it — an ecosystem: SDK for Python, JS/TS, a WordPress plugin for Woo checkout, and a CLI for automatic code migration from DAWA.","stack":["FastAPI","PostgreSQL","ETL"],"proof":"Prod + 5 public packages on GitHub"},{"id":"phone_check","name":"nummeropslag.dk — number lookup","domain":"data","status":"prod","year":"2026","commits":496,"url":"https://nummeropslag.dk","one":"Who is calling and whether it is spam — portal + iOS + Android.","full":"Danish caller ID and spam portal. Backend on own DK data, enrichment via CVR and operators. iOS app 1.2.x in the App Store, Android as well. Public Partner API + MCP server for AI agents.","stack":["FastAPI","MySQL","Meilisearch","Swift","Kotlin"],"proof":"Prod + iOS and Android in the stores"},{"id":"soge","name":"Soge.dk — search for cars and companies","domain":"data","status":"building","year":"2026","commits":385,"url":"https://soge.dk","one":"130+ million records: cars (DMR), companies (CVR), addresses.","full":"Search engine over Danish government registers: transport from DMR (100+ GB XML), companies from CVR, addresses. Public REST API + MCP server, so AI agents can use it. Documentation — andrey-tut.github.io/soge-api.","stack":["Python","SQL","XML ETL","MCP"],"proof":"130M+ records · public API"},{"id":"auctions_history","name":"US car auction history","domain":"scraping","status":"building","year":"2026","commits":469,"one":"Copart, IAAI, Manheim — catalogue, VIN, photos, bids, history.","full":"Successor to parsing_vin: collection from Copart, IAAI, Manheim and aggregators. Full lot history by VIN, photos, bids, buy-now. Separate auth toolkit: 4 login channels, mobile APIs log in without a human. This is the same domain where I ran a real car export business for 4 years.","stack":["Python","curl_cffi","Camoufox","Playwright","residential proxy"],"proof":"469 commits · source research finished"},{"id":"renet","name":"renet.dk — networking at events","domain":"community","status":"building","year":"2026","commits":499,"url":"https://renet.dk","one":"So that at an event you can find the right people, not business cards.","full":"Networking platform for participants of one event: profiles, interest-based matching, messages. My product with the second-highest number of commits.","stack":["Python","Web"],"proof":"499 commits"},{"id":"brella","name":"Brella outreach at TechBBQ 2026","domain":"community","status":"prod","year":"2026","one":"5,290 messages, ~300 replies — in one conference.","full":"Own tool for cold intros and inbox handling on the Brella platform. Works via ActionCable websockets. Production use on 27.08.2026 at TechBBQ: 5,290 messages, about 300 replies.","stack":["Python","httpx","websockets"],"proof":"5,290 messages · 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 for service businesses, several salons on one database.","full":"Booking platform for service businesses: specialists, services, schedule, reminders. Multi-tenant architecture.","stack":["FastAPI","MySQL"],"proof":"215 commits · prod"},{"id":"lynbro","name":"lynbro.dk — site of my own IT company","domain":"web","status":"prod","year":"2026","commits":27,"url":"https://lynbro.dk","one":"366 pages in three languages + AI chat.","full":"LynBro ApS site: 366 pages in Danish, English and Ukrainian, blog, cases, AI chat on n8n. Name: Lyn (lightning) + Bro (bridge).","stack":["Node","n8n","i18n"],"proof":"Prod · 366 pages · da/en/uk"},{"id":"ferrysport","name":"ferrysport.dk — web shop resuscitation","domain":"web","status":"prod","year":"2026","commits":110,"url":"https://ferrysport.dk","one":"56% of product variations were dead. 69% — without a description. Fixed.","full":"Danish workwear web shop. Decoded a 30-page paper audit (da/en/uk), made an 8-stage plan. Did: spam registrations 92→16, thin pages 67→15, logo 65→41 KB, Google fonts gone (7→0 requests), newsletter finally works. Client sees progress in their own dashboard.","stack":["WordPress","WooCommerce","Python"],"proof":"Live client project since September 2026"},{"id":"lynbro-projektportal","name":"Portal “customer ↔ contractor”","domain":"saas","status":"prod","year":"2026","one":"Client clicks a spot on their site and writes what is wrong.","full":"Own product: a progress portal inside the client’s site. Comment with area selection right on the page, time tracking, “why” explained in the client’s language, “before/after” comparison. v1.0 in production on ferrysport.dk.","stack":["Python","JS"],"proof":"v1.0 in production on a real client"},{"id":"plugins_wp","name":"plugins.lynbro.dk — WP plugin studio","domain":"web","status":"prod","year":"2026","commits":266,"url":"https://plugins.lynbro.dk","one":"My own WordPress plugins for the Danish market.","full":"Portal and plugin sources: search in Danish registers right inside WP, free multilingual GDPR/ePrivacy cookie banner, address autocomplete for WooCommerce. Translations go through translate.wordpress.org.","stack":["PHP","WordPress","FastAPI"],"proof":"3 public plugins · translations in WP.org"},{"id":"test_mobilepay","name":"MobilePay / Vipps — payment harness","domain":"fintech","status":"prod","year":"2026","one":"Took apart the API of Denmark’s main payment system end to end.","full":"Vipps/MobilePay research harness: my and client modules, e2e run 09.06.2026, FastAPI + PHP bridge, HMAC webhook verification. Recurring and Report are waiting for prod. Result — a ready API cheat sheet.","stack":["FastAPI","PHP","HMAC webhooks"],"proof":"e2e run passed"},{"id":"skat_moms","name":"Moms API — VAT declarations in Skat","domain":"fintech","status":"building","year":"2026","commits":6,"one":"SOAP client for the Danish tax authority. WS-Security from scratch in Python.","full":"NemVirksomhed / RSU B2B: filing VAT declarations to Skattestyrelsen. Built WS-Security from scratch because there was no ready one for Python. Test is live, prod is waiting for an OCES3 certificate.","stack":["Python","lxml","WS-Security","SOAP"],"proof":"Test environment — live"},{"id":"regnskabhub","name":"RegnskabHub — AI accountant","domain":"fintech","status":"building","year":"2026","one":"One API on top of all Danish accounting systems.","full":"Unified API for Danish accounting systems plus an AI accountant: multi-tenant, plans and metering, 3 read adapters, sync cache, anomaly detection, enrichment via CVR. 54 tests, 20+ endpoints.","stack":["FastAPI","PostgreSQL","LLM"],"proof":"54 tests · 20+ endpoints"},{"id":"dinero","name":"Dinero (Visma) integration","domain":"fintech","status":"building","year":"2026","commits":5,"one":"148 endpoints in Danish accounting mapped.","full":"Public Dinero integration for LynBro: OAuth + personal authorisation, 148 endpoints mapped, e2e read on a live Pro organisation. Provider-agnostic layer so e-conomic can sit next to it.","stack":["Python","OAuth"],"proof":"148 endpoints · e2e on a live org."},{"id":"rykker","name":"rykkerbot.dk — payment reminders","domain":"fintech","status":"research","year":"2026","one":"Automatic rykker's under the Danish renteloven.","full":"Rykker-as-a-Service: automation of payment reminders under Danish law. Segmented rates, fees under §9b, state machine, escalation to debt collection. Danish landing page with a live calculator.","stack":["FastAPI","Python 3.12"],"proof":"Core + 37 tests"},{"id":"jakun","name":"JaKun — CRM from metadata","domain":"saas","status":"building","year":"2026","commits":55,"one":"DB schema, API, and permissions are generated from metadata.","full":"CRM platform: instead of writing tables and endpoints by hand — you describe metadata, and it generates DDL, API, and the permissions matrix. Multi-tenant SaaS + self-host in Docker. Has an MCP server so an AI agent can work with the CRM directly. 100 Python files.","stack":["FastAPI","PostgreSQL","React/TS","MCP"],"proof":"100 core files · MCP server"},{"id":"jadel","name":"JaDel — cross-posting to social networks","domain":"saas","status":"research","year":"2026","commits":29,"one":"Like Postiz/Mixpost. First — a full market reverse-engineering.","full":"Planning, AI generation, and publishing to social networks. Before code — analysis of Postiz (AGPL, 34.5k stars) and Mixpost, an API matrix for 20+ networks, pains from issues and reviews. Main conclusion: the advantage is not in code, but in passed platform audits.","stack":["FastAPI","TypeScript"],"proof":"Full market research 08.2026"},{"id":"imin","name":"imin.dk — website builder","domain":"saas","status":"building","year":"2026","commits":148,"url":"https://imin.dk","one":"Visual editor, domains, add-on modules.","full":"Multi-tenant SaaS website builder: visual editor, domain connection, add-on modules. The idea of a plugin architecture that I later took into other products.","stack":["FastAPI","MySQL","Stripe"],"proof":"148 commits"},{"id":"gyk","name":"gyk.dk — link shortener","domain":"saas","status":"building","year":"2026","commits":153,"url":"https://gyk.dk","one":"Short links + QR + click stats.","full":"Your own bit.ly: short links, QR generation, click analytics.","stack":["FastAPI","MariaDB"],"proof":"153 commits"},{"id":"ommig","name":"ommig.dk — bio pages","domain":"saas","status":"prod","year":"2026","commits":146,"url":"https://ommig.dk","one":"Linktree for Denmark, with respect for privacy.","full":"Personal bio pages on ommig.dk/username. Focus on privacy — no third-party trackers.","stack":["FastAPI async"],"proof":"146 commits · live"},{"id":"srvc","name":"srvc.dk — services marketplace","domain":"saas","status":"building","year":"2026","commits":36,"one":"Escrow, disputes, payouts, contracts.","full":"A services marketplace with a full financial cycle: escrow, dispute mechanism, payouts to contractors, contracts.","stack":["FastAPI","Alembic"],"proof":"36 commits"},{"id":"jajob","name":"jajob.dk — AI-first job platform","domain":"saas","status":"building","year":"2026","commits":59,"one":"Jobs, CVs, ATS and matching — via multilingual vector search.","full":"Employment platform for Denmark: jobs, CVs, ATS, candidate matching. Search is vector-based and multilingual, because the DK market is trilingual.","stack":["FastAPI","MySQL","vector search"],"proof":"59 commits"},{"id":"jatid","name":"jatid.dk — time tracking","domain":"saas","status":"building","year":"2026","url":"https://jatid.dk","one":"Modular time tracking for business.","full":"Time tracking platform: shifts, projects, reports. Modular architecture for different business types.","stack":["FastAPI","Alembic"],"proof":"Development"},{"id":"games","name":"Party Game OS","domain":"saas","status":"building","year":"2026","commits":55,"one":"Jackbox, my way: TV — the screen, phones — the controllers.","full":"Party game platform: a shared screen on the TV, guests' phones as controllers. 8 games, voiceover via Piper TTS.","stack":["FastAPI","vanilla JS","SQLite","Piper TTS"],"proof":"8 games · 55 commits"},{"id":"video_editor","name":"AI video pipeline without paid SaaS","domain":"ai","status":"building","year":"2026","one":"A full video workshop locally on Apple Silicon.","full":"Researched 5 open-source clusters: auto-slicing, reframe to vertical, subtitles, voice clone, dubbing, avatar/generation. Orchestrator — Claude Code with skills and MCP. Pipeline A v0.1 assembled.","stack":["ffmpeg","Whisper","MLX","Remotion"],"proof":"5-cluster research + working pipeline"},{"id":"localmind","name":"LocalMind — LLM chat on iPhone","domain":"ai","status":"building","year":"2026","one":"The model runs on the phone, not in someone else’s cloud.","full":"iOS app for local language models. Native Swift.","stack":["Swift","SwiftUI"],"proof":"Development"},{"id":"cbot","name":"cbot.dk — conversational lead-gen","domain":"bots","status":"building","year":"2026","commits":57,"url":"https://cbot.dk","one":"The bot catches a lead and drops it straight into the CRM.","full":"Conversational lead-gen: the bot runs a conversation, qualifies, hands over to CRM. Web, backend, and mobile part.","stack":["Python","JS","mobile"],"proof":"57 commits"},{"id":"whatsapp_privat","name":"WhatsApp → canonical database → AI","domain":"bots","status":"building","year":"2026","commits":30,"one":"Years of chats turned into a database that the AI talks to.","full":"Collecting my own WhatsApp accounts into a single canonical database with AI access afterwards. Architecture is SaaS-ready from day one.","stack":["FastAPI","WAHA"],"proof":"30 commits"},{"id":"uaeuhub_bots","name":"Bots for Ukrainian communities","domain":"bots","status":"prod","year":"2023–2026","one":"Moderation, broadcasts, and anti-spam for groups with thousands of people.","full":"A family of Telegram bots for Ukrainian communities in Europe: moderation, anti-spam, broadcasts, AI replies. Five iterations of one bot — honestly, they need to be merged into one.","stack":["Python","aiogram","OpenAI"],"proof":"Live groups · ~300 users in the Mini App"},{"id":"padel","name":"Padel Copenhagen game seating","domain":"bots","status":"prod","year":"2026","one":"Poll in the group → ready roster and pairs.","full":"Reads a poll in the Telegram group of the Copenhagen padel community and builds the roster and court seating.","stack":["Python","Telethon"],"proof":"Used by the community"},{"id":"sms_gateway","name":"SMS gateway","domain":"infra","status":"prod","year":"2026","commits":164,"one":"Shared notification channel for all my products.","full":"My own SMS gateway that other projects use as a notifications service.","stack":["Python"],"proof":"164 commits · additive-prod"},{"id":"aapanel","name":"Managing aaPanel servers","domain":"infra","status":"prod","year":"2026","commits":147,"one":"My VPS are managed by code, not by clicks in a panel.","full":"aaPanel automation: sites, DBs, SSL, deploy. Separately solved the painful story with the mail server — SSL via DNS-01 in Simply.com and Postfix SNI.","stack":["Python","nginx","MySQL"],"proof":"147 commits · all my servers"},{"id":"vps_monitor","name":"Claude as on-call SRE","domain":"infra","status":"research","year":"2026","one":"AI watches the servers and fixes small stuff on its own.","full":"Monitoring VPS and projects with reports in Telegram and by email, automatic safe actions plus a confirmation request for risky ones.","stack":["Claude Code","Python"],"proof":"Starter kit"},{"id":"push_projects","name":"Project promotion engine","domain":"infra","status":"building","year":"2026","one":"Automatic submission of my products to directories and forums.","full":"Promotion system: directories, API directories, GitHub, forums, email. First wave — danadresse, phone_check, soge.","stack":["Python"],"proof":"Wave 1 launched"},{"id":"ideamine","name":"ideamine — radar for ideas and pains","domain":"ai","status":"building","year":"2026","one":"24 sources of user complaints → my daily digest.","full":"Radar of product signals: HN, competitors’ feature requests on GitHub, 1–3★ reviews in App Store DK, reviews of WP plugins, RSS, Danish media. Deduplication, scoring to my profile, Ukrainian digest, and matching signals to current projects.","stack":["Python","LLM"],"proof":"24 zero-auth sources"},{"id":"freelancehunt","name":"Bypassing Cloudflare for freelancehunt","domain":"scraping","status":"prod","year":"2026","one":"API where it exists. Browser where there is no API.","full":"Auto-login and account reading: API v2 as the main channel, nodriver browser for what is not in the API — including bypassing Cloudflare.","stack":["Python","httpx","nodriver"],"proof":"Works"},{"id":"virkdata","name":"Scraping Danish business data","domain":"scraping","status":"building","year":"2026","one":"CVR and virk — into my own database.","full":"Collecting Danish business data from virk/CVR via proxy rotation. Feeds enrichment in other products.","stack":["Python","proxy"],"proof":"Feeds other projects"},{"id":"sifjakobs","name":"Shopify catalogue scraper","domain":"scraping","status":"prod","year":"2026","one":"Catalogue + AI fallback when the markup changes.","full":"Full Shopify catalogue scraper with proxy rotation and AI fallback in case the layout changes.","stack":["Python","uv"],"proof":"Working"},{"id":"offline_cph","name":"Research of offline niches in Copenhagen","domain":"business","status":"research","year":"2026","one":"Where to invest 150,000 DKK in a real business.","full":"Systematic breakdown of offline business niches in Copenhagen for a 150k DKK budget. Top candidates: skin studio, ceramics, bike service.","stack":["Research"],"proof":"Full RESEARCH.md"},{"id":"make_money","name":"Order intake system","domain":"business","status":"research","year":"2026","commits":33,"one":"11 agents analysed the Danish freelance market.","full":"Research of order channels in Denmark: brokers charge 1400–1750 DKK/hour, ApS is the access key to corporate clients, bidding platforms are dead, email aggregation is a legal channel.","stack":["Research","FastAPI"],"proof":"Research by 11 agents"},{"id":"mitid","name":"Research of MitID brokers","domain":"data","status":"research","year":"2026","one":"How to legally log Danes in via the state eID.","full":"Breakdown of MitID brokers for login via the Danish eID. Recommendation: Criipto/Idura — €67/month + €0.037 per authentication, OIDC, sandbox without a card.","stack":["OIDC","Research"],"proof":"Decision made"},{"id":"carsexport","name":"Cars Export LLC","domain":"business","status":"archive","year":"2018–2022","one":"Car export from the US and China. ×10 in three years.","full":"CEO. Car sourcing on Copart, IAAI, Manheim, ADESA, sea logistics, brokerage, customs. Routes UA/GE/USA/EU/CN. Growth of over 1000% in three years, million-level monthly turnover. This is where I learned how contracts, risk, and money really work.","stack":["Logistics","Customs","International trade"],"proof":"4 years · +1000% in 3 years"},{"id":"lintek","name":"Lintek","domain":"business","status":"archive","year":"2008–2012","one":"CEO at 25. Cable products and internet distribution, Kharkiv.","full":"Cable products, internet industry, distribution. First experience running a company.","stack":["Distribution","Sales"],"proof":"4 years"},{"id":"ltv","name":"LTV — international procurement","domain":"business","status":"archive","year":"2013–2017","one":"Bicycles and parts. Procurement across half the world.","full":"Foreign trade procurement manager: bicycles and spare parts, international trade, suppliers from Asia and Europe.","stack":["Foreign trade","Purchasing"],"proof":"5 years"},{"id":"bestchoice","name":"Best Choice","domain":"business","status":"prod","year":"2022–","url":"https://best-choice.dk","one":"Where my IT practice in Denmark began. CVR 44079844.","full":"IT services: automation, AI tools, CRM, Telegram bots, SEO, hosting. Lead Consultant & Solution Architect.","stack":["Consulting","Development","CVR 44079844"],"proof":"Active practice since 2022"},{"id":"lynbro_aps","name":"LynBro ApS","domain":"business","status":"prod","year":"2025–","url":"https://lynbro.dk","one":"Danish IT company. CVR 46321499.","full":"Lyn — lightning, speed. Bro — bridge. Websites, AI agents, automation. Registered in Rødovre.","stack":["ApS","CVR 46321499"],"proof":"Active company"},{"id":"aktive","name":"Aktive Ukrainere","domain":"community","status":"prod","year":"2023–","url":"https://aktive-ukrainere.dk","one":"Non-profit association of Ukrainians in Denmark (frivillig forening). CVR 43922939.","full":"Founder and organiser of the non-profit association: help, volunteering, meetups, AI workshops and events for Ukrainians in Denmark.","stack":["Frivillig forening","CVR 43922939"],"proof":"Regular events since 2023"},{"id":"uahub","name":"UaHub","domain":"community","status":"prod","year":"2025–","url":"https://uahub.dk","one":"Association of Ukrainian entrepreneurs and businesses (frivillig forening). CVR 45599841.","full":"Founder of the non-profit association. Workshops, forums, networking and a directory of Ukrainian businesses at uahub.dk/katalog.","stack":["Frivillig forening","CVR 45599841"],"proof":"Active association · business directory"},{"id":"oksana","name":"oksana.dk — PMU studio website","domain":"web","status":"building","year":"2026","commits":39,"url":"https://oksana.dk","one":"Website redesign for a beauty studio.","full":"Full website redesign for a permanent makeup studio: WordPress + Node part.","stack":["WordPress","Node","PHP"],"proof":"39 commits"},{"id":"msdf","name":"msdf.dk — furniture assembly","domain":"web","status":"building","year":"2026","url":"https://msdf.dk","one":"Reach #1 for furniture assembly in Copenhagen.","full":"Furniture assembly and installation service in Storkøbenhavn. SEO + lead gen, transparent fixed price with a calculator, programmatic SEO “furniture × location”, scraping JYSK and IKEA catalogues.","stack":["WordPress","pSEO"],"proof":"In progress"},{"id":"nailshub","name":"nailshub.dk — salon automation","domain":"web","status":"prod","year":"2026","commits":17,"url":"https://nailshub.dk","one":"A salon that no longer replies to bookings manually.","full":"Support and automation for a salon on WordPress + Amelia: online booking, confirmations via WhatsApp Cloud API, auto reports with AI audit, migration from the old system.","stack":["WordPress","Amelia","n8n","WhatsApp API"],"proof":"Prod · live salon"},{"id":"zhanna","name":"zhanna.dk — site for a nail artist and coach","domain":"web","status":"prod","year":"2026","url":"https://zhanna.dk","one":"Personal brand of a nail artist: five languages, booking via NailsHub.","full":"Client site by Best Choice for Zhanna Osaulenko — nail artist, coach and co-owner of NailsHub. Five languages (UK/EN/DA/PL/RU), light and dark theme, bookings go to the NailsHub salon.","stack":["Web","i18n"],"proof":"Live client site"},{"id":"bilafgift","name":"Car registration tax calculator","domain":"data","status":"building","year":"2026","one":"How much it costs to import a car to Denmark. PWA with no dependencies.","full":"Danish registreringsafgift calculator. Clean HTML/CSS/JS, works offline.","stack":["vanilla JS","PWA"],"proof":"Working"},{"id":"speed_cam","name":"Video speed radar","domain":"ai","status":"research","year":"2026","one":"A phone measures a car’s speed from video.","full":"Measuring object speed from video: YOLO + tracking + number plate recognition, scale is calculated by the number plate or wheelbase. Evidence report, ratings, sharing. Positioning — a universal tool, not only for enforcement.","stack":["YOLO","iOS/Android","FastAPI","GPU"],"proof":"Research complete"},{"id":"runners","name":"Running app with territory capture","domain":"saas","status":"research","year":"2026","one":"You run — you capture city districts.","full":"Gamified running app: territory capture, league, web part. Launch market — Copenhagen and Scandinavia.","stack":["greenfield"],"proof":"Concept"},{"id":"looi","name":"LOOI Robot BLE toolkit","domain":"ai","status":"prod","year":"2025","commits":4,"url":"https://github.com/andrey-tut/LOOI-Robot","one":"I reverse-engineered the robot’s BLE protocol. 12 stars on GitHub.","full":"Unofficial toolkit for the LOOI robot: keyboard control, sensor decoding, telemetry, Flutter companion roadmap. Public, with real stars from strangers.","stack":["Python","BLE","reverse engineering"],"proof":"12 stars · public repository"},{"id":"azbyg","name":"Chat archive as proof of work","domain":"business","status":"building","year":"2026","url":"https://azbyg.dk","one":"A work chat in Telegram → legal proof of completed work.","full":"Archive of the work chat for the client (object “Hotel Hornbek”, AZ Byg ApS): Telegram-like web viewer, PDF, DA/EN/UA translation, daily summaries with crew hours.","stack":["Python","Web","PDF"],"proof":"Viewer ready"},{"id":"hub_pools","name":"TEUH automation","domain":"community","status":"prod","year":"2026","one":"Registration, surveys, and materials — no manual work.","full":"Automation for the TEUH platform: participant registration, surveys, distribution of materials from Google Drive.","stack":["Playwright","httpx"],"proof":"Work automation"},{"id":"job_search","name":"A systematic job search as an engineering task","domain":"business","status":"prod","year":"2026","one":"Not sending out CVs, but a controlled pipeline.","full":"Job search in Hvidovre/Rødovre reduced to a system: a tracker in a spreadsheet, automatic drafts in Gmail, sources from CVR. A non-IT task solved with an IT method.","stack":["Python","Gmail API","CVR"],"proof":"Works"}]},"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":"What do you do, in short?","a":"I build systems for businesses: websites, automation, AI agents, CRM, API, and data systems. I also consult on operations and online presence. I do this through my own IT company LynBro ApS and the Best Choice practice."},{"q":"Are you a developer or an entrepreneur?","a":"Both, in that order: first twenty years in business, then code. That is the key difference. I don’t ask “what feature to build” — I ask where your margin is leaking and whether it is worth automating at all. Often the best answer is to build nothing."},{"q":"How much does it cost to make a website or automation?","a":"Honestly: before we talk — I don’t know. The range is too wide: a landing page in a week and a multi-tenant platform in half a year are different orders of magnitude. The first call is free, and after it you will get either a price range with justification, or advice not to do it."},{"q":"Do you work only in Denmark?","a":"I live in Denmark, work from anywhere. I know the Danish market deeper than others: CVR, MitID, MobilePay, DAWA, Skat, e-conomic and Dinero — I have been through this and have ready integrations."},{"q":"What languages do you speak?","a":"Ukrainian and Russian fluently, English at work level, Danish — in progress; I understand more than I speak. I can present in Ukrainian or English."},{"q":"Have you really built 62 projects?","a":"Documented — 62, and that is only what I consider worth showing. 88 repositories, 6,119 commits. Some is in production and makes money, some is in progress, some — honestly, research that did not go further. I show those too: did not work out — is also a result."},{"q":"What does working with you look like?","a":"First a conversation, where you talk more. Then a short research: what is already on the market, what we do not need to build. Then the shortest path to validating the idea, not a pretty mockup. Then — to production, with logs and monitoring. You see progress in your own dashboard, not in chats."},{"q":"Do you take small tasks?","a":"Yes, if they are really small. Fix a form, set up a Google listing, connect online booking — that is fine. Worse is when a “small task” is actually big, and the budget is set for a small one — I will say that right away."},{"q":"What do you need to invite you to speak?","a":"Date, city or online, audience, and how much time I have. I regularly speak in Copenhagen and online. Formats: a talk 20–45 minutes, a panel, a 2-hour workshop with laptops. Fee is discussed; for Ukrainian communities and volunteer events — free."},{"q":"Why does the site look like this?","a":"Because it is also a portfolio. The 3D field in the background — these are 6,119 particles, one for each of my commits. The bridge is built from frames of a generated video, which are scrubbed by scrolling. The eyes in the portrait follow the cursor. Everything is written from scratch, without builders and animation libraries. Press “/” — there is also a console."}],"speaking":{"formats":[{"t":"Talk","m":"20–45 min","d":"A classic conference or forum slot. Cases, numbers, conclusions. I leave 10 minutes for questions — usually there are more."},{"t":"Workshop","m":"2–3 hours","d":"With laptops. Each participant leaves not with notes, but with something working: a configured Google listing, a ready bot, or a first automation."},{"t":"Panel / round table","m":"per programme","d":"Moderation or participation. I work well where you need to speak concretely, not in generalities."},{"t":"Online","m":"any","d":"Zoom, Teams, Meet, stream. I have a proper microphone, light, and a backup connection — there will be no technical surprises."}],"topics":[{"t":"The client is already looking for you. Why do they choose someone else?","d":"The most popular topic. I break down 12 points where a small business loses a customer even before the first contact: Google listing, reviews, site speed, clarity of the offer, contact method, response outside working hours. Not theory — on live examples from Danish and Ukrainian businesses.","take":["A 12-point checklist you can go through the same evening","What costs zero money and gives results this week","How to measure whether it really got better"],"aud":"Small business, craftswomen, experts, beginners","len":"30–45 min · or a 2-hour workshop","lang":"UK / EN"},{"t":"AI without magic: what really works in small business","d":"No “AI will change everything”. Specifics: what tasks automation covers today, what it costs per month, where AI is more expensive than a human, and what mistakes I made myself. I show my own systems — a phone agent that speaks Ukrainian, and a knowledge base that remembers 100 projects.","take":["A list of tasks worth automating first","Real cost numbers, not marketing","Three ways to lose money on AI — and how to avoid them"],"aud":"Business owners, managers, IT communities","len":"30–45 min","lang":"UK / EN"},{"t":"Starting a business in Denmark when you don’t know anyone here","d":"My path since 2022 with no polishing: CVR and ApS, taxes, NemKonto, MitID, first clients, why Danish networking works differently, and what I would do differently. The topic people ask for most often after talks for a Ukrainian audience.","take":["A step-by-step plan for the first three months","What ApS really costs and when you need it","Where to find your first clients if you have zero contacts"],"aud":"Ukrainians in Denmark, relocators, beginners","len":"30–60 min","lang":"UK"},{"t":"From car exports to government APIs","d":"A story about how experience in “heavy” offline business — customs, contracts, sea freight — becomes an advantage in digital. Why someone who shipped cars across the ocean designs systems differently. For those changing industries and thinking their experience is wasted.","take":["What from offline experience transfers to IT one-to-one","How not to start your career from zero at 40","Why domain knowledge is more valuable than a programming language"],"aud":"Conferences, communities, career events","len":"20–40 min","lang":"UK / EN"},{"t":"Workshop: your AI assistant in 2 hours","d":"Practice. Each participant builds their own assistant for their task in two hours — customer replies, processing enquiries, content preparation. We work with free tools, so after the workshop nothing breaks because of a subscription.","take":["A working assistant on your own data","Understanding where the boundary of what’s possible is","What to do next on your own"],"aud":"Up to 25 people with laptops","len":"2–3 hours","lang":"UK / EN"}],"past":[{"y":"2026","e":"Woman's Day Copenhagen","d":"Beauty Business block — small business online presence"},{"y":"2026","e":"TechBBQ","d":"Networking campaign: 5,290 messages, ~300 conversations"},{"y":"2026","e":"Business and taxes in Denmark","d":"Roundtable for Ukrainian entrepreneurs"},{"y":"2025","e":"EU Funding & Mentorship","d":"Zoku Copenhagen"},{"y":"2025","e":"Blockchain Impact Forum","d":"Copenhagen"},{"y":"2023–","e":"Aktive Ukrainere AI workshops","d":"Regular hands-on meetups that I organise myself"}],"bioShort":"Andrii Lynnyk — Ukrainian entrepreneur and IT practitioner in Denmark. Founder of the IT company LynBro ApS and of two non-profit associations — Aktive Ukrainere and UaHub. In business since 1996: from cable distribution and exporting cars from US auctions to AI agents and public data APIs. 62 documented projects, 88 repositories.","bioOneLine":"Andrii Lynnyk — entrepreneur, IT practitioner, and founder of LynBro ApS (Denmark). Builds systems that work without him."},"now":{"updated":"2026-09-14","intro":"This page is about what I’m actually busy with now, not what I can do in general. I update it when priorities change. Idea borrowed from nownownow.com.","blocks":[{"t":"What I work on","items":[{"n":"ferrysport.dk","d":"Reviving a Danish workwear webshop. An 8-step plan, the client sees progress in their own dashboard. Done: spam registrations 92→16, dead product variations, speed, mailing. Next — online payments and product descriptions."},{"n":"RegnskabHub","d":"A unified API for Danish accounting systems plus an AI accountant. 54 tests, 20+ endpoints, three read adapters. The most ambitious of the current ones."},{"n":"ideamine","d":"A radar for product ideas: 24 sources of user complaints and requests, dedup, scoring for my profile, a daily digest in Ukrainian."},{"n":"soge.dk і danadresse.dk","d":"Danish data APIs: transport, companies, addresses. 130+ million records. Public APIs and MCP servers so AI agents can use them."},{"n":"andrii.dk","d":"This site. Rewritten from scratch in September 2026: a WebGL field with 6,119 particles, scroll-scrubbed video, a catalogue of 62 projects."}]},{"t":"What I study","items":[{"n":"Real-time voice AI agents","d":"After launching an AI PBX, I’m digging deeper: interruptions, latency, behaviour in noise, when the agent should give up and call a human."},{"n":"Danish","d":"I read more fluently than I speak. Goal for the year — do a talk in Danish without switching to English."},{"n":"Local models on Apple Silicon","d":"How much useful work you can do without the cloud: MLX, quantisation, quality limits."}]},{"t":"What I look for","items":[{"n":"One–two client projects in Q4","d":"I do best with tasks that have real operations: accounting, logistics, client booking, data. I don’t like “make it look nice for us”."},{"n":"Talks and workshops","d":"Especially where the audience is small business owners, not fellow developers."},{"n":"Partners for Danish data products","d":"If you work with CVR, DMR, addresses, or telephony in Denmark — write to me, we probably have something to exchange."}]},{"t":"Outside work","items":[{"n":"Two kids","d":"Main project, deadline — my whole life."},{"n":"Tennis, squash, padel","d":"Copenhagen padel community; my own bot makes the game rosters."},{"n":"3D printing and a laser engraving machine","d":"Garage as a second server. If a thing can be taken apart — I will take it apart."}]}]},"checklist":{"slug":"cheklist","title":"12 points where a small business loses customers online","subtitle":"A practical checklist. You can go through it in an evening, most items cost zero money.","intro":"This is not theory from a course. This is what I see in every second business that comes to me — in Denmark and beyond. The customer is already looking for you. The only question is at which step they go to someone else.\n\nGo from top to bottom. Next to each point there is a two-minute check — do it right away, do not put it off. If the point is green, move on. If not — it says right there what to do.","howto":"Give yourself an honest score for each point: ✓ works · ~ half-way · ✗ not there. Count the crosses. Seven or more — you are losing most of those who had already been looking for you.","points":[{"n":1,"t":"You are not on Google Maps","sym":"A person searches for “[your service] + city”, sees competitors in the top map cards, you — nowhere.","check":"Open google.com in incognito mode and type your business name. Then — the service and the city. Do you see yourself in the first three map cards?","fix":"Create a Google Business Profile and verify it by postcard or video. It’s free and gives the biggest increase in calls out of everything on this list.","cost":"0 kr · 30 min + 2 weeks for verification"},{"n":2,"t":"The listing exists, but it’s empty","sym":"Wrong category, no opening hours, one photo — and it’s blurry. Google shows such a listing lower down.","check":"Open your listing and count: category, hours, phone, website, service area, description, 10+ photos. How many of the seven are filled in?","fix":"Fill in everything. Photos are not stock: your workplace, the result of your work, you. Google prefers listings that get updated.","cost":"0 kr · 1 hour"},{"n":3,"t":"No reviews — or they are left without a reply","sym":"Zero reviews reads as “zero customers”. A review without a reply is like “the owner doesn’t care”.","check":"How many reviews do you have, and how many of them did you reply to? The negative ones too?","fix":"Ask ten happy customers — personally, with a direct link. Reply to every review, including a bad one: a future customer reads your reply, not the complaint.","cost":"0 kr · 15 min per week, continuously"},{"n":4,"t":"The site takes more than three seconds to open","sym":"Half the people on a phone leave before they see the first screen. You pay for ads that never arrive.","check":"pagespeed.web.dev, paste the address, look at the mobile tab. LCP over 2.5 seconds — red zone.","fix":"Most common culprits: heavy photos without compression, a dozen plugins, fonts from someone else’s domain, a slider on the homepage. Remove the slider, compress the photos, disable what you don’t use.","cost":"0–2,000 kr · depending on how bad things are"},{"n":5,"t":"The site is awkward to use on a phone","sym":"Text is small, buttons miss the finger, the menu hides, the form doesn’t fit on the screen.","check":"Open your site on your own phone and try to do what you want the customer to do — book, order, call. How many taps did it take?","fix":"More than three taps to the goal is already too much. The main button must be on the first screen and not disappear when scrolling.","cost":"0 kr to check · then it depends"},{"n":6,"t":"In five seconds it’s unclear what you sell","sym":"On the first screen there is “Welcome” and a nice picture. What exactly, for whom, and how much it costs — somewhere below or nowhere.","check":"Show the first screen to someone who doesn’t know your business. Ask: what do they sell here, and to whom? If they can’t guess in five seconds — rewrite it.","fix":"The formula without decorations: what you do + for whom + in which city + what to do next. “We assemble furniture in Copenhagen for a fixed price. Calculate the cost”.","cost":"0 kr · half an hour and honesty"},{"n":7,"t":"There is no price anywhere, even an approximate one","sym":"“Price on request” reads as “expensive” or “depends on how rich you are”. Most people simply won’t write.","check":"Can a person find out at least the order of magnitude, without talking to you?","fix":"Give a range, examples of typical jobs with amounts, or a calculator. Price filters out those who would not buy anyway — and your time is freed up for the rest.","cost":"0 kr · but it requires a decision"},{"n":8,"t":"You can only get in touch via the form","sym":"A person wants to message in a messenger, but instead has to fill in fields and wait. They just close the tab.","check":"Count the channels on the site: a phone number you can dial with one tap, messenger, email, form. Fewer than three is too little.","fix":"Add what your audience actually uses. In Denmark this is often phone and email, in the Ukrainian community — Telegram and WhatsApp. Put the buttons where they are visible on a phone.","cost":"0 kr · hour"},{"n":9,"t":"Outside working hours nobody replies","sym":"Most enquiries come in the evening and at weekends. A reply on Monday at ten — that is already a different contractor.","check":"Check your email or phone to see what time enquiries come in. How many of them got a reply the same day?","fix":"Minimum — an auto-reply that honestly says when there will be an answer, and gives something useful already now. Maximum — a simple bot or AI assistant that answers common questions and collects a contact.","cost":"0 kr for an auto-reply · bot — from a few thousand"},{"n":10,"t":"No online booking","sym":"To book, you have to call. Some people do not like calling at all — and that is not a small share.","check":"Can a client choose a time and book on their own, without you, at ten in the evening?","fix":"Set up any online booking system and put the link in your Google listing, in your profile, and on the site. It works when you are asleep.","cost":"0–300 kr per month"},{"n":11,"t":"No proof that you can actually do it","sym":"There are texts about “individual approach” and “quality”, but there are no photos of work, cases, or “before/after”.","check":"Find three concrete proofs on your site: a photo of your work, a client story with a number, a review with a name. Found them?","fix":"Photograph every job, even on a phone. One case per month — that is twelve per year. A number in a case matters more than any epithet.","cost":"0 kr · discipline"},{"n":12,"t":"The client left — and you do nothing else","sym":"A person looked, did not write, left forever. You have to buy a new one with money again.","check":"Do you have at least one way to bring the person back: an email list, a channel, retargeting, reminders to repeat clients?","fix":"Start with the simplest — collect emails in exchange for something useful and write once a month. The second-simplest step — reminders to those who have already bought: selling to them is ten times cheaper than to new ones.","cost":"0 kr to start"}],"after":{"t":"Counted the crosses?","items":[{"r":"0–2 ✗","d":"You are doing fine. Work on point 12 — repeat customers, that is where the most money is."},{"r":"3–6 ✗","d":"A typical situation. Pick three cheapest points from the list and close them this week — you will see the result in a month."},{"r":"7+ ✗","d":"You pay for ads and networking, and then lose people on the doorstep. Start with points 1, 2, 6 — they are free and give the most."}]},"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": "/about/", "projects": "/projects/", "speaking": "/speaking/", "now": "/now/", "checklist": "/checklist/", "quiz": "/uk/womans-day/", "ommig": "https://ommig.dk/andrii/en", "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 = '
' + esc(t.d) + '
' + '' + esc(f.d) + '