fix: Rename pkd_codes column to ceidg_pkd_list to avoid backref conflict

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-01 07:22:32 +01:00
parent 9f2b261df2
commit 448937dabd
33 changed files with 4465 additions and 6 deletions

0
.mcp.json Normal file
View File

13
blueprints/CLAUDE.md Normal file
View File

@ -0,0 +1,13 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
### Jan 31, 2026
| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #248 | 6:33 PM | 🔵 | Nordabiz register_blueprints() function implements phased migration with 407 lines of endpoint aliasing for backward compatibility | ~1102 |
| #180 | 6:25 PM | 🔵 | Nordabiz project architecture analyzed revealing 16+ Flask blueprints with modular organization | ~831 |
| #166 | 6:23 PM | 🔵 | Nordabiz implements phased blueprint registration with backward-compatible endpoint aliases | ~801 |
</claude-mem-context>

View File

@ -0,0 +1,14 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
### Jan 31, 2026
| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #250 | 6:33 PM | 🔵 | Nordabiz admin blueprint imports 14 separate routes modules demonstrating extreme modularization | ~677 |
| #180 | 6:25 PM | 🔵 | Nordabiz project architecture analyzed revealing 16+ Flask blueprints with modular organization | ~831 |
| #170 | 6:23 PM | 🔵 | Nordabiz admin routes handle recommendations moderation with Polish localization | ~713 |
| #168 | " | 🔵 | Nordabiz admin blueprint splits functionality across 15 route modules | ~726 |
</claude-mem-context>

12
blueprints/api/CLAUDE.md Normal file
View File

@ -0,0 +1,12 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
### Jan 31, 2026
| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #245 | 6:33 PM | 🔵 | Nordabiz API blueprint aggregates 7 specialized route modules for frontend interactions | ~574 |
| #180 | 6:25 PM | 🔵 | Nordabiz project architecture analyzed revealing 16+ Flask blueprints with modular organization | ~831 |
</claude-mem-context>

View File

@ -0,0 +1,7 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
*No recent activity*
</claude-mem-context>

View File

@ -0,0 +1,12 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
### Jan 31, 2026
| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #180 | 6:25 PM | 🔵 | Nordabiz project architecture analyzed revealing 16+ Flask blueprints with modular organization | ~831 |
| #169 | 6:23 PM | 🔵 | Nordabiz public blueprint organized into three route modules for homepage, ZOPK integration, and announcements | ~551 |
</claude-mem-context>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,18 @@
[
{
"company_id": 119,
"company_name": "3W",
"search_query": "3W",
"found_nip": null,
"found_regon": null,
"found_name": null,
"found_owner": null,
"found_address": null,
"found_status": null,
"matches": [],
"confidence": "low",
"verified": false,
"error": "Timeout",
"searched_at": "2026-02-01T07:07:32.763556"
}
]

View File

@ -0,0 +1,18 @@
[
{
"company_id": 95,
"company_name": "Kancelaria Notarialna Henryk Mizak",
"search_query": "Kancelaria Notarialna Henryk Mizak",
"found_nip": null,
"found_regon": null,
"found_name": null,
"found_owner": null,
"found_address": null,
"found_status": null,
"matches": [],
"confidence": "low",
"verified": false,
"error": "Nie znaleziono pola wyszukiwania. Screenshot: /Users/maciejpi/claude/projects/active/nordabiz/data/ceidg_search_results/ceidg_debug_95.png",
"searched_at": "2026-02-01T07:12:10.623430"
}
]

View File

@ -0,0 +1,19 @@
[
{
"company_id": 113,
"company_name": "Piotrex",
"domain": "piotrex.info",
"url_scanned": "https://piotrex.info/kontakt",
"nip_found": "5881846715",
"regon_found": null,
"nips_all": [
"5881846715"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T06:59:00.521605"
}
]

View File

@ -0,0 +1,331 @@
[
{
"company_id": 119,
"company_name": "3W",
"domain": "3wdb.pl",
"url_scanned": "https://3wdb.pl/o-nas",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:12.777529"
},
{
"company_id": 121,
"company_name": "BIS Maszyny",
"domain": "bis-bau.pl",
"url_scanned": "https://bis-bau.pl/o-nas",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:17.025257"
},
{
"company_id": 93,
"company_name": "Dom Dziecka Pro-Sport",
"domain": "prosport.wejher.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:24.231589"
},
{
"company_id": 122,
"company_name": "Elgreen EM",
"domain": "elgreen.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:26.087690"
},
{
"company_id": 117,
"company_name": "Elzit",
"domain": "elzit.pl",
"url_scanned": "https://elzit.pl",
"nip_found": "9581160890",
"regon_found": "191427842",
"nips_all": [
"9581160890"
],
"regons_all": [
"191427842"
],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T06:59:27.702428"
},
{
"company_id": 94,
"company_name": "Event Investycje",
"domain": "bombera.pl",
"url_scanned": "https://bombera.pl",
"nip_found": "5892024537",
"regon_found": null,
"nips_all": [
"5892024537"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T06:59:30.031341"
},
{
"company_id": 112,
"company_name": "Informatyk1",
"domain": "informatyk1.pl",
"url_scanned": "https://informatyk1.pl/about",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:32.687646"
},
{
"company_id": 123,
"company_name": "Iwona Spaleniak Coaching",
"domain": "iwonaspaleniak.pl",
"url_scanned": "https://iwonaspaleniak.pl/about",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:37.620806"
},
{
"company_id": 114,
"company_name": "Kancelaria Ostrowski i Wspólnicy",
"domain": "ostrowski-legal.net",
"url_scanned": "https://ostrowski-legal.net",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:48.733603"
},
{
"company_id": 124,
"company_name": "Kaszubia 2030",
"domain": "kaszubia2030.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:51.434997"
},
{
"company_id": 111,
"company_name": "Nowatel",
"domain": "nowatel.com",
"url_scanned": "https://nowatel.com",
"nip_found": "5932407062",
"regon_found": null,
"nips_all": [
"5932407062"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T06:59:52.617348"
},
{
"company_id": 110,
"company_name": "Omega Energy",
"domain": "omega-energy.eu",
"url_scanned": "https://www.omega-energy.eu",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:53.838239"
},
{
"company_id": 96,
"company_name": "Orlex Design",
"domain": "orlexbeton.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T06:59:57.143298"
},
{
"company_id": 113,
"company_name": "Piotrex",
"domain": "piotrex.info",
"url_scanned": "https://piotrex.info/kontakt",
"nip_found": "5881846715",
"regon_found": null,
"nips_all": [
"5881846715"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T06:59:58.758426"
},
{
"company_id": 98,
"company_name": "Podróże i My",
"domain": "itakarumia.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:02.539004"
},
{
"company_id": 100,
"company_name": "PZU TFI",
"domain": "pzu.pl",
"url_scanned": "https://www.pzu.pl",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:04.298279"
},
{
"company_id": 116,
"company_name": "Renk Hurtownie",
"domain": "renk.pl",
"url_scanned": "https://renk.pl/kontakt",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:08.159123"
},
{
"company_id": 101,
"company_name": "Rozsądni Bracia",
"domain": "rozsandnibracia.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:14.494471"
},
{
"company_id": 102,
"company_name": "TERMO",
"domain": "termocenter.pl",
"url_scanned": "https://termocenter.pl/about",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:15.685339"
},
{
"company_id": 109,
"company_name": "Unimot",
"domain": "unimot-eig.pl",
"url_scanned": "https://unimot-eig.pl",
"nip_found": "9730421440",
"regon_found": "970619205",
"nips_all": [
"9730421440"
],
"regons_all": [
"970619205"
],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T07:00:24.581291"
},
{
"company_id": 118,
"company_name": "Your Welcome",
"domain": "yourewelcome.pl",
"url_scanned": "https://yourewelcome.pl/o-nas",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:26.083523"
}
]

View File

@ -0,0 +1,331 @@
[
{
"company_id": 119,
"company_name": "3W",
"domain": "3wdb.pl",
"url_scanned": "https://3wdb.pl/o-nas",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:40.822770"
},
{
"company_id": 121,
"company_name": "BIS Maszyny",
"domain": "bis-bau.pl",
"url_scanned": "https://bis-bau.pl/o-nas",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:44.927095"
},
{
"company_id": 93,
"company_name": "Dom Dziecka Pro-Sport",
"domain": "prosport.wejher.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:52.609620"
},
{
"company_id": 122,
"company_name": "Elgreen EM",
"domain": "elgreen.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:00:54.221059"
},
{
"company_id": 117,
"company_name": "Elzit",
"domain": "elzit.pl",
"url_scanned": "https://elzit.pl",
"nip_found": "9581160890",
"regon_found": "191427842",
"nips_all": [
"9581160890"
],
"regons_all": [
"191427842"
],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T07:00:55.749843"
},
{
"company_id": 94,
"company_name": "Event Investycje",
"domain": "bombera.pl",
"url_scanned": "https://bombera.pl",
"nip_found": "5892024537",
"regon_found": null,
"nips_all": [
"5892024537"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T07:00:58.050212"
},
{
"company_id": 112,
"company_name": "Informatyk1",
"domain": "informatyk1.pl",
"url_scanned": "https://informatyk1.pl/about",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:00.371984"
},
{
"company_id": 123,
"company_name": "Iwona Spaleniak Coaching",
"domain": "iwonaspaleniak.pl",
"url_scanned": "https://iwonaspaleniak.pl/about",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:05.234668"
},
{
"company_id": 114,
"company_name": "Kancelaria Ostrowski i Wspólnicy",
"domain": "ostrowski-legal.net",
"url_scanned": "https://ostrowski-legal.net",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:16.190843"
},
{
"company_id": 124,
"company_name": "Kaszubia 2030",
"domain": "kaszubia2030.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:18.672999"
},
{
"company_id": 111,
"company_name": "Nowatel",
"domain": "nowatel.com",
"url_scanned": "https://nowatel.com",
"nip_found": "5932407062",
"regon_found": null,
"nips_all": [
"5932407062"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T07:01:19.699237"
},
{
"company_id": 110,
"company_name": "Omega Energy",
"domain": "omega-energy.eu",
"url_scanned": "https://www.omega-energy.eu",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:20.870985"
},
{
"company_id": 96,
"company_name": "Orlex Design",
"domain": "orlexbeton.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:24.345116"
},
{
"company_id": 113,
"company_name": "Piotrex",
"domain": "piotrex.info",
"url_scanned": "https://piotrex.info/kontakt",
"nip_found": "5881846715",
"regon_found": null,
"nips_all": [
"5881846715"
],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T07:01:25.806602"
},
{
"company_id": 98,
"company_name": "Podróże i My",
"domain": "itakarumia.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:29.373428"
},
{
"company_id": 100,
"company_name": "PZU TFI",
"domain": "pzu.pl",
"url_scanned": "https://www.pzu.pl",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:30.817174"
},
{
"company_id": 116,
"company_name": "Renk Hurtownie",
"domain": "renk.pl",
"url_scanned": "https://renk.pl/kontakt",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:34.485235"
},
{
"company_id": 101,
"company_name": "Rozsądni Bracia",
"domain": "rozsandnibracia.pl",
"url_scanned": "",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:40.789330"
},
{
"company_id": 102,
"company_name": "TERMO",
"domain": "termocenter.pl",
"url_scanned": "https://termocenter.pl/about",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:41.822554"
},
{
"company_id": 109,
"company_name": "Unimot",
"domain": "unimot-eig.pl",
"url_scanned": "https://unimot-eig.pl",
"nip_found": "9730421440",
"regon_found": "970619205",
"nips_all": [
"9730421440"
],
"regons_all": [
"970619205"
],
"phone_found": null,
"address_found": null,
"confidence": "high",
"error": null,
"scanned_at": "2026-02-01T07:01:47.902680"
},
{
"company_id": 118,
"company_name": "Your Welcome",
"domain": "yourewelcome.pl",
"url_scanned": "https://yourewelcome.pl/o-nas",
"nip_found": null,
"regon_found": null,
"nips_all": [],
"regons_all": [],
"phone_found": null,
"address_found": null,
"confidence": "low",
"error": "NIP/REGON not found on website",
"scanned_at": "2026-02-01T07:01:49.260663"
}
]

View File

@ -674,7 +674,7 @@ class Company(Base):
# PKD (kod działalności gospodarczej) - z CEIDG
pkd_code = Column(String(10)) # np. "6201Z" (główny PKD)
pkd_description = Column(Text) # np. "Działalność związana z oprogramowaniem"
pkd_codes = Column(PG_JSONB, default=[]) # Wszystkie PKD jako [{kod, nazwa}]
ceidg_pkd_list = Column(PG_JSONB, default=[]) # Wszystkie PKD z CEIDG jako [{kod, nazwa}]
# Data rozpoczęcia działalności - z CEIDG
business_start_date = Column(Date) # np. 2021-02-10

View File

@ -14,8 +14,8 @@ ALTER TABLE companies ADD COLUMN IF NOT EXISTS correspondence_street VARCHAR(255
ALTER TABLE companies ADD COLUMN IF NOT EXISTS correspondence_city VARCHAR(100);
ALTER TABLE companies ADD COLUMN IF NOT EXISTS correspondence_postal VARCHAR(10);
-- PKD - wszystkie kody (JSONB array)
ALTER TABLE companies ADD COLUMN IF NOT EXISTS pkd_codes JSONB DEFAULT '[]';
-- PKD - wszystkie kody z CEIDG (JSONB array)
ALTER TABLE companies ADD COLUMN IF NOT EXISTS ceidg_pkd_list JSONB DEFAULT '[]';
-- Obywatelstwa właściciela (JSONB array)
ALTER TABLE companies ADD COLUMN IF NOT EXISTS owner_citizenships JSONB DEFAULT '[]';
@ -32,7 +32,7 @@ CREATE INDEX IF NOT EXISTS idx_companies_ceidg_id ON companies(ceidg_id);
-- Komentarze
COMMENT ON COLUMN companies.ceidg_id IS 'GUID firmy w rejestrze CEIDG';
COMMENT ON COLUMN companies.ceidg_status IS 'Status z CEIDG: AKTYWNY, ZAWIESZONY, WYKREŚLONY';
COMMENT ON COLUMN companies.pkd_codes IS 'Wszystkie kody PKD jako JSON array [{kod, nazwa}]';
COMMENT ON COLUMN companies.ceidg_pkd_list IS 'Wszystkie kody PKD z CEIDG jako JSON array [{kod, nazwa}]';
COMMENT ON COLUMN companies.ceidg_raw_data IS 'Pełna odpowiedź z API CEIDG (JSON)';
COMMENT ON COLUMN companies.ceidg_fetched_at IS 'Data ostatniego pobrania danych z CEIDG';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
# NordaBiz UI Redesign Changelog
## Cel projektu
Zmiana wyglądu nordabiznes.pl na zgodny z norda-biznes.info, zachowując wszystkie funkcjonalności.
---
## PRZED ZMIANAMI (Produkcja - 30.01.2026)
**Screenshot ID:** ss_7892h2ggn
### Obecny stan:
- **Czcionka:** Inter
- **Primary color:** #2563eb (jasny niebieski)
- **Tło:** #f8fafc
- **Event banner:** pomarańczowy (#f59e0b)
- **NordaGPT banner:** fioletowy (#7c3aed)
- **Karty firm:** cień (box-shadow), hover z translateY
- **Przyciski:** border-radius 8px (symetryczny)
---
## SPRINT 1: Fundamenty (Commit: de46e12)
**Screenshot ID:** ss_3438lwagj
### Zmiany:
1. **Czcionka:** Inter → **Poppins**
2. **Primary color:** #2563eb**#2E4872**
3. **Primary dark:** #1e40af**#1e3050**
4. **Primary light:** #3b82f6**#4a6999**
5. **Tło (background):** #f8fafc**#EDF0F5**
6. **Border color:** #e2e8f0**#e0e4eb**
7. **Border-radius przycisków:** 0.5rem → **12px 4px 12px 4px** (asymetryczny)
8. **Header:** usunięcie border-bottom, cień `0 2px 10px rgba(46, 72, 114, 0.08)`
9. **Footer:** background #1e293b**#2E4872**, padding 60px/30px
### Pliki zmienione:
- `templates/base.html`
---
## SPRINT 2: Bannery i karty firm (Commit: 1da42f2)
**Screenshot ID:** ss_29596hr83
### Zmiany:
1. **Event banner:** #f59e0b (pomarańczowy) → **#2E4872** (niebieski)
2. **NordaGPT banner:** #7c3aed (fioletowy) → **#2E4872** (niebieski)
3. **Karty firm:** usunięcie box-shadow, dodanie `border: 1px solid #e0e4eb`
4. **Karty firm hover:** `border-color: var(--primary)` zamiast translateY
5. **Tagi kategorii:**
- background: primary (#2E4872)
- color: white
- font-size: 11px
- text-transform: uppercase
- letter-spacing: 0.5px
6. **Przyciski w bannerach:** asymetryczny border-radius
### Pliki zmienione:
- `templates/index.html`
---
## SPRINT 3: Strona chatu NordaGPT (Commit: b680e3f)
**Screenshot ID:** ss_6281tcv2v
### Zmiany:
1. **Chat header:** #7c3aed/#5b21b6**#2E4872/#1e3050**
2. **Sidebar "Nowa rozmowa" button:** fioletowy → niebieski
3. **Message avatars (AI):** fioletowy gradient → niebieski gradient
4. **Input focus:** border-color i box-shadow na niebieski
5. **Send button:** fioletowy gradient → niebieski gradient
6. **Typing indicator dots:** #7c3aed#2E4872
7. **Suggestion chips hover:** fioletowy → niebieski
8. **Thinking toggle:** fioletowe akcenty → niebieskie
9. **Fluent CSS:** wszystkie zmienne primary na NordaBiz blue
### Pliki zmienione:
- `templates/chat.html`
- `static/css/fluent-nordabiz.css`
---
## SPRINT 6: Karty firm i tagi (Commit: 64583b6)
**Screenshot ID:** ss_sprint6
### Zmiany:
1. **Karty firm border-radius:** `8px``0` (ostre rogi jak na źródle)
2. **Karty firm border:** `#e0e4eb``#E4E4E4`
3. **Tagi kategorii background:** niebieski primary → `#EDF0F5` (szary)
4. **Tagi kategorii color:** biały → `#464646` (ciemny)
5. **Text primary:** `#1e293b``#303030`
6. **Text secondary:** `#64748b``#464646`
### Pliki zmienione:
- `templates/base.html`
- `templates/index.html`
---
## SPRINT 5: Landing page (Commit: ede9d09)
**Screenshot ID:** ss_sprint5
### Zmiany:
1. **Hero gradient:** `#1e40af/#2563eb/#3b82f6``#1e3050/#2E4872/#4a6999`
2. **Accent color:** `#2563eb``#2E4872`
3. **Hero section:** `data-animate="fadeIn"`
4. **Stats section:** `data-animate="fadeInUp"`
5. **CTA section:** `data-animate="fadeInUp"`
### Pliki zmienione:
- `templates/landing.html`
---
## SPRINT 4: Animacje scroll (Commit: 6e1c46e)
**Screenshot ID:** ss_sprint4
### Zmiany:
1. **Nowy plik:** `static/js/scroll-animations.js` z IntersectionObserver
2. **CSS keyframes:** fadeIn, fadeInUp, fadeInLeft, fadeInRight, scaleIn
3. **Event banner:** `data-animate="fadeIn"`
4. **Chat banner:** `data-animate="fadeIn"`
5. **Search section:** `data-animate="fadeIn"`
6. **Karty firm:** `data-animate="fadeInUp"` z delay stagger (1-6)
7. **Accessibility:** `prefers-reduced-motion` wsparcie
### Pliki zmienione:
- `templates/base.html`
- `templates/index.html`
- `static/js/scroll-animations.js` (NOWY)
---
## PODSUMOWANIE ZMIAN
| Element | PRZED | PO |
|---------|-------|-----|
| Czcionka | Inter | **Poppins** |
| Primary | #2563eb | **#2E4872** |
| Tło | #f8fafc | **#EDF0F5** |
| Border-radius btn | 8px | **12px 4px** |
| Event banner | #f59e0b | **#2E4872** |
| NordaGPT | #7c3aed | **#2E4872** |
| Karty firm | box-shadow | **border** |
| Tagi | szare, małe | **uppercase, primary** |
| Footer | #1e293b | **#2E4872** |
| Animacje scroll | brak | **fadeIn + IntersectionObserver** |
| Landing hero | #1e40af/#2563eb | **#1e3050/#2E4872** |
| Karty firm radius | 8px | **0 (ostre rogi)** |
| Tagi kategorii | niebieski bg | **szary (#EDF0F5)** |
| Tekst body | #1e293b | **#303030** |
---
## Screenshot IDs (do osadzenia w HTML)
1. `ss_7892h2ggn` - PRZED zmianami
2. `ss_3438lwagj` - Po Sprint 1
3. `ss_29596hr83` - Po Sprint 2
4. `ss_6281tcv2v` - Po Sprint 3 (chat)
5. `ss_sprint4` - Po Sprint 4 (animacje)
6. `ss_sprint5` - Po Sprint 5 (landing page)
7. `ss_sprint6` - Po Sprint 6 (karty firm, tagi)
---
Data dokumentacji: 2026-01-30

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

View File

@ -0,0 +1,722 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NordaBiz UI Redesign - Porównanie zmian</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--old-primary: #2563eb;
--new-primary: #2E4872;
--old-bg: #f8fafc;
--new-bg: #EDF0F5;
}
body {
font-family: 'Poppins', sans-serif;
background: #f5f5f5;
color: #1e293b;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
header {
background: linear-gradient(135deg, #1e3050 0%, #2E4872 100%);
color: white;
padding: 3rem 2rem;
text-align: center;
margin-bottom: 3rem;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
header p {
opacity: 0.9;
font-size: 1.1rem;
}
.sprint-section {
background: white;
border-radius: 12px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}
.sprint-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 2px solid #EDF0F5;
}
.sprint-badge {
background: linear-gradient(135deg, #1e3050 0%, #2E4872 100%);
color: white;
padding: 0.5rem 1rem;
border-radius: 12px 4px 12px 4px;
font-weight: 600;
font-size: 0.9rem;
}
.sprint-badge.before {
background: linear-gradient(135deg, #64748b 0%, #475569 100%);
}
.sprint-title {
font-size: 1.5rem;
font-weight: 600;
}
.commit-hash {
font-family: monospace;
background: #EDF0F5;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.85rem;
color: #64748b;
}
.changes-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.change-card {
background: #f8fafc;
border-radius: 8px;
padding: 1rem;
border-left: 4px solid var(--new-primary);
}
.change-card h4 {
font-size: 1rem;
margin-bottom: 0.5rem;
color: var(--new-primary);
}
.change-value {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
}
.old-value {
color: #ef4444;
text-decoration: line-through;
}
.arrow {
color: #64748b;
}
.new-value {
color: #10b981;
font-weight: 600;
}
.color-preview {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 4px;
vertical-align: middle;
border: 1px solid rgba(0,0,0,0.1);
}
.files-changed {
background: #EDF0F5;
padding: 1rem;
border-radius: 8px;
margin-top: 1rem;
}
.files-changed h4 {
font-size: 0.9rem;
color: #64748b;
margin-bottom: 0.5rem;
}
.files-changed code {
display: block;
font-family: monospace;
font-size: 0.85rem;
color: var(--new-primary);
padding: 0.25rem 0;
}
.screenshot-container {
margin-top: 1.5rem;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.screenshot-container img {
width: 100%;
height: auto;
display: block;
}
.screenshot-label {
background: #EDF0F5;
padding: 0.75rem 1rem;
font-size: 0.85rem;
color: #64748b;
text-align: center;
}
.summary-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
.summary-table th,
.summary-table td {
padding: 0.75rem 1rem;
text-align: left;
border-bottom: 1px solid #e2e8f0;
}
.summary-table th {
background: #EDF0F5;
font-weight: 600;
color: var(--new-primary);
}
.summary-table tr:hover {
background: #f8fafc;
}
footer {
text-align: center;
padding: 2rem;
color: #64748b;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
header h1 {
font-size: 1.75rem;
}
.changes-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<header>
<h1>NordaBiz UI Redesign</h1>
<p>Dokumentacja zmian graficznych: norda-biznes.info → nordabiznes.pl</p>
<p style="margin-top: 0.5rem; font-size: 0.9rem; opacity: 0.7;">Data: 30 stycznia 2026</p>
</header>
<div class="container">
<!-- PRZED ZMIANAMI -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge before">PRZED</span>
<h2 class="sprint-title">Stan wyjściowy (Produkcja)</h2>
</div>
<div class="changes-grid">
<div class="change-card">
<h4>Czcionka</h4>
<div class="change-value">Inter (Google Fonts)</div>
</div>
<div class="change-card">
<h4>Kolor primary</h4>
<div class="change-value">
<span class="color-preview" style="background: #2563eb;"></span>
#2563eb (jasny niebieski)
</div>
</div>
<div class="change-card">
<h4>Tło strony</h4>
<div class="change-value">
<span class="color-preview" style="background: #f8fafc;"></span>
#f8fafc
</div>
</div>
<div class="change-card">
<h4>Event banner</h4>
<div class="change-value">
<span class="color-preview" style="background: #f59e0b;"></span>
#f59e0b (pomaranczowy)
</div>
</div>
<div class="change-card">
<h4>NordaGPT banner</h4>
<div class="change-value">
<span class="color-preview" style="background: #7c3aed;"></span>
#7c3aed (fioletowy)
</div>
</div>
<div class="change-card">
<h4>Przyciski border-radius</h4>
<div class="change-value">8px (symetryczny)</div>
</div>
</div>
<div class="screenshot-container">
<img src="screenshots/screenshot-0-PRZED.png" alt="Screenshot przed zmianami">
<div class="screenshot-label">Stan przed zmianami (commit d9f32b7)</div>
</div>
</section>
<!-- SPRINT 1 -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge">SPRINT 1</span>
<h2 class="sprint-title">Fundamenty</h2>
<span class="commit-hash">de46e12</span>
</div>
<div class="changes-grid">
<div class="change-card">
<h4>Czcionka</h4>
<div class="change-value">
<span class="old-value">Inter</span>
<span class="arrow"></span>
<span class="new-value">Poppins</span>
</div>
</div>
<div class="change-card">
<h4>Kolor primary</h4>
<div class="change-value">
<span class="color-preview" style="background: #2563eb;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #2E4872;"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
<div class="change-card">
<h4>Tło strony</h4>
<div class="change-value">
<span class="color-preview" style="background: #f8fafc;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #EDF0F5;"></span>
<span class="new-value">#EDF0F5</span>
</div>
</div>
<div class="change-card">
<h4>Border-radius przycisków</h4>
<div class="change-value">
<span class="old-value">8px</span>
<span class="arrow"></span>
<span class="new-value">12px 4px 12px 4px</span>
</div>
</div>
<div class="change-card">
<h4>Header</h4>
<div class="change-value">
<span class="old-value">border-bottom</span>
<span class="arrow"></span>
<span class="new-value">subtelny cien</span>
</div>
</div>
<div class="change-card">
<h4>Footer</h4>
<div class="change-value">
<span class="color-preview" style="background: #1e293b;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #2E4872;"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
</div>
<div class="files-changed">
<h4>Zmienione pliki:</h4>
<code>templates/base.html</code>
</div>
<div class="screenshot-container">
<img src="screenshots/screenshot-1-sprint1.png" alt="Screenshot po Sprint 1">
<div class="screenshot-label">Po Sprint 1 (commit de46e12)</div>
</div>
</section>
<!-- SPRINT 2 -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge">SPRINT 2</span>
<h2 class="sprint-title">Bannery i karty firm</h2>
<span class="commit-hash">1da42f2</span>
</div>
<div class="changes-grid">
<div class="change-card">
<h4>Event banner</h4>
<div class="change-value">
<span class="color-preview" style="background: #f59e0b;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #2E4872;"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
<div class="change-card">
<h4>NordaGPT banner</h4>
<div class="change-value">
<span class="color-preview" style="background: #7c3aed;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #2E4872;"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
<div class="change-card">
<h4>Karty firm - styl</h4>
<div class="change-value">
<span class="old-value">box-shadow</span>
<span class="arrow"></span>
<span class="new-value">border 1px solid</span>
</div>
</div>
<div class="change-card">
<h4>Karty firm - hover</h4>
<div class="change-value">
<span class="old-value">translateY(-2px)</span>
<span class="arrow"></span>
<span class="new-value">border-color: primary</span>
</div>
</div>
<div class="change-card">
<h4>Tagi kategorii</h4>
<div class="change-value">
<span class="old-value">szare, male litery</span>
<span class="arrow"></span>
<span class="new-value">UPPERCASE, primary</span>
</div>
</div>
<div class="change-card">
<h4>Przyciski w bannerach</h4>
<div class="change-value">
<span class="new-value">asymetryczny border-radius</span>
</div>
</div>
</div>
<div class="files-changed">
<h4>Zmienione pliki:</h4>
<code>templates/index.html</code>
</div>
<div class="screenshot-container">
<img src="screenshots/screenshot-2-sprint2.png" alt="Screenshot po Sprint 2">
<div class="screenshot-label">Po Sprint 2 (commit 1da42f2)</div>
</div>
</section>
<!-- SPRINT 3 -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge">SPRINT 3</span>
<h2 class="sprint-title">Strona chatu NordaGPT</h2>
<span class="commit-hash">b680e3f</span>
</div>
<div class="changes-grid">
<div class="change-card">
<h4>Chat header</h4>
<div class="change-value">
<span class="color-preview" style="background: #7c3aed;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #2E4872;"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
<div class="change-card">
<h4>Sidebar button</h4>
<div class="change-value">
<span class="old-value">fioletowy</span>
<span class="arrow"></span>
<span class="new-value">niebieski</span>
</div>
</div>
<div class="change-card">
<h4>AI Message avatar</h4>
<div class="change-value">
<span class="old-value">fioletowy gradient</span>
<span class="arrow"></span>
<span class="new-value">niebieski gradient</span>
</div>
</div>
<div class="change-card">
<h4>Input focus</h4>
<div class="change-value">
<span class="old-value">#7c3aed</span>
<span class="arrow"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
<div class="change-card">
<h4>Send button</h4>
<div class="change-value">
<span class="old-value">fioletowy</span>
<span class="arrow"></span>
<span class="new-value">niebieski</span>
</div>
</div>
<div class="change-card">
<h4>Fluent CSS</h4>
<div class="change-value">
<span class="new-value">wszystkie zmienne primary</span>
</div>
</div>
</div>
<div class="files-changed">
<h4>Zmienione pliki:</h4>
<code>templates/chat.html</code>
<code>static/css/fluent-nordabiz.css</code>
</div>
<div class="screenshot-container">
<img src="screenshots/screenshot-3-sprint3.png" alt="Screenshot po Sprint 3 - strona główna">
<div class="screenshot-label">Po Sprint 3 - strona glowna (commit b680e3f)</div>
</div>
<div class="screenshot-container" style="margin-top: 1rem;">
<img src="screenshots/screenshot-4-chat.png" alt="Screenshot po Sprint 3 - strona chatu">
<div class="screenshot-label">Po Sprint 3 - strona /chat (commit b680e3f)</div>
</div>
</section>
<!-- SPRINT 4 -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge">SPRINT 4</span>
<h2 class="sprint-title">Animacje scroll</h2>
<span class="commit-hash">6e1c46e</span>
</div>
<div class="changes-grid">
<div class="change-card">
<h4>IntersectionObserver</h4>
<div class="change-value">
<span class="new-value">Nowy plik scroll-animations.js</span>
</div>
</div>
<div class="change-card">
<h4>CSS Keyframes</h4>
<div class="change-value">
<span class="new-value">fadeIn, fadeInUp, fadeInLeft, fadeInRight, scaleIn</span>
</div>
</div>
<div class="change-card">
<h4>Event banner</h4>
<div class="change-value">
<span class="new-value">data-animate="fadeIn"</span>
</div>
</div>
<div class="change-card">
<h4>Chat banner</h4>
<div class="change-value">
<span class="new-value">data-animate="fadeIn"</span>
</div>
</div>
<div class="change-card">
<h4>Karty firm</h4>
<div class="change-value">
<span class="new-value">data-animate="fadeInUp" + delay stagger</span>
</div>
</div>
<div class="change-card">
<h4>Reduced motion</h4>
<div class="change-value">
<span class="new-value">prefers-reduced-motion wsparcie</span>
</div>
</div>
</div>
<div class="files-changed">
<h4>Zmienione pliki:</h4>
<code>templates/base.html</code>
<code>templates/index.html</code>
<code>static/js/scroll-animations.js (NOWY)</code>
</div>
<div class="screenshot-container">
<img src="screenshots/screenshot-before-sprint4.png" alt="Screenshot przed Sprint 4">
<div class="screenshot-label">Przed Sprint 4 (commit b680e3f)</div>
</div>
<div class="screenshot-container" style="margin-top: 1rem;">
<img src="screenshots/screenshot-4-sprint4.png" alt="Screenshot po Sprint 4">
<div class="screenshot-label">Po Sprint 4 (commit 6e1c46e)</div>
</div>
</section>
<!-- SPRINT 5 -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge">SPRINT 5</span>
<h2 class="sprint-title">Landing page</h2>
<span class="commit-hash">ede9d09</span>
</div>
<div class="changes-grid">
<div class="change-card">
<h4>Hero gradient</h4>
<div class="change-value">
<span class="color-preview" style="background: linear-gradient(90deg, #1e40af, #2563eb);"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: linear-gradient(90deg, #1e3050, #2E4872);"></span>
<span class="new-value">NordaBiz blue</span>
</div>
</div>
<div class="change-card">
<h4>Accent color</h4>
<div class="change-value">
<span class="color-preview" style="background: #2563eb;"></span>
<span class="arrow"></span>
<span class="color-preview" style="background: #2E4872;"></span>
<span class="new-value">#2E4872</span>
</div>
</div>
<div class="change-card">
<h4>Hero section</h4>
<div class="change-value">
<span class="new-value">data-animate="fadeIn"</span>
</div>
</div>
<div class="change-card">
<h4>Stats section</h4>
<div class="change-value">
<span class="new-value">data-animate="fadeInUp"</span>
</div>
</div>
<div class="change-card">
<h4>CTA section</h4>
<div class="change-value">
<span class="new-value">data-animate="fadeInUp"</span>
</div>
</div>
</div>
<div class="files-changed">
<h4>Zmienione pliki:</h4>
<code>templates/landing.html</code>
</div>
<div class="screenshot-container">
<img src="screenshots/screenshot-before-sprint5.png" alt="Screenshot przed Sprint 5">
<div class="screenshot-label">Przed Sprint 5 - landing page (commit 6e1c46e)</div>
</div>
<div class="screenshot-container" style="margin-top: 1rem;">
<img src="screenshots/screenshot-5-sprint5.png" alt="Screenshot po Sprint 5">
<div class="screenshot-label">Po Sprint 5 - landing page (commit ede9d09)</div>
</div>
</section>
<!-- PODSUMOWANIE -->
<section class="sprint-section">
<div class="sprint-header">
<span class="sprint-badge">PODSUMOWANIE</span>
<h2 class="sprint-title">Wszystkie zmiany</h2>
</div>
<table class="summary-table">
<thead>
<tr>
<th>Element</th>
<th>PRZED</th>
<th>PO</th>
</tr>
</thead>
<tbody>
<tr>
<td>Czcionka</td>
<td>Inter</td>
<td><strong>Poppins</strong></td>
</tr>
<tr>
<td>Kolor primary</td>
<td><span class="color-preview" style="background: #2563eb;"></span> #2563eb</td>
<td><span class="color-preview" style="background: #2E4872;"></span> <strong>#2E4872</strong></td>
</tr>
<tr>
<td>Tło strony</td>
<td><span class="color-preview" style="background: #f8fafc;"></span> #f8fafc</td>
<td><span class="color-preview" style="background: #EDF0F5;"></span> <strong>#EDF0F5</strong></td>
</tr>
<tr>
<td>Border-radius btn</td>
<td>8px</td>
<td><strong>12px 4px</strong></td>
</tr>
<tr>
<td>Event banner</td>
<td><span class="color-preview" style="background: #f59e0b;"></span> #f59e0b</td>
<td><span class="color-preview" style="background: #2E4872;"></span> <strong>#2E4872</strong></td>
</tr>
<tr>
<td>NordaGPT</td>
<td><span class="color-preview" style="background: #7c3aed;"></span> #7c3aed</td>
<td><span class="color-preview" style="background: #2E4872;"></span> <strong>#2E4872</strong></td>
</tr>
<tr>
<td>Karty firm</td>
<td>box-shadow</td>
<td><strong>border</strong></td>
</tr>
<tr>
<td>Tagi kategorii</td>
<td>szare, male</td>
<td><strong>UPPERCASE, primary</strong></td>
</tr>
<tr>
<td>Footer</td>
<td><span class="color-preview" style="background: #1e293b;"></span> #1e293b</td>
<td><span class="color-preview" style="background: #2E4872;"></span> <strong>#2E4872</strong></td>
</tr>
<tr>
<td>Animacje scroll</td>
<td>brak</td>
<td><strong>fadeIn + IntersectionObserver</strong></td>
</tr>
<tr>
<td>Landing hero</td>
<td><span class="color-preview" style="background: linear-gradient(90deg, #1e40af, #2563eb);"></span> stary niebieski</td>
<td><span class="color-preview" style="background: linear-gradient(90deg, #1e3050, #2E4872);"></span> <strong>NordaBiz blue</strong></td>
</tr>
</tbody>
</table>
</section>
</div>
<footer>
<p>Dokumentacja wygenerowana automatycznie | NordaBiz UI Redesign | 30.01.2026</p>
<p style="margin-top: 0.5rem;">
<a href="https://nordabiznes.pl" target="_blank" style="color: #2E4872;">nordabiznes.pl</a> |
<a href="https://norda-biznes.info" target="_blank" style="color: #2E4872;">norda-biznes.info</a>
</p>
</footer>
</body>
</html>

View File

@ -464,9 +464,9 @@ def update_company_from_ceidg(company_id: int, ceidg_data: dict, db) -> bool:
company.pkd_code = pkd_glowny.get("kod")
company.pkd_description = pkd_glowny.get("nazwa")
# Wszystkie PKD
# Wszystkie PKD z CEIDG
if ceidg_data.get("pkd"):
company.pkd_codes = ceidg_data.get("pkd")
company.ceidg_pkd_list = ceidg_data.get("pkd")
# Data rozpoczęcia działalności
if ceidg_data.get("dataRozpoczecia"):

View File

@ -0,0 +1,493 @@
#!/usr/bin/env python3
"""
CEIDG Search by Name - wyszukuje firmy w CEIDG po nazwie
Dla firm bez NIP w bazie - szuka w portalu CEIDG po nazwie firmy
i weryfikuje wyniki przez porównanie adresu/telefonu.
Portal CEIDG: https://aplikacja.ceidg.gov.pl/ceidg/ceidg.public.ui/search.aspx
Usage:
python scripts/search_ceidg_by_name.py # Szukaj wszystkich
python scripts/search_ceidg_by_name.py --id 119 # Szukaj konkretnej firmy
python scripts/search_ceidg_by_name.py --apply # Zapisz znalezione NIP
"""
import os
import sys
import re
import argparse
import time
import json
from pathlib import Path
from datetime import datetime
from dataclasses import dataclass, asdict, field
from typing import Optional, List
from difflib import SequenceMatcher
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout
except ImportError:
print("Playwright nie jest zainstalowany. Uruchom: pip install playwright && playwright install chromium")
sys.exit(1)
from database import SessionLocal, Company
# Output directory
RESULTS_DIR = Path(__file__).parent.parent / "data" / "ceidg_search_results"
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
# Domains to skip (public email providers)
SKIP_DOMAINS = {
'gmail.com', 'wp.pl', 'onet.pl', 'op.pl', 'interia.pl',
'o2.pl', 'poczta.fm', 'yahoo.com', 'hotmail.com', 'outlook.com'
}
@dataclass
class CEIDGSearchResult:
"""Wynik wyszukiwania w CEIDG"""
company_id: int
company_name: str
search_query: str
# Znalezione dane
found_nip: Optional[str] = None
found_regon: Optional[str] = None
found_name: Optional[str] = None
found_owner: Optional[str] = None
found_address: Optional[str] = None
found_status: Optional[str] = None
# Weryfikacja
matches: List[str] = field(default_factory=list) # Co się zgadza
confidence: str = "low" # low, medium, high
verified: bool = False
error: Optional[str] = None
searched_at: str = ""
def __post_init__(self):
if not self.searched_at:
self.searched_at = datetime.now().isoformat()
def to_dict(self):
return asdict(self)
def normalize_phone(phone: str) -> str:
"""Normalizuje numer telefonu do samych cyfr"""
if not phone:
return ""
return re.sub(r'[^0-9]', '', phone)
def normalize_address(address: str) -> str:
"""Normalizuje adres do porównania"""
if not address:
return ""
# Lowercase, usuń znaki specjalne
addr = address.lower()
addr = re.sub(r'[^\w\s]', ' ', addr)
addr = re.sub(r'\s+', ' ', addr).strip()
return addr
def similarity(a: str, b: str) -> float:
"""Oblicza podobieństwo dwóch stringów (0-1)"""
if not a or not b:
return 0.0
return SequenceMatcher(None, a.lower(), b.lower()).ratio()
def validate_nip(nip: str) -> bool:
"""Waliduje NIP (checksum)"""
nip = re.sub(r'[^0-9]', '', nip)
if len(nip) != 10:
return False
weights = [6, 5, 7, 2, 3, 4, 5, 6, 7]
try:
checksum = sum(int(nip[i]) * weights[i] for i in range(9)) % 11
return checksum == int(nip[9])
except (ValueError, IndexError):
return False
def extract_nip_from_text(text: str) -> Optional[str]:
"""Wyciąga NIP z tekstu"""
patterns = [
r'NIP[:\s]*(\d{3}[-\s]?\d{3}[-\s]?\d{2}[-\s]?\d{2})',
r'NIP[:\s]*(\d{10})',
r'\b(\d{10})\b', # Standalone 10 digits
]
for pattern in patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
for match in matches:
nip = re.sub(r'[^0-9]', '', match)
if validate_nip(nip):
return nip
return None
def extract_regon_from_text(text: str) -> Optional[str]:
"""Wyciąga REGON z tekstu"""
patterns = [
r'REGON[:\s]*(\d{9,14})',
]
for pattern in patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
for match in matches:
regon = re.sub(r'[^0-9]', '', match)
if len(regon) in (9, 14):
return regon
return None
def search_ceidg(company: Company) -> CEIDGSearchResult:
"""
Szuka firmy w CEIDG po nazwie.
Portal CEIDG: https://aplikacja.ceidg.gov.pl/ceidg/ceidg.public.ui/search.aspx
"""
# Prepare search query
search_name = company.name
# Remove common suffixes (CEIDG is for sole proprietorships, not companies)
for suffix in [' sp. z o.o.', ' sp.z o.o.', ' s.c.', ' s.j.']:
search_name = search_name.replace(suffix, '').replace(suffix.upper(), '')
search_name = search_name.strip()
result = CEIDGSearchResult(
company_id=company.id,
company_name=company.name,
search_query=search_name
)
print(f" Szukam w CEIDG: '{search_name}'")
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
page = context.new_page()
page.set_default_timeout(60000) # 60 seconds default timeout
try:
# Go to CEIDG search page
print(" → Ładuję stronę CEIDG...")
page.goto("https://aplikacja.ceidg.gov.pl/ceidg/ceidg.public.ui/search.aspx", timeout=60000)
time.sleep(3)
# Wait for page to be ready
page.wait_for_load_state("domcontentloaded", timeout=30000)
print(" → Strona załadowana")
# Try multiple selectors for company name field
firma_input = None
selectors = [
"input[id*='txtFirma']",
"input[id*='Firma']",
"input[name*='Firma']",
"#ctl00_MainContent_txtFirma",
"input[placeholder*='Nazwa firmy']",
]
for selector in selectors:
try:
elem = page.locator(selector).first
if elem.is_visible(timeout=2000):
firma_input = elem
print(f" → Znaleziono pole wyszukiwania: {selector}")
break
except:
continue
if not firma_input:
# Take screenshot for debugging
screenshot_path = RESULTS_DIR / f"ceidg_debug_{company.id}.png"
page.screenshot(path=str(screenshot_path))
result.error = f"Nie znaleziono pola wyszukiwania. Screenshot: {screenshot_path}"
return result
# Fill in company name
firma_input.fill(search_name)
print(f" → Wpisano: '{search_name}'")
time.sleep(1)
# Add city if available
if company.address_city:
city_selectors = [
"input[id*='txtMiasto']",
"input[id*='Miasto']",
"#ctl00_MainContent_txtMiasto",
]
for selector in city_selectors:
try:
city_input = page.locator(selector).first
if city_input.is_visible(timeout=2000):
city_input.fill(company.address_city)
print(f" → Dodano miasto: '{company.address_city}'")
break
except:
continue
# Find and click search button
search_selectors = [
"input[id*='btnSearch']",
"input[value='Szukaj']",
"button:has-text('Szukaj')",
"#ctl00_MainContent_btnSearch",
]
search_clicked = False
for selector in search_selectors:
try:
btn = page.locator(selector).first
if btn.is_visible(timeout=2000):
btn.click()
search_clicked = True
print(" → Kliknięto Szukaj")
break
except:
continue
if not search_clicked:
page.keyboard.press("Enter")
print(" → Wysłano Enter")
# Wait for results
time.sleep(5)
page.wait_for_load_state("networkidle", timeout=30000)
print(" → Wyniki załadowane")
# Check for "no results" message
page_text_check = page.inner_text("body")
if "Brak wyników" in page_text_check or "nie znaleziono" in page_text_check.lower():
result.error = "Nie znaleziono w CEIDG"
return result
# Find details link
details_selectors = [
"a:has-text('Szczegóły')",
"a[href*='SearchDetails']",
"a[id*='Details']",
"a.details-link",
]
details_link = None
for selector in details_selectors:
try:
link = page.locator(selector).first
if link.is_visible(timeout=3000):
details_link = link
break
except:
continue
if not details_link:
# Maybe direct results page?
page_text = page.inner_text("body")
nip = extract_nip_from_text(page_text)
if nip:
result.found_nip = nip
result.found_regon = extract_regon_from_text(page_text)
result = verify_result(result, company)
return result
result.error = "Brak linku do szczegółów"
screenshot_path = RESULTS_DIR / f"ceidg_results_{company.id}.png"
page.screenshot(path=str(screenshot_path))
return result
# Click details link
details_link.click()
print(" → Kliknięto Szczegóły")
time.sleep(4)
page.wait_for_load_state("networkidle", timeout=30000)
# Extract data from details page
page_text = page.inner_text("body")
# Extract NIP
result.found_nip = extract_nip_from_text(page_text)
result.found_regon = extract_regon_from_text(page_text)
# Extract owner name
owner_match = re.search(r'Imię i nazwisko[:\s]*([A-ZĄĆĘŁŃÓŚŹŻ][a-ząćęłńóśźż]+\s+[A-ZĄĆĘŁŃÓŚŹŻ][a-ząćęłńóśźż]+)', page_text)
if owner_match:
result.found_owner = owner_match.group(1).strip()
# Extract company name from CEIDG
firma_match = re.search(r'Firma przedsiębiorcy[:\s]*(.+?)(?:\n|Adres|Status)', page_text, re.DOTALL)
if firma_match:
result.found_name = firma_match.group(1).strip()[:200]
# Extract address
addr_match = re.search(r'Adres[:\s]*(.+?)(?:\n\n|Status|Data)', page_text, re.DOTALL)
if addr_match:
result.found_address = addr_match.group(1).strip()[:200]
# Extract status
if 'AKTYWNY' in page_text.upper():
result.found_status = 'AKTYWNY'
elif 'ZAWIESZONY' in page_text.upper():
result.found_status = 'ZAWIESZONY'
elif 'WYKREŚLONY' in page_text.upper():
result.found_status = 'WYKREŚLONY'
# Verify the result
result = verify_result(result, company)
except PlaywrightTimeout:
result.error = "Timeout"
except Exception as e:
result.error = str(e)[:200]
finally:
browser.close()
return result
def verify_result(result: CEIDGSearchResult, company: Company) -> CEIDGSearchResult:
"""
Weryfikuje czy znaleziony wynik pasuje do naszej firmy.
"""
if not result.found_nip:
result.error = "NIP nie znaleziony na stronie szczegółów"
return result
matches = []
# 1. Sprawdź podobieństwo nazwy
if result.found_name:
name_sim = similarity(company.name, result.found_name)
if name_sim > 0.7:
matches.append(f"nazwa ({name_sim:.0%})")
elif name_sim > 0.5:
matches.append(f"nazwa częściowa ({name_sim:.0%})")
# 2. Sprawdź adres/miasto
if result.found_address and company.address_city:
if company.address_city.lower() in result.found_address.lower():
matches.append("miasto")
if result.found_address and company.address_street:
if company.address_street.lower()[:10] in result.found_address.lower():
matches.append("ulica")
# 3. Sprawdź właściciela (jeśli mamy w nazwie)
if result.found_owner:
owner_parts = result.found_owner.lower().split()
company_name_lower = company.name.lower()
for part in owner_parts:
if len(part) > 3 and part in company_name_lower:
matches.append("właściciel w nazwie")
break
# Determine confidence
result.matches = matches
if len(matches) >= 2:
result.confidence = "high"
result.verified = True
elif len(matches) == 1 and "nazwa" in matches[0]:
result.confidence = "medium"
result.verified = True
elif len(matches) == 1:
result.confidence = "low"
result.verified = False
else:
result.confidence = "low"
result.verified = False
return result
def get_companies_without_nip(db, company_id: int = None) -> List[Company]:
"""Pobiera firmy bez NIP"""
query = db.query(Company).filter(
(Company.nip == None) | (Company.nip == '')
)
if company_id:
query = query.filter(Company.id == company_id)
return query.order_by(Company.name).all()
def main():
parser = argparse.ArgumentParser(description="Search CEIDG by company name")
parser.add_argument('--id', type=int, help="Search specific company ID")
parser.add_argument('--apply', action='store_true', help="Apply found NIPs to database")
parser.add_argument('--limit', type=int, default=50, help="Limit number of companies to search")
parser.add_argument('--output', type=str, help="Output JSON file path")
args = parser.parse_args()
db = SessionLocal()
try:
companies = get_companies_without_nip(db, args.id)
if not args.id:
companies = companies[:args.limit]
print(f"\n=== Wyszukiwanie {len(companies)} firm w CEIDG ===\n")
results = []
found_count = 0
verified_count = 0
for i, company in enumerate(companies, 1):
print(f"[{i}/{len(companies)}] {company.name}")
result = search_ceidg(company)
results.append(result)
if result.found_nip:
found_count += 1
status = "" if result.verified else "?"
print(f" {status} NIP: {result.found_nip} (confidence: {result.confidence})")
print(f" Matches: {', '.join(result.matches) if result.matches else 'brak'}")
if result.verified:
verified_count += 1
if args.apply:
company.nip = result.found_nip
if result.found_regon and not company.regon:
company.regon = result.found_regon
db.commit()
print(f" → Zapisano do bazy")
elif result.error:
print(f"{result.error}")
# Rate limiting - CEIDG może blokować
time.sleep(3)
# Save results to JSON
output_file = args.output or (RESULTS_DIR / f"ceidg_search_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
with open(output_file, 'w', encoding='utf-8') as f:
json.dump([r.to_dict() for r in results], f, ensure_ascii=False, indent=2)
print(f"\n=== Podsumowanie ===")
print(f"Przeszukano: {len(companies)} firm")
print(f"Znaleziono NIP: {found_count}")
print(f"Zweryfikowano: {verified_count}")
print(f"Wyniki zapisane: {output_file}")
if verified_count > 0 and not args.apply:
print(f"\nUżyj --apply aby zapisać zweryfikowane NIP do bazy")
finally:
db.close()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,353 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>NordaGPT - Propozycje ikon v2</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
padding: 2rem;
color: white;
}
h1 { text-align: center; margin-bottom: 0.5rem; font-weight: 300; }
h2 { text-align: center; margin: 2rem 0 1rem; font-weight: 400; color: #22c55e; font-size: 1.3rem; }
.subtitle { text-align: center; margin-bottom: 2rem; color: rgba(255,255,255,0.6); }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.5rem;
max-width: 1200px;
margin: 0 auto;
}
.icon-card {
background: rgba(255,255,255,0.05);
border-radius: 16px;
padding: 1.5rem;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
border: 1px solid rgba(255,255,255,0.1);
position: relative;
}
.icon-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0,0,0,0.3);
border-color: rgba(34, 197, 94, 0.5);
}
.icon-container {
width: 80px;
height: 80px;
margin: 0 auto 1rem;
display: flex;
align-items: center;
justify-content: center;
}
.icon-container svg { width: 100%; height: 100%; }
.icon-name { font-size: 1rem; font-weight: 600; margin-bottom: 0.3rem; color: #22c55e; }
.icon-desc { font-size: 0.8rem; color: rgba(255,255,255,0.6); line-height: 1.3; }
.icon-number {
position: absolute;
top: 0.8rem;
left: 0.8rem;
background: #22c55e;
color: #1a1a2e;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
}
.section-divider {
border: none;
border-top: 1px solid rgba(255,255,255,0.1);
margin: 2rem 0;
}
</style>
</head>
<body>
<h1>NordaGPT - Propozycje ikon v2</h1>
<p class="subtitle">5 wariantów robota + 5 wariantów litery N</p>
<h2>🤖 Warianty: Przyjazny Robot</h2>
<div class="grid">
<!-- Robot 1: Okrągły, minimalistyczny -->
<div class="icon-card">
<div class="icon-number">R1</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Antenna -->
<line x1="50" y1="22" x2="50" y2="12" stroke="#22c55e" stroke-width="3" stroke-linecap="round"/>
<circle cx="50" cy="10" r="4" fill="#fbbf24"/>
<!-- Face circle -->
<circle cx="50" cy="52" r="28" fill="#3b82f6"/>
<circle cx="50" cy="52" r="24" fill="#60a5fa"/>
<!-- Eyes - happy crescents -->
<path d="M35 48 Q 40 42, 45 48" stroke="#233e6d" stroke-width="3" fill="none" stroke-linecap="round"/>
<path d="M55 48 Q 60 42, 65 48" stroke="#233e6d" stroke-width="3" fill="none" stroke-linecap="round"/>
<!-- Smile -->
<path d="M38 58 Q 50 68, 62 58" stroke="#233e6d" stroke-width="3" fill="none" stroke-linecap="round"/>
</svg>
</div>
<div class="icon-name">Uśmiechnięty Krąg</div>
<div class="icon-desc">Minimalistyczny, zamknięte oczy = szczęście</div>
</div>
<!-- Robot 2: Z dużymi oczami -->
<div class="icon-card">
<div class="icon-number">R2</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Antenna with glow -->
<line x1="50" y1="20" x2="50" y2="8" stroke="#22c55e" stroke-width="2"/>
<circle cx="50" cy="6" r="4" fill="#fbbf24"/>
<circle cx="50" cy="6" r="6" fill="#fbbf24" opacity="0.3"/>
<!-- Head -->
<rect x="22" y="24" width="56" height="50" rx="12" fill="#22c55e"/>
<rect x="26" y="28" width="48" height="42" rx="9" fill="#4ade80"/>
<!-- Big cute eyes -->
<ellipse cx="38" cy="48" rx="10" ry="12" fill="white"/>
<ellipse cx="62" cy="48" rx="10" ry="12" fill="white"/>
<circle cx="40" cy="48" r="6" fill="#233e6d"/>
<circle cx="64" cy="48" r="6" fill="#233e6d"/>
<circle cx="42" cy="46" r="2" fill="white"/>
<circle cx="66" cy="46" r="2" fill="white"/>
<!-- Small smile -->
<path d="M44 62 Q 50 67, 56 62" stroke="#233e6d" stroke-width="2.5" fill="none" stroke-linecap="round"/>
</svg>
</div>
<div class="icon-name">Duże Oczy</div>
<div class="icon-desc">Zielony robot z dużymi, uroczymi oczami</div>
</div>
<!-- Robot 3: Pixel art style -->
<div class="icon-card">
<div class="icon-number">R3</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Pixel antenna -->
<rect x="47" y="12" width="6" height="10" fill="#22c55e"/>
<rect x="44" y="8" width="12" height="6" fill="#fbbf24"/>
<!-- Pixel head -->
<rect x="24" y="26" width="52" height="48" rx="4" fill="#8b5cf6"/>
<rect x="28" y="30" width="44" height="40" fill="#a78bfa"/>
<!-- Pixel eyes -->
<rect x="34" y="40" width="10" height="10" fill="white"/>
<rect x="56" y="40" width="10" height="10" fill="white"/>
<rect x="38" y="44" width="4" height="4" fill="#233e6d"/>
<rect x="60" y="44" width="4" height="4" fill="#233e6d"/>
<!-- Pixel smile -->
<rect x="38" y="58" width="4" height="4" fill="#233e6d"/>
<rect x="42" y="62" width="16" height="4" fill="#233e6d"/>
<rect x="58" y="58" width="4" height="4" fill="#233e6d"/>
</svg>
</div>
<div class="icon-name">Pixel Robot</div>
<div class="icon-desc">Styl retro/pixel art, fioletowy</div>
</div>
<!-- Robot 4: Sleek modern -->
<div class="icon-card">
<div class="icon-number">R4</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="robotGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#8b5cf6"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Sleek antenna -->
<path d="M50 18 L50 8" stroke="url(#robotGrad)" stroke-width="3" stroke-linecap="round"/>
<circle cx="50" cy="6" r="3" fill="#fbbf24"/>
<!-- Modern rounded head -->
<rect x="20" y="22" width="60" height="52" rx="16" fill="url(#robotGrad)"/>
<!-- Visor style eye -->
<rect x="28" y="38" width="44" height="14" rx="7" fill="#1a1a2e"/>
<rect x="32" y="41" width="10" height="8" rx="4" fill="#22c55e"/>
<rect x="58" y="41" width="10" height="8" rx="4" fill="#22c55e"/>
<!-- Subtle mouth line -->
<path d="M40 62 L60 62" stroke="#1a1a2e" stroke-width="3" stroke-linecap="round"/>
</svg>
</div>
<div class="icon-name">Nowoczesny</div>
<div class="icon-desc">Sleek design z wizjerem</div>
</div>
<!-- Robot 5: Okrągły z sercem -->
<div class="icon-card">
<div class="icon-number">R5</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Antenna -->
<line x1="50" y1="18" x2="50" y2="10" stroke="#f472b6" stroke-width="2"/>
<circle cx="50" cy="8" r="4" fill="#fbbf24"/>
<!-- Round head -->
<circle cx="50" cy="50" r="32" fill="#ec4899"/>
<circle cx="50" cy="50" r="28" fill="#f472b6"/>
<!-- Happy eyes -->
<circle cx="38" cy="45" r="6" fill="white"/>
<circle cx="62" cy="45" r="6" fill="white"/>
<circle cx="39" cy="45" r="3" fill="#233e6d"/>
<circle cx="63" cy="45" r="3" fill="#233e6d"/>
<!-- Heart mouth -->
<path d="M45 58 L50 65 L55 58 Q 55 54, 50 58 Q 45 54, 45 58" fill="#233e6d"/>
<!-- Blush -->
<circle cx="30" cy="52" r="5" fill="#fb7185" opacity="0.5"/>
<circle cx="70" cy="52" r="5" fill="#fb7185" opacity="0.5"/>
</svg>
</div>
<div class="icon-name">Różowy z Sercem</div>
<div class="icon-desc">Przyjazny, ciepły, z rumieńcami</div>
</div>
</div>
<hr class="section-divider">
<h2>🅽 Warianty: Litera N + GPT</h2>
<div class="grid">
<!-- N1: Klasyczny z GPT -->
<div class="icon-card">
<div class="icon-number">N1</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="nGrad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="100%" style="stop-color:#14b8a6"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<circle cx="50" cy="50" r="38" fill="none" stroke="url(#nGrad1)" stroke-width="2.5"/>
<!-- Letter N -->
<path d="M30 72 L30 28 L70 72 L70 28" stroke="url(#nGrad1)" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- North indicator -->
<circle cx="50" cy="8" r="4" fill="#fbbf24"/>
<!-- GPT badge -->
<rect x="58" y="68" width="28" height="16" rx="4" fill="#22c55e"/>
<text x="72" y="80" text-anchor="middle" fill="white" font-size="9" font-weight="bold">GPT</text>
</svg>
</div>
<div class="icon-name">Klasyczne N</div>
<div class="icon-desc">Oryginalna wersja z badge GPT</div>
</div>
<!-- N2: Outline style -->
<div class="icon-card">
<div class="icon-number">N2</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- N as outline -->
<path d="M28 75 L28 25 L72 75 L72 25" stroke="#22c55e" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M28 75 L28 25 L72 75 L72 25" stroke="#4ade80" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="5,5"/>
<!-- North star -->
<polygon points="50,5 52,12 50,10 48,12" fill="#fbbf24"/>
<circle cx="50" cy="5" r="3" fill="#fbbf24"/>
<!-- GPT text below -->
<text x="50" y="95" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold">GPT</text>
</svg>
</div>
<div class="icon-name">N Outline</div>
<div class="icon-desc">Konturowe N z GPT na dole</div>
</div>
<!-- N3: Bold chunky -->
<div class="icon-card">
<div class="icon-number">N3</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="nGrad3" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#8b5cf6"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Bold N -->
<path d="M25 78 L25 22 L75 78 L75 22" stroke="url(#nGrad3)" stroke-width="12" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Glow effect -->
<path d="M25 78 L25 22 L75 78 L75 22" stroke="#3b82f6" stroke-width="16" fill="none" stroke-linecap="round" stroke-linejoin="round" opacity="0.2"/>
<!-- GPT chip -->
<rect x="60" y="60" width="30" height="20" rx="4" fill="#1a1a2e" stroke="#22c55e" stroke-width="1.5"/>
<text x="75" y="74" text-anchor="middle" fill="#22c55e" font-size="10" font-weight="bold">GPT</text>
<!-- North -->
<circle cx="50" cy="6" r="4" fill="#fbbf24"/>
</svg>
</div>
<div class="icon-name">Bold N</div>
<div class="icon-desc">Grube N z chipem GPT</div>
</div>
<!-- N4: Geometric -->
<div class="icon-card">
<div class="icon-number">N4</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Geometric N made of shapes -->
<rect x="25" y="25" width="10" height="50" rx="2" fill="#22c55e"/>
<rect x="65" y="25" width="10" height="50" rx="2" fill="#22c55e"/>
<polygon points="25,25 35,25 75,75 65,75" fill="#22c55e"/>
<!-- GPT in center -->
<circle cx="50" cy="50" r="15" fill="#1a1a2e"/>
<text x="50" y="54" text-anchor="middle" fill="#22c55e" font-size="9" font-weight="bold">GPT</text>
<!-- North arrow -->
<polygon points="50,5 54,12 50,9 46,12" fill="#fbbf24"/>
</svg>
</div>
<div class="icon-name">Geometryczne N</div>
<div class="icon-desc">N z prostokątów z GPT w środku</div>
</div>
<!-- N5: Neon style -->
<div class="icon-card">
<div class="icon-number">N5</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<filter id="neon" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="2" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0f172a"/>
<!-- Neon N -->
<path d="M28 75 L28 25 L72 75 L72 25" stroke="#22c55e" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#neon)"/>
<!-- Neon circle -->
<circle cx="50" cy="50" r="40" fill="none" stroke="#22c55e" stroke-width="1.5" opacity="0.5" filter="url(#neon)"/>
<!-- GPT neon -->
<text x="50" y="92" text-anchor="middle" fill="#f472b6" font-size="12" font-weight="bold" filter="url(#neon)">GPT</text>
<!-- North glow -->
<circle cx="50" cy="6" r="4" fill="#fbbf24" filter="url(#neon)"/>
</svg>
</div>
<div class="icon-name">Neonowe N</div>
<div class="icon-desc">Efekt neonu, nocny vibe</div>
</div>
</div>
<hr class="section-divider">
<p style="text-align: center; color: rgba(255,255,255,0.4); margin-top: 1rem;">
Wybierz numer ikony do wdrożenia
</p>
</body>
</html>

View File

@ -0,0 +1,337 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>NordaGPT - Propozycje ikon v3</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #0a0a1a 0%, #1a1a2e 100%);
min-height: 100vh;
padding: 2rem;
color: white;
}
h1 { text-align: center; margin-bottom: 0.5rem; font-weight: 300; }
h2 { text-align: center; margin: 2rem 0 1rem; font-weight: 400; color: #22c55e; font-size: 1.3rem; }
.subtitle { text-align: center; margin-bottom: 2rem; color: rgba(255,255,255,0.6); }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.2rem;
max-width: 1100px;
margin: 0 auto;
}
.icon-card {
background: rgba(255,255,255,0.03);
border-radius: 16px;
padding: 1.2rem;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
border: 1px solid rgba(255,255,255,0.08);
position: relative;
}
.icon-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0,0,0,0.4);
border-color: rgba(34, 197, 94, 0.4);
}
.icon-container {
width: 90px;
height: 90px;
margin: 0 auto 0.8rem;
}
.icon-container svg { width: 100%; height: 100%; }
.icon-name { font-size: 0.95rem; font-weight: 600; margin-bottom: 0.2rem; color: #4ade80; }
.icon-desc { font-size: 0.75rem; color: rgba(255,255,255,0.5); line-height: 1.3; }
.icon-number {
position: absolute;
top: 0.6rem;
left: 0.6rem;
background: #22c55e;
color: #0a0a1a;
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.7rem;
}
.section-divider {
border: none;
border-top: 1px solid rgba(255,255,255,0.08);
margin: 2rem 0;
}
</style>
</head>
<body>
<h1>NordaGPT - Propozycje ikon v3</h1>
<p class="subtitle">Warianty R4 (Nowoczesny Robot) + N5 (Neonowe N)</p>
<h2>🤖 Warianty: Nowoczesny Robot z Wizjerem</h2>
<div class="grid">
<!-- R4-A: Zielony wizjer -->
<div class="icon-card">
<div class="icon-number">A</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="rA" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="100%" style="stop-color:#14b8a6"/>
</linearGradient>
<filter id="glowA"><feGaussianBlur stdDeviation="2" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#1a1a2e"/>
<path d="M50 18 L50 8" stroke="url(#rA)" stroke-width="3" stroke-linecap="round"/>
<circle cx="50" cy="6" r="3" fill="#fbbf24" filter="url(#glowA)"/>
<rect x="20" y="22" width="60" height="52" rx="14" fill="url(#rA)"/>
<rect x="28" y="38" width="44" height="14" rx="7" fill="#0a0a1a"/>
<rect x="32" y="41" width="12" height="8" rx="4" fill="#4ade80" filter="url(#glowA)"/>
<rect x="56" y="41" width="12" height="8" rx="4" fill="#4ade80" filter="url(#glowA)"/>
<rect x="38" y="60" width="24" height="4" rx="2" fill="#0a0a1a"/>
</svg>
</div>
<div class="icon-name">Zielony INPI</div>
<div class="icon-desc">Kolory INPI, świecące oczy</div>
</div>
<!-- R4-B: Niebieski gradient -->
<div class="icon-card">
<div class="icon-number">B</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="rB" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#6366f1"/>
</linearGradient>
<filter id="glowB"><feGaussianBlur stdDeviation="2" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#1a1a2e"/>
<path d="M50 18 L50 8" stroke="url(#rB)" stroke-width="3" stroke-linecap="round"/>
<circle cx="50" cy="6" r="3" fill="#fbbf24" filter="url(#glowB)"/>
<rect x="20" y="22" width="60" height="52" rx="14" fill="url(#rB)"/>
<rect x="26" y="36" width="48" height="16" rx="8" fill="#0a0a1a"/>
<circle cx="38" cy="44" r="5" fill="#22c55e" filter="url(#glowB)"/>
<circle cx="62" cy="44" r="5" fill="#22c55e" filter="url(#glowB)"/>
<path d="M40 62 Q50 68, 60 62" stroke="#0a0a1a" stroke-width="3" fill="none" stroke-linecap="round"/>
</svg>
</div>
<div class="icon-name">Niebieski + Uśmiech</div>
<div class="icon-desc">Okrągłe oczy, delikatny uśmiech</div>
</div>
<!-- R4-C: Granat Norda -->
<div class="icon-card">
<div class="icon-number">C</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="rC" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#233e6d"/>
<stop offset="100%" style="stop-color:#1e3a5f"/>
</linearGradient>
<filter id="glowC"><feGaussianBlur stdDeviation="1.5" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0f172a"/>
<circle cx="50" cy="50" r="42" fill="none" stroke="#233e6d" stroke-width="1" opacity="0.5"/>
<path d="M50 16 L50 6" stroke="#22c55e" stroke-width="2" stroke-linecap="round"/>
<circle cx="50" cy="4" r="3" fill="#fbbf24" filter="url(#glowC)"/>
<rect x="18" y="20" width="64" height="54" rx="12" fill="url(#rC)" stroke="#22c55e" stroke-width="1.5"/>
<rect x="26" y="36" width="48" height="14" rx="7" fill="#0a0a1a"/>
<rect x="30" y="39" width="14" height="8" rx="4" fill="#22c55e" filter="url(#glowC)"/>
<rect x="56" y="39" width="14" height="8" rx="4" fill="#22c55e" filter="url(#glowC)"/>
<rect x="36" y="58" width="28" height="6" rx="3" fill="#0a0a1a"/>
<text x="50" y="64" text-anchor="middle" fill="#22c55e" font-size="6" font-weight="bold">GPT</text>
</svg>
</div>
<div class="icon-name">Norda Navy</div>
<div class="icon-desc">Granat Norda + GPT w ustach</div>
</div>
<!-- R4-D: Minimalistyczny -->
<div class="icon-card">
<div class="icon-number">D</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#1a1a2e"/>
<circle cx="50" cy="6" r="3" fill="#fbbf24"/>
<line x1="50" y1="9" x2="50" y2="18" stroke="#22c55e" stroke-width="2"/>
<rect x="22" y="22" width="56" height="48" rx="10" fill="none" stroke="#22c55e" stroke-width="2.5"/>
<rect x="30" y="38" width="40" height="12" rx="6" fill="#22c55e" opacity="0.2"/>
<circle cx="40" cy="44" r="4" fill="#22c55e"/>
<circle cx="60" cy="44" r="4" fill="#22c55e"/>
<line x1="40" y1="58" x2="60" y2="58" stroke="#22c55e" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<div class="icon-name">Line Art</div>
<div class="icon-desc">Minimalistyczny kontur</div>
</div>
<!-- R4-E: Z badge GPT -->
<div class="icon-card">
<div class="icon-number">E</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="rE" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#8b5cf6"/>
<stop offset="100%" style="stop-color:#a855f7"/>
</linearGradient>
<filter id="glowE"><feGaussianBlur stdDeviation="2" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#1a1a2e"/>
<path d="M50 18 L50 8" stroke="url(#rE)" stroke-width="3" stroke-linecap="round"/>
<circle cx="50" cy="6" r="3" fill="#fbbf24" filter="url(#glowE)"/>
<rect x="20" y="22" width="60" height="48" rx="14" fill="url(#rE)"/>
<rect x="28" y="36" width="44" height="14" rx="7" fill="#0a0a1a"/>
<rect x="32" y="39" width="10" height="8" rx="4" fill="#22c55e" filter="url(#glowE)"/>
<rect x="58" y="39" width="10" height="8" rx="4" fill="#22c55e" filter="url(#glowE)"/>
<line x1="40" y1="58" x2="60" y2="58" stroke="#0a0a1a" stroke-width="3" stroke-linecap="round"/>
<!-- GPT Badge -->
<circle cx="75" cy="70" r="14" fill="#22c55e"/>
<text x="75" y="74" text-anchor="middle" fill="white" font-size="8" font-weight="bold">GPT</text>
</svg>
</div>
<div class="icon-name">Fiolet + Badge</div>
<div class="icon-desc">Fioletowy z badge GPT</div>
</div>
</div>
<hr class="section-divider">
<h2>✨ Warianty: Neonowe N</h2>
<div class="grid">
<!-- N5-A: Zielony neon -->
<div class="icon-card">
<div class="icon-number">A</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<filter id="neonA" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="2.5" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0a0a1a"/>
<path d="M28 75 L28 25 L72 75 L72 25" stroke="#22c55e" stroke-width="6" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#neonA)"/>
<circle cx="50" cy="50" r="38" fill="none" stroke="#22c55e" stroke-width="1" opacity="0.3" filter="url(#neonA)"/>
<text x="50" y="94" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold" filter="url(#neonA)">GPT</text>
<circle cx="50" cy="6" r="4" fill="#fbbf24" filter="url(#neonA)"/>
</svg>
</div>
<div class="icon-name">Zielony Neon</div>
<div class="icon-desc">Klasyczny zielony, styl INPI</div>
</div>
<!-- N5-B: Cyan neon -->
<div class="icon-card">
<div class="icon-number">B</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<filter id="neonB" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0a0a1a"/>
<path d="M28 75 L28 25 L72 75 L72 25" stroke="#06b6d4" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#neonB)"/>
<circle cx="50" cy="50" r="36" fill="none" stroke="#06b6d4" stroke-width="1.5" opacity="0.4" filter="url(#neonB)"/>
<text x="50" y="94" text-anchor="middle" fill="#06b6d4" font-size="11" font-weight="bold" filter="url(#neonB)">GPT</text>
<circle cx="50" cy="6" r="4" fill="#fbbf24" filter="url(#neonB)"/>
</svg>
</div>
<div class="icon-name">Cyan Neon</div>
<div class="icon-desc">Turkusowy, futurystyczny</div>
</div>
<!-- N5-C: Multi-color -->
<div class="icon-card">
<div class="icon-number">C</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="neonGradC" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="50%" style="stop-color:#06b6d4"/>
<stop offset="100%" style="stop-color:#8b5cf6"/>
</linearGradient>
<filter id="neonC" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="2" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0a0a1a"/>
<path d="M28 75 L28 25 L72 75 L72 25" stroke="url(#neonGradC)" stroke-width="6" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#neonC)"/>
<text x="50" y="94" text-anchor="middle" fill="url(#neonGradC)" font-size="11" font-weight="bold" filter="url(#neonC)">GPT</text>
<circle cx="50" cy="6" r="4" fill="#fbbf24" filter="url(#neonC)"/>
</svg>
</div>
<div class="icon-name">Gradient Neon</div>
<div class="icon-desc">Zielono-fioletowy gradient</div>
</div>
<!-- N5-D: Pink neon -->
<div class="icon-card">
<div class="icon-number">D</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<filter id="neonD" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="2.5" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0a0a1a"/>
<path d="M28 75 L28 25 L72 75 L72 25" stroke="#ec4899" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#neonD)"/>
<circle cx="50" cy="50" r="36" fill="none" stroke="#ec4899" stroke-width="1" opacity="0.3" filter="url(#neonD)"/>
<text x="50" y="94" text-anchor="middle" fill="#ec4899" font-size="11" font-weight="bold" filter="url(#neonD)">GPT</text>
<circle cx="50" cy="6" r="4" fill="#fbbf24" filter="url(#neonD)"/>
</svg>
</div>
<div class="icon-name">Różowy Neon</div>
<div class="icon-desc">Magenta, wyrazisty</div>
</div>
<!-- N5-E: Double outline -->
<div class="icon-card">
<div class="icon-number">E</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<filter id="neonE" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="2" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#0a0a1a"/>
<!-- Outer N -->
<path d="M25 78 L25 22 L75 78 L75 22" stroke="#22c55e" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round" opacity="0.4" filter="url(#neonE)"/>
<!-- Inner N -->
<path d="M30 73 L30 27 L70 73 L70 27" stroke="#22c55e" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#neonE)"/>
<!-- GPT badge -->
<rect x="60" y="75" width="30" height="16" rx="4" fill="#22c55e" filter="url(#neonE)"/>
<text x="75" y="87" text-anchor="middle" fill="#0a0a1a" font-size="9" font-weight="bold">GPT</text>
<circle cx="50" cy="6" r="4" fill="#fbbf24" filter="url(#neonE)"/>
</svg>
</div>
<div class="icon-name">Podwójne N</div>
<div class="icon-desc">Efekt głębi z badge</div>
</div>
</div>
<hr class="section-divider">
<p style="text-align: center; color: rgba(255,255,255,0.4); margin-top: 1rem; font-size: 0.9rem;">
Podaj kombinację np. "R4-C" lub "N5-A" do wdrożenia
</p>
</body>
</html>

View File

@ -0,0 +1,317 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>NordaGPT - Propozycje ikon</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
padding: 2rem;
color: white;
}
h1 { text-align: center; margin-bottom: 2rem; font-weight: 300; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.icon-card {
background: rgba(255,255,255,0.05);
border-radius: 16px;
padding: 2rem;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
border: 1px solid rgba(255,255,255,0.1);
}
.icon-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0,0,0,0.3);
border-color: rgba(34, 197, 94, 0.5);
}
.icon-container {
width: 100px;
height: 100px;
margin: 0 auto 1rem;
display: flex;
align-items: center;
justify-content: center;
}
.icon-container svg { width: 100%; height: 100%; }
.icon-name { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.5rem; color: #22c55e; }
.icon-desc { font-size: 0.85rem; color: rgba(255,255,255,0.6); line-height: 1.4; }
.icon-number {
position: absolute;
top: 1rem;
left: 1rem;
background: #22c55e;
color: #1a1a2e;
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.9rem;
}
.icon-card { position: relative; }
</style>
</head>
<body>
<h1>NordaGPT - Propozycje ikon</h1>
<div class="grid">
<!-- 1. Minimalist North Star -->
<div class="icon-card">
<div class="icon-number">1</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="g1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="100%" style="stop-color:#059669"/>
</linearGradient>
<filter id="glow1"><feGaussianBlur stdDeviation="3" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<path d="M50 10 L55 40 L85 50 L55 60 L50 90 L45 60 L15 50 L45 40 Z" fill="url(#g1)" filter="url(#glow1)"/>
<circle cx="50" cy="50" r="12" fill="#233e6d"/>
<circle cx="50" cy="50" r="8" fill="#22c55e" opacity="0.8"/>
<circle cx="50" cy="15" r="4" fill="#fbbf24"/>
</svg>
</div>
<div class="icon-name">Gwiazda Polarna</div>
<div class="icon-desc">Minimalistyczna 4-ramienna gwiazda z akcentem na północ</div>
</div>
<!-- 2. Friendly Chat Bubble -->
<div class="icon-card">
<div class="icon-number">2</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="g2" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#8b5cf6"/>
</linearGradient>
</defs>
<rect x="10" y="15" width="80" height="55" rx="12" fill="url(#g2)"/>
<polygon points="25,70 40,70 30,85" fill="url(#g2)"/>
<circle cx="35" cy="42" r="6" fill="white" opacity="0.9"/>
<circle cx="50" cy="42" r="6" fill="white" opacity="0.9"/>
<circle cx="65" cy="42" r="6" fill="white" opacity="0.9"/>
<path d="M50 20 L52 28 L50 26 L48 28 Z" fill="#fbbf24"/>
</svg>
</div>
<div class="icon-name">Przyjazny Chat</div>
<div class="icon-desc">Dymek czatu z animowanymi kropkami i wskaźnikiem północy</div>
</div>
<!-- 3. Glowing Compass -->
<div class="icon-card">
<div class="icon-number">3</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<radialGradient id="g3" cx="50%" cy="50%" r="50%">
<stop offset="0%" style="stop-color:#22c55e;stop-opacity:0.3"/>
<stop offset="100%" style="stop-color:#22c55e;stop-opacity:0"/>
</radialGradient>
</defs>
<circle cx="50" cy="50" r="48" fill="#233e6d"/>
<circle cx="50" cy="50" r="40" fill="url(#g3)"/>
<circle cx="50" cy="50" r="35" fill="none" stroke="#22c55e" stroke-width="2" opacity="0.5"/>
<circle cx="50" cy="50" r="25" fill="none" stroke="#22c55e" stroke-width="1.5" opacity="0.3"/>
<!-- Compass needle -->
<path d="M50 20 L55 50 L50 55 L45 50 Z" fill="#ef4444"/>
<path d="M50 80 L55 50 L50 45 L45 50 Z" fill="white" opacity="0.8"/>
<circle cx="50" cy="50" r="5" fill="#22c55e"/>
<text x="50" y="15" text-anchor="middle" fill="#fbbf24" font-size="10" font-weight="bold">N</text>
</svg>
</div>
<div class="icon-name">Świecący Kompas</div>
<div class="icon-desc">Klasyczny kompas z efektem świecenia i czerwoną igłą</div>
</div>
<!-- 4. Abstract Brain Wave -->
<div class="icon-card">
<div class="icon-number">4</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<path d="M20 50 Q 30 30, 40 50 T 60 50 T 80 50" stroke="#22c55e" stroke-width="3" fill="none" opacity="0.6"/>
<path d="M20 50 Q 30 70, 40 50 T 60 50 T 80 50" stroke="#3b82f6" stroke-width="3" fill="none" opacity="0.6"/>
<path d="M25 50 Q 35 35, 45 50 T 65 50 T 85 50" stroke="#22c55e" stroke-width="2" fill="none"/>
<circle cx="50" cy="50" r="15" fill="#1a2d4f" stroke="#22c55e" stroke-width="2"/>
<text x="50" y="55" text-anchor="middle" fill="#22c55e" font-size="12" font-weight="bold">AI</text>
<circle cx="50" cy="12" r="4" fill="#fbbf24"/>
</svg>
</div>
<div class="icon-name">Fala Neuronowa</div>
<div class="icon-desc">Abstrakcyjne fale mózgowe symbolizujące AI</div>
</div>
<!-- 5. Lighthouse -->
<div class="icon-card">
<div class="icon-number">5</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Light rays -->
<path d="M50 25 L30 10 M50 25 L50 5 M50 25 L70 10" stroke="#fbbf24" stroke-width="2" opacity="0.6"/>
<path d="M50 25 L25 15 M50 25 L75 15" stroke="#fbbf24" stroke-width="1.5" opacity="0.4"/>
<!-- Lighthouse -->
<path d="M40 85 L45 40 L55 40 L60 85 Z" fill="white"/>
<rect x="43" y="30" width="14" height="12" rx="2" fill="#22c55e"/>
<rect x="46" y="33" width="8" height="6" fill="#fbbf24"/>
<rect x="42" y="50" width="16" height="4" fill="#ef4444"/>
<rect x="42" y="65" width="16" height="4" fill="#ef4444"/>
<path d="M38 85 L62 85 L60 90 L40 90 Z" fill="#64748b"/>
</svg>
</div>
<div class="icon-name">Latarnia Morska</div>
<div class="icon-desc">Symbolizuje przewodnictwo i wskazywanie drogi w biznesie</div>
</div>
<!-- 6. Cute Robot -->
<div class="icon-card">
<div class="icon-number">6</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Antenna -->
<line x1="50" y1="25" x2="50" y2="15" stroke="#22c55e" stroke-width="3"/>
<circle cx="50" cy="12" r="5" fill="#fbbf24"/>
<!-- Head -->
<rect x="25" y="28" width="50" height="45" rx="10" fill="#3b82f6"/>
<rect x="28" y="31" width="44" height="39" rx="8" fill="#60a5fa"/>
<!-- Eyes -->
<circle cx="38" cy="48" r="8" fill="white"/>
<circle cx="62" cy="48" r="8" fill="white"/>
<circle cx="40" cy="48" r="4" fill="#233e6d"/>
<circle cx="64" cy="48" r="4" fill="#233e6d"/>
<circle cx="41" cy="47" r="1.5" fill="white"/>
<circle cx="65" cy="47" r="1.5" fill="white"/>
<!-- Smile -->
<path d="M38 62 Q 50 72, 62 62" stroke="#233e6d" stroke-width="3" fill="none" stroke-linecap="round"/>
<!-- Ears -->
<rect x="18" y="40" width="8" height="15" rx="3" fill="#22c55e"/>
<rect x="74" y="40" width="8" height="15" rx="3" fill="#22c55e"/>
</svg>
</div>
<div class="icon-name">Przyjazny Robot</div>
<div class="icon-desc">Uroczy robot z uśmiechem i anteną wskazującą północ</div>
</div>
<!-- 7. Magic Sparkle -->
<div class="icon-card">
<div class="icon-number">7</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="g7" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#8b5cf6"/>
<stop offset="100%" style="stop-color:#ec4899"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Main sparkle -->
<path d="M50 15 L53 45 L83 50 L53 55 L50 85 L47 55 L17 50 L47 45 Z" fill="url(#g7)" opacity="0.8"/>
<!-- Small sparkles -->
<path d="M25 25 L27 32 L34 34 L27 36 L25 43 L23 36 L16 34 L23 32 Z" fill="#fbbf24"/>
<path d="M75 70 L76 74 L80 75 L76 76 L75 80 L74 76 L70 75 L74 74 Z" fill="#22c55e"/>
<path d="M78 25 L79 28 L82 29 L79 30 L78 33 L77 30 L74 29 L77 28 Z" fill="#fbbf24"/>
<circle cx="50" cy="50" r="8" fill="white"/>
<text x="50" y="54" text-anchor="middle" fill="#8b5cf6" font-size="10" font-weight="bold">AI</text>
</svg>
</div>
<div class="icon-name">Magiczna Iskra</div>
<div class="icon-desc">Błyszcząca gwiazda symbolizująca magię AI</div>
</div>
<!-- 8. Circle N -->
<div class="icon-card">
<div class="icon-number">8</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="g8" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="100%" style="stop-color:#14b8a6"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<circle cx="50" cy="50" r="38" fill="none" stroke="url(#g8)" stroke-width="3"/>
<!-- Letter N stylized -->
<path d="M32 70 L32 30 L68 70 L68 30" stroke="url(#g8)" stroke-width="8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- North dot -->
<circle cx="50" cy="8" r="5" fill="#fbbf24"/>
<!-- Small AI badge -->
<circle cx="75" cy="75" r="12" fill="#22c55e"/>
<text x="75" y="79" text-anchor="middle" fill="white" font-size="9" font-weight="bold">AI</text>
</svg>
</div>
<div class="icon-name">Litera N</div>
<div class="icon-desc">Stylizowana litera N (Norda) z badge AI</div>
</div>
<!-- 9. Orbit -->
<div class="icon-card">
<div class="icon-number">9</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Orbits -->
<ellipse cx="50" cy="50" rx="35" ry="15" fill="none" stroke="#22c55e" stroke-width="1.5" opacity="0.5" transform="rotate(-30 50 50)"/>
<ellipse cx="50" cy="50" rx="35" ry="15" fill="none" stroke="#3b82f6" stroke-width="1.5" opacity="0.5" transform="rotate(30 50 50)"/>
<ellipse cx="50" cy="50" rx="35" ry="15" fill="none" stroke="#8b5cf6" stroke-width="1.5" opacity="0.5" transform="rotate(90 50 50)"/>
<!-- Center -->
<circle cx="50" cy="50" r="15" fill="#1a2d4f" stroke="#22c55e" stroke-width="2"/>
<text x="50" y="54" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold">AI</text>
<!-- Orbit dots -->
<circle cx="50" cy="15" r="5" fill="#fbbf24"/>
<circle cx="80" cy="60" r="4" fill="#22c55e"/>
<circle cx="25" cy="35" r="4" fill="#3b82f6"/>
</svg>
</div>
<div class="icon-name">Orbita</div>
<div class="icon-desc">Orbity symbolizujące połączenia i sieć biznesową</div>
</div>
<!-- 10. Gradient Compass Simple -->
<div class="icon-card">
<div class="icon-number">10</div>
<div class="icon-container">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="g10" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="50%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#8b5cf6"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="#233e6d"/>
<!-- Simple compass with gradient -->
<circle cx="50" cy="50" r="35" fill="none" stroke="url(#g10)" stroke-width="3"/>
<line x1="50" y1="20" x2="50" y2="35" stroke="#fbbf24" stroke-width="4" stroke-linecap="round"/>
<line x1="50" y1="65" x2="50" y2="80" stroke="url(#g10)" stroke-width="3" stroke-linecap="round"/>
<line x1="20" y1="50" x2="35" y2="50" stroke="url(#g10)" stroke-width="3" stroke-linecap="round"/>
<line x1="65" y1="50" x2="80" y2="50" stroke="url(#g10)" stroke-width="3" stroke-linecap="round"/>
<!-- Center dot -->
<circle cx="50" cy="50" r="12" fill="#1a2d4f"/>
<circle cx="50" cy="50" r="8" fill="url(#g10)"/>
<text x="50" y="10" text-anchor="middle" fill="#fbbf24" font-size="8" font-weight="bold">N</text>
</svg>
</div>
<div class="icon-name">Prosty Kompas</div>
<div class="icon-desc">Elegancki, minimalistyczny kompas z gradientem</div>
</div>
</div>
</body>
</html>