tacilytics 0.0.0-stage → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +23 -0
- package/LICENSE +21 -0
- package/README.md +253 -2
- package/cli.js +173 -0
- package/day-salt.js +55 -0
- package/package.json +56 -4
- package/public/app.js +788 -0
- package/public/assets/DISCLAIMER-simple-icons.md +51 -0
- package/public/assets/LICENSE-lucide.txt +43 -0
- package/public/assets/LICENSE-simple-icons.md +30 -0
- package/public/assets/cosmic-bg.webp +0 -0
- package/public/assets/flags/LICENSE-flag-icons.txt +21 -0
- package/public/assets/flags/ad.svg +150 -0
- package/public/assets/flags/ae.svg +6 -0
- package/public/assets/flags/af.svg +81 -0
- package/public/assets/flags/ag.svg +14 -0
- package/public/assets/flags/ai.svg +29 -0
- package/public/assets/flags/al.svg +5 -0
- package/public/assets/flags/am.svg +5 -0
- package/public/assets/flags/ao.svg +13 -0
- package/public/assets/flags/aq.svg +5 -0
- package/public/assets/flags/ar.svg +32 -0
- package/public/assets/flags/arab.svg +109 -0
- package/public/assets/flags/as.svg +72 -0
- package/public/assets/flags/asean.svg +13 -0
- package/public/assets/flags/at.svg +4 -0
- package/public/assets/flags/au.svg +8 -0
- package/public/assets/flags/aw.svg +186 -0
- package/public/assets/flags/ax.svg +18 -0
- package/public/assets/flags/az.svg +8 -0
- package/public/assets/flags/ba.svg +12 -0
- package/public/assets/flags/bb.svg +6 -0
- package/public/assets/flags/bd.svg +4 -0
- package/public/assets/flags/be.svg +7 -0
- package/public/assets/flags/bf.svg +7 -0
- package/public/assets/flags/bg.svg +5 -0
- package/public/assets/flags/bh.svg +4 -0
- package/public/assets/flags/bi.svg +15 -0
- package/public/assets/flags/bj.svg +14 -0
- package/public/assets/flags/bl.svg +5 -0
- package/public/assets/flags/bm.svg +97 -0
- package/public/assets/flags/bn.svg +36 -0
- package/public/assets/flags/bo.svg +673 -0
- package/public/assets/flags/bq.svg +5 -0
- package/public/assets/flags/br.svg +45 -0
- package/public/assets/flags/bs.svg +13 -0
- package/public/assets/flags/bt.svg +89 -0
- package/public/assets/flags/bv.svg +13 -0
- package/public/assets/flags/bw.svg +7 -0
- package/public/assets/flags/by.svg +18 -0
- package/public/assets/flags/bz.svg +145 -0
- package/public/assets/flags/ca.svg +4 -0
- package/public/assets/flags/cc.svg +19 -0
- package/public/assets/flags/cd.svg +5 -0
- package/public/assets/flags/cefta.svg +13 -0
- package/public/assets/flags/cf.svg +15 -0
- package/public/assets/flags/cg.svg +12 -0
- package/public/assets/flags/ch.svg +9 -0
- package/public/assets/flags/ci.svg +7 -0
- package/public/assets/flags/ck.svg +9 -0
- package/public/assets/flags/cl.svg +13 -0
- package/public/assets/flags/cm.svg +15 -0
- package/public/assets/flags/cn.svg +11 -0
- package/public/assets/flags/co.svg +7 -0
- package/public/assets/flags/cp.svg +7 -0
- package/public/assets/flags/cr.svg +7 -0
- package/public/assets/flags/cu.svg +13 -0
- package/public/assets/flags/cv.svg +13 -0
- package/public/assets/flags/cw.svg +14 -0
- package/public/assets/flags/cx.svg +15 -0
- package/public/assets/flags/cy.svg +6 -0
- package/public/assets/flags/cz.svg +5 -0
- package/public/assets/flags/de.svg +5 -0
- package/public/assets/flags/dg.svg +130 -0
- package/public/assets/flags/dj.svg +13 -0
- package/public/assets/flags/dk.svg +5 -0
- package/public/assets/flags/dm.svg +152 -0
- package/public/assets/flags/do.svg +121 -0
- package/public/assets/flags/dz.svg +5 -0
- package/public/assets/flags/eac.svg +48 -0
- package/public/assets/flags/ec.svg +138 -0
- package/public/assets/flags/ee.svg +5 -0
- package/public/assets/flags/eg.svg +38 -0
- package/public/assets/flags/eh.svg +16 -0
- package/public/assets/flags/er.svg +8 -0
- package/public/assets/flags/es-ct.svg +4 -0
- package/public/assets/flags/es-ga.svg +187 -0
- package/public/assets/flags/es-pv.svg +5 -0
- package/public/assets/flags/es.svg +544 -0
- package/public/assets/flags/et.svg +14 -0
- package/public/assets/flags/eu.svg +28 -0
- package/public/assets/flags/fi.svg +5 -0
- package/public/assets/flags/fj.svg +120 -0
- package/public/assets/flags/fk.svg +90 -0
- package/public/assets/flags/fm.svg +11 -0
- package/public/assets/flags/fo.svg +12 -0
- package/public/assets/flags/fr.svg +5 -0
- package/public/assets/flags/ga.svg +7 -0
- package/public/assets/flags/gb-eng.svg +5 -0
- package/public/assets/flags/gb-nir.svg +132 -0
- package/public/assets/flags/gb-sct.svg +4 -0
- package/public/assets/flags/gb-wls.svg +9 -0
- package/public/assets/flags/gb.svg +7 -0
- package/public/assets/flags/gd.svg +27 -0
- package/public/assets/flags/ge.svg +6 -0
- package/public/assets/flags/gf.svg +5 -0
- package/public/assets/flags/gg.svg +9 -0
- package/public/assets/flags/gh.svg +6 -0
- package/public/assets/flags/gi.svg +32 -0
- package/public/assets/flags/gl.svg +4 -0
- package/public/assets/flags/gm.svg +14 -0
- package/public/assets/flags/gn.svg +7 -0
- package/public/assets/flags/gp.svg +5 -0
- package/public/assets/flags/gq.svg +23 -0
- package/public/assets/flags/gr.svg +16 -0
- package/public/assets/flags/gs.svg +133 -0
- package/public/assets/flags/gt.svg +204 -0
- package/public/assets/flags/gu.svg +19 -0
- package/public/assets/flags/gw.svg +13 -0
- package/public/assets/flags/gy.svg +9 -0
- package/public/assets/flags/hk.svg +8 -0
- package/public/assets/flags/hm.svg +8 -0
- package/public/assets/flags/hn.svg +18 -0
- package/public/assets/flags/hr.svg +58 -0
- package/public/assets/flags/ht.svg +116 -0
- package/public/assets/flags/hu.svg +7 -0
- package/public/assets/flags/ic.svg +7 -0
- package/public/assets/flags/id.svg +4 -0
- package/public/assets/flags/ie.svg +7 -0
- package/public/assets/flags/il.svg +14 -0
- package/public/assets/flags/im.svg +36 -0
- package/public/assets/flags/in.svg +25 -0
- package/public/assets/flags/io.svg +130 -0
- package/public/assets/flags/iq.svg +10 -0
- package/public/assets/flags/ir.svg +219 -0
- package/public/assets/flags/is.svg +12 -0
- package/public/assets/flags/it.svg +7 -0
- package/public/assets/flags/je.svg +62 -0
- package/public/assets/flags/jm.svg +8 -0
- package/public/assets/flags/jo.svg +16 -0
- package/public/assets/flags/jp.svg +11 -0
- package/public/assets/flags/ke.svg +23 -0
- package/public/assets/flags/kg.svg +4 -0
- package/public/assets/flags/kh.svg +61 -0
- package/public/assets/flags/ki.svg +36 -0
- package/public/assets/flags/km.svg +16 -0
- package/public/assets/flags/kn.svg +14 -0
- package/public/assets/flags/kp.svg +15 -0
- package/public/assets/flags/kr.svg +24 -0
- package/public/assets/flags/kw.svg +13 -0
- package/public/assets/flags/ky.svg +103 -0
- package/public/assets/flags/kz.svg +36 -0
- package/public/assets/flags/la.svg +12 -0
- package/public/assets/flags/lb.svg +15 -0
- package/public/assets/flags/lc.svg +8 -0
- package/public/assets/flags/li.svg +43 -0
- package/public/assets/flags/lk.svg +22 -0
- package/public/assets/flags/lr.svg +14 -0
- package/public/assets/flags/ls.svg +8 -0
- package/public/assets/flags/lt.svg +7 -0
- package/public/assets/flags/lu.svg +5 -0
- package/public/assets/flags/lv.svg +6 -0
- package/public/assets/flags/ly.svg +13 -0
- package/public/assets/flags/ma.svg +4 -0
- package/public/assets/flags/mc.svg +6 -0
- package/public/assets/flags/md.svg +70 -0
- package/public/assets/flags/me.svg +116 -0
- package/public/assets/flags/mf.svg +5 -0
- package/public/assets/flags/mg.svg +7 -0
- package/public/assets/flags/mh.svg +7 -0
- package/public/assets/flags/mk.svg +5 -0
- package/public/assets/flags/ml.svg +7 -0
- package/public/assets/flags/mm.svg +12 -0
- package/public/assets/flags/mn.svg +14 -0
- package/public/assets/flags/mo.svg +9 -0
- package/public/assets/flags/mp.svg +86 -0
- package/public/assets/flags/mq.svg +5 -0
- package/public/assets/flags/mr.svg +6 -0
- package/public/assets/flags/ms.svg +29 -0
- package/public/assets/flags/mt.svg +58 -0
- package/public/assets/flags/mu.svg +8 -0
- package/public/assets/flags/mv.svg +6 -0
- package/public/assets/flags/mw.svg +10 -0
- package/public/assets/flags/mx.svg +382 -0
- package/public/assets/flags/my.svg +26 -0
- package/public/assets/flags/mz.svg +21 -0
- package/public/assets/flags/na.svg +16 -0
- package/public/assets/flags/nc.svg +13 -0
- package/public/assets/flags/ne.svg +6 -0
- package/public/assets/flags/nf.svg +9 -0
- package/public/assets/flags/ng.svg +6 -0
- package/public/assets/flags/ni.svg +129 -0
- package/public/assets/flags/nl.svg +5 -0
- package/public/assets/flags/no.svg +7 -0
- package/public/assets/flags/np.svg +13 -0
- package/public/assets/flags/nr.svg +12 -0
- package/public/assets/flags/nu.svg +10 -0
- package/public/assets/flags/nz.svg +36 -0
- package/public/assets/flags/om.svg +115 -0
- package/public/assets/flags/pa.svg +14 -0
- package/public/assets/flags/pc.svg +33 -0
- package/public/assets/flags/pe.svg +4 -0
- package/public/assets/flags/pf.svg +19 -0
- package/public/assets/flags/pg.svg +9 -0
- package/public/assets/flags/ph.svg +6 -0
- package/public/assets/flags/pk.svg +15 -0
- package/public/assets/flags/pl.svg +6 -0
- package/public/assets/flags/pm.svg +5 -0
- package/public/assets/flags/pn.svg +53 -0
- package/public/assets/flags/pr.svg +13 -0
- package/public/assets/flags/ps.svg +6 -0
- package/public/assets/flags/pt.svg +57 -0
- package/public/assets/flags/pw.svg +11 -0
- package/public/assets/flags/py.svg +157 -0
- package/public/assets/flags/qa.svg +4 -0
- package/public/assets/flags/re.svg +5 -0
- package/public/assets/flags/ro.svg +7 -0
- package/public/assets/flags/rs.svg +292 -0
- package/public/assets/flags/ru.svg +5 -0
- package/public/assets/flags/rw.svg +13 -0
- package/public/assets/flags/sa.svg +25 -0
- package/public/assets/flags/sb.svg +13 -0
- package/public/assets/flags/sc.svg +7 -0
- package/public/assets/flags/sd.svg +13 -0
- package/public/assets/flags/se.svg +4 -0
- package/public/assets/flags/sg.svg +13 -0
- package/public/assets/flags/sh-ac.svg +689 -0
- package/public/assets/flags/sh-hl.svg +164 -0
- package/public/assets/flags/sh-ta.svg +76 -0
- package/public/assets/flags/sh.svg +7 -0
- package/public/assets/flags/si.svg +18 -0
- package/public/assets/flags/sj.svg +7 -0
- package/public/assets/flags/sk.svg +9 -0
- package/public/assets/flags/sl.svg +7 -0
- package/public/assets/flags/sm.svg +75 -0
- package/public/assets/flags/sn.svg +8 -0
- package/public/assets/flags/so.svg +11 -0
- package/public/assets/flags/sr.svg +6 -0
- package/public/assets/flags/ss.svg +8 -0
- package/public/assets/flags/st.svg +16 -0
- package/public/assets/flags/sv.svg +593 -0
- package/public/assets/flags/sx.svg +56 -0
- package/public/assets/flags/sy.svg +6 -0
- package/public/assets/flags/sz.svg +34 -0
- package/public/assets/flags/tc.svg +50 -0
- package/public/assets/flags/td.svg +7 -0
- package/public/assets/flags/tf.svg +15 -0
- package/public/assets/flags/tg.svg +14 -0
- package/public/assets/flags/th.svg +7 -0
- package/public/assets/flags/tj.svg +22 -0
- package/public/assets/flags/tk.svg +5 -0
- package/public/assets/flags/tl.svg +13 -0
- package/public/assets/flags/tm.svg +204 -0
- package/public/assets/flags/tn.svg +4 -0
- package/public/assets/flags/to.svg +10 -0
- package/public/assets/flags/tr.svg +8 -0
- package/public/assets/flags/tt.svg +5 -0
- package/public/assets/flags/tv.svg +9 -0
- package/public/assets/flags/tw.svg +34 -0
- package/public/assets/flags/tz.svg +13 -0
- package/public/assets/flags/ua.svg +6 -0
- package/public/assets/flags/ug.svg +30 -0
- package/public/assets/flags/um.svg +9 -0
- package/public/assets/flags/un.svg +16 -0
- package/public/assets/flags/us.svg +9 -0
- package/public/assets/flags/uy.svg +28 -0
- package/public/assets/flags/uz.svg +30 -0
- package/public/assets/flags/va.svg +190 -0
- package/public/assets/flags/vc.svg +8 -0
- package/public/assets/flags/ve.svg +26 -0
- package/public/assets/flags/vg.svg +59 -0
- package/public/assets/flags/vi.svg +28 -0
- package/public/assets/flags/vn.svg +11 -0
- package/public/assets/flags/vu.svg +21 -0
- package/public/assets/flags/wf.svg +5 -0
- package/public/assets/flags/ws.svg +7 -0
- package/public/assets/flags/xk.svg +5 -0
- package/public/assets/flags/xx.svg +4 -0
- package/public/assets/flags/ye.svg +7 -0
- package/public/assets/flags/yt.svg +5 -0
- package/public/assets/flags/za.svg +17 -0
- package/public/assets/flags/zm.svg +27 -0
- package/public/assets/flags/zw.svg +21 -0
- package/public/assets/fonts/Inter-OFL.txt +93 -0
- package/public/assets/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
- package/public/assets/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/public/assets/icons.svg +200 -0
- package/public/assets/nyx-logo-256.png +0 -0
- package/public/assets/tacilytics-mark.svg +16 -0
- package/public/index.html +239 -0
- package/public/style.css +415 -0
- package/public/tacilytics.js +184 -0
- package/sdk/flutter/CHANGELOG.md +6 -0
- package/sdk/flutter/LICENSE +21 -0
- package/sdk/flutter/README.md +41 -0
- package/sdk/flutter/dart_test.yaml +3 -0
- package/sdk/flutter/lib/nyx_analytics.dart +13 -0
- package/sdk/flutter/lib/src/beacon.dart +87 -0
- package/sdk/flutter/lib/src/client.dart +177 -0
- package/sdk/flutter/lib/tacilytics.dart +13 -0
- package/sdk/flutter/pubspec.lock +221 -0
- package/sdk/flutter/pubspec.yaml +18 -0
- package/sdk/flutter/test/alias_test.dart +13 -0
- package/sdk/flutter/test/beacon_test.dart +46 -0
- package/sdk/flutter/test/e2e_test.dart +18 -0
- package/server.js +896 -0
- package/tz-country.js +32 -0
package/public/style.css
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/* Inter (SIL OFL 1.1, see assets/fonts/Inter-OFL.txt) — self-hosted, no third-party requests */
|
|
2
|
+
@font-face { font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 100 900; src: url('/assets/fonts/inter-latin-wght-normal.woff2') format('woff2-variations'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
|
|
3
|
+
@font-face { font-family: 'Inter'; font-style: normal; font-display: swap; font-weight: 100 900; src: url('/assets/fonts/inter-latin-ext-wght-normal.woff2') format('woff2-variations'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
|
|
4
|
+
|
|
5
|
+
/* Tacilytics — calm, professional dark theme (14.10.2026). One accent, no glow, numbers first. */
|
|
6
|
+
:root {
|
|
7
|
+
--accent: #5b8def;
|
|
8
|
+
--accent-2: #34d399;
|
|
9
|
+
--accent-soft: rgba(91, 141, 239, 0.14);
|
|
10
|
+
--accent-line: rgba(91, 141, 239, 0.45);
|
|
11
|
+
--green: #34d399;
|
|
12
|
+
--red: #f87171;
|
|
13
|
+
--yellow: #fbbf24;
|
|
14
|
+
--bg: #0f1115;
|
|
15
|
+
--bg2: #14161c;
|
|
16
|
+
--surface: #171a21;
|
|
17
|
+
--surface-2: #1d2129;
|
|
18
|
+
--line: #262b35;
|
|
19
|
+
--line-strong: #323946;
|
|
20
|
+
--text: #e6e8ec;
|
|
21
|
+
--text-dim: #8b93a3;
|
|
22
|
+
--text-faint: #5f6675;
|
|
23
|
+
/* legacy names, mapped so older rules keep rendering in the new palette */
|
|
24
|
+
--pink: var(--accent); --purple: var(--accent); --violet: var(--accent); --magenta: var(--accent); --cyan: #8fb3f5;
|
|
25
|
+
--glass: var(--surface); --glass-border: var(--line);
|
|
26
|
+
--glow-pink: transparent; --glow-purple: transparent;
|
|
27
|
+
--grad: var(--accent);
|
|
28
|
+
--grad-text: none;
|
|
29
|
+
--radius: 12px;
|
|
30
|
+
--radius-sm: 8px;
|
|
31
|
+
--radius-lg: 14px;
|
|
32
|
+
/* chart colours read by app.js */
|
|
33
|
+
--chart-visitors: #5b8def;
|
|
34
|
+
--chart-pageviews: #8b93a3;
|
|
35
|
+
}
|
|
36
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
37
|
+
html { scroll-behavior: smooth; }
|
|
38
|
+
body {
|
|
39
|
+
font-family: 'Inter', ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
40
|
+
font-feature-settings: 'cv11', 'ss01', 'tnum';
|
|
41
|
+
background: var(--bg); color: var(--text); line-height: 1.5; overflow-x: hidden; min-height: 100vh;
|
|
42
|
+
-webkit-font-smoothing: antialiased;
|
|
43
|
+
}
|
|
44
|
+
a { color: var(--accent); text-decoration: none; }
|
|
45
|
+
a:hover { text-decoration: underline; }
|
|
46
|
+
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
47
|
+
|
|
48
|
+
.container { max-width: 1140px; margin: 0 auto; padding: 22px; }
|
|
49
|
+
.grad-text { color: var(--text); }
|
|
50
|
+
|
|
51
|
+
/* ── Header ── */
|
|
52
|
+
header { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 6px 0 18px; border-bottom: 1px solid var(--line); margin-bottom: 22px; flex-wrap: wrap; }
|
|
53
|
+
.brand { display: flex; align-items: center; gap: 13px; }
|
|
54
|
+
.brand .logo { width: 34px; height: 34px; border-radius: 9px; }
|
|
55
|
+
|
|
56
|
+
.brand h1 { font-size: 1.15rem; font-weight: 650; letter-spacing: -.01em; line-height: 1.1; }
|
|
57
|
+
.brand .subtitle { font-size: 0.74rem; color: var(--text-dim); margin-top: 2px; }
|
|
58
|
+
.header-right { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
59
|
+
|
|
60
|
+
.btn { border: 1px solid var(--accent); cursor: pointer; font-weight: 600; border-radius: var(--radius-sm); padding: 8px 14px; font-size: 0.84rem; font-family: inherit; transition: background .15s, border-color .15s; color: #fff; background: var(--accent); }
|
|
61
|
+
.btn:hover { background: #4a7de0; border-color: #4a7de0; }
|
|
62
|
+
.btn.ghost { background: var(--surface); border: 1px solid var(--line); color: var(--text); }
|
|
63
|
+
.btn.ghost:hover { background: var(--surface-2); border-color: var(--line-strong); }
|
|
64
|
+
.btn.danger:hover { border-color: var(--red); color: var(--red); background: rgba(248,113,113,.06); }
|
|
65
|
+
|
|
66
|
+
select, input { background: var(--surface); border: 1px solid var(--line); color: var(--text); padding: 8px 12px; border-radius: var(--radius-sm); font-size: 0.84rem; font-family: inherit; outline: none; transition: border .15s; }
|
|
67
|
+
select:focus, input:focus { border-color: var(--accent-line); }
|
|
68
|
+
.realtime { display: flex; align-items: center; gap: 7px; font-size: 0.8rem; color: var(--text-dim); padding: 6px 12px; background: var(--surface); border: 1px solid var(--line); border-radius: 999px; }
|
|
69
|
+
.realtime .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); animation: pulse 2s infinite; }
|
|
70
|
+
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
|
|
71
|
+
|
|
72
|
+
/* ── toolbar ── */
|
|
73
|
+
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 22px; flex-wrap: wrap; }
|
|
74
|
+
.periods { display: inline-flex; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 3px; gap: 2px; }
|
|
75
|
+
.periods button { background: transparent; border: none; color: var(--text-dim); padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; font-weight: 550; font-family: inherit; transition: background .15s, color .15s; }
|
|
76
|
+
.periods button.active { background: var(--surface-2); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-strong); }
|
|
77
|
+
.periods button:hover:not(.active) { color: var(--text); }
|
|
78
|
+
|
|
79
|
+
/* ── toolbar right (range + actions) ── */
|
|
80
|
+
.toolbar-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
81
|
+
.range { display: inline-flex; align-items: center; gap: 6px; }
|
|
82
|
+
.range input[type=date] { padding: 8px 10px; font-size: 0.8rem; color-scheme: dark; }
|
|
83
|
+
.range .arrow { color: var(--text-dim); }
|
|
84
|
+
|
|
85
|
+
/* ── segmentation filter chips ── */
|
|
86
|
+
.filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 18px; }
|
|
87
|
+
.filters .flabel { font-size: 0.74rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.08em; }
|
|
88
|
+
.chip { display: inline-flex; align-items: center; gap: 7px; padding: 5px 10px; border-radius: 999px; font-size: 0.78rem; font-weight: 550; background: var(--accent-soft); border: 1px solid var(--accent-line); color: var(--text); }
|
|
89
|
+
.chip .k { color: var(--text-dim); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
90
|
+
.chip button { background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 1rem; line-height: 1; padding: 0; transition: color .2s; }
|
|
91
|
+
.chip button:hover { color: var(--pink); }
|
|
92
|
+
.chip.clear { background: transparent; border-color: transparent; color: var(--text-dim); cursor: pointer; }
|
|
93
|
+
.chip.clear:hover { color: var(--pink); }
|
|
94
|
+
|
|
95
|
+
/* ── panel header tabs ── */
|
|
96
|
+
.panel h3 .tabs { margin-left: auto; display: inline-flex; gap: 2px; background: var(--bg2); border: 1px solid var(--line); border-radius: 7px; padding: 2px; }
|
|
97
|
+
.panel h3 .tabs button { background: transparent; border: none; color: var(--text-dim); padding: 3px 9px; border-radius: 5px; cursor: pointer; font-size: 0.72rem; font-weight: 550; font-family: inherit; transition: all .15s; }
|
|
98
|
+
.panel h3 .tabs button.active { background: var(--surface-2); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-strong); }
|
|
99
|
+
|
|
100
|
+
/* ── glass cards ── */
|
|
101
|
+
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px 20px; position: relative; overflow: hidden; }
|
|
102
|
+
.card:hover { border-color: var(--line-strong); }
|
|
103
|
+
.stats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; margin-bottom: 18px; }
|
|
104
|
+
.stat .label { font-size: 0.74rem; color: var(--text-dim); font-weight: 550; margin-bottom: 6px; }
|
|
105
|
+
.stat .value { font-size: 1.75rem; font-weight: 650; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
|
|
106
|
+
.trend { font-size: 0.74rem; font-weight: 700; margin-top: 6px; display: inline-block; }
|
|
107
|
+
.trend.up { color: var(--green); }
|
|
108
|
+
.trend.down { color: var(--red); }
|
|
109
|
+
|
|
110
|
+
/* ── chart ── */
|
|
111
|
+
.chart-card { padding: 22px; margin-bottom: 18px; }
|
|
112
|
+
.chart-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
|
113
|
+
.chart-head strong { font-size: 0.92rem; font-weight: 600; }
|
|
114
|
+
.chart-head .legend { display: flex; gap: 14px; font-size: 0.78rem; color: var(--text-dim); }
|
|
115
|
+
.chart-head .legend span { display: inline-flex; align-items: center; gap: 6px; }
|
|
116
|
+
.chart-head .legend .leg { display: inline-flex; align-items: center; gap: 6px; background: none; border: 0; padding: 3px 8px; border-radius: 8px; font: inherit; color: var(--text-dim); cursor: pointer; transition: background .15s, opacity .15s; }
|
|
117
|
+
.chart-head .legend .leg:hover { background: rgba(255,255,255,.06); }
|
|
118
|
+
.chart-head .legend .leg[aria-pressed="false"] { opacity: .38; text-decoration: line-through; }
|
|
119
|
+
.chart-head .legend .leg[aria-pressed="false"] .swatch { background: var(--text-dim) !important; }
|
|
120
|
+
.swatch { width: 10px; height: 10px; border-radius: 3px; }
|
|
121
|
+
#chart { width: 100%; height: 220px; display: block; }
|
|
122
|
+
.tip { position: fixed; pointer-events: none; background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: 8px; padding: 7px 10px; font-size: 0.78rem; opacity: 0; transition: opacity .12s; z-index: 50; white-space: nowrap; box-shadow: 0 8px 24px rgba(0,0,0,.4); }
|
|
123
|
+
|
|
124
|
+
/* ── panels ── */
|
|
125
|
+
.panels { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
126
|
+
.panel h3 { font-size: 0.86rem; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--text); }
|
|
127
|
+
.panel h3 .em { font-size: .95rem; filter: grayscale(1); opacity: .7; }
|
|
128
|
+
.bars { display: flex; flex-direction: column; gap: 3px; }
|
|
129
|
+
.bar { position: relative; display: flex; align-items: center; justify-content: space-between; padding: 7px 10px; border-radius: 6px; font-size: 0.84rem; overflow: hidden; }
|
|
130
|
+
.bar .fill { position: absolute; inset: 0 auto 0 0; background: var(--accent-soft); border-radius: 6px; z-index: 0; transition: width .4s cubic-bezier(.4,0,.2,1); }
|
|
131
|
+
.bar .name { position: relative; z-index: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 76%; }
|
|
132
|
+
.bar .val { position: relative; z-index: 1; color: var(--text-dim); font-variant-numeric: tabular-nums; font-weight: 600; }
|
|
133
|
+
.bar:hover .fill { background: rgba(91,141,239,.22); }
|
|
134
|
+
.bar.clickable { cursor: pointer; }
|
|
135
|
+
.bar.clickable:hover { box-shadow: inset 0 0 0 1px var(--accent-line); }
|
|
136
|
+
.bar .cr { position: relative; z-index: 1; color: var(--accent); font-size: 0.74rem; font-weight: 600; margin-left: 8px; font-variant-numeric: tabular-nums; }
|
|
137
|
+
.empty { color: var(--text-dim); font-size: 0.85rem; text-align: center; padding: 26px 0; }
|
|
138
|
+
|
|
139
|
+
/* ── modals ── */
|
|
140
|
+
.modal-overlay { position: fixed; inset: 0; background: rgba(5,6,9,.66); display: flex; align-items: center; justify-content: center; z-index: 1000; animation: fadeIn .2s ease; padding: 20px; }
|
|
141
|
+
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
142
|
+
.modal { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); padding: 32px 30px; width: 100%; max-width: 420px; text-align: center; position: relative; overflow: hidden; box-shadow: 0 24px 64px rgba(0,0,0,.5); animation: slideUp .25s cubic-bezier(.16,1,.3,1); }
|
|
143
|
+
|
|
144
|
+
@keyframes slideUp { from { transform: translateY(20px) scale(.97); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } }
|
|
145
|
+
.modal .logo { width: 48px; height: 48px; margin: 0 auto 14px; display: block; border-radius: 12px; }
|
|
146
|
+
.modal .logo.em { font-size: 2rem; width: auto; height: auto; filter: grayscale(1); opacity: .75; }
|
|
147
|
+
.modal h2 { font-size: 1.2rem; font-weight: 650; margin-bottom: 6px; }
|
|
148
|
+
.modal p { color: var(--text-dim); font-size: 0.88rem; margin-bottom: 22px; }
|
|
149
|
+
.modal input { width: 100%; margin-bottom: 14px; text-align: center; }
|
|
150
|
+
.modal .btn { width: 100%; padding: 13px; }
|
|
151
|
+
.err { color: var(--red); font-size: 0.82rem; min-height: 18px; margin-bottom: 8px; }
|
|
152
|
+
.snippet { background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px; font-family: ui-monospace, 'JetBrains Mono', Menlo, monospace; font-size: 0.76rem; color: #c9d4e8; text-align: left; word-break: break-all; margin: 12px 0; line-height: 1.55; }
|
|
153
|
+
/* ── live visitors modal ── */
|
|
154
|
+
.modal.live { max-width: 600px; text-align: left; }
|
|
155
|
+
.live-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
156
|
+
.live-head h2 { margin: 0; }
|
|
157
|
+
.live-sub { color: var(--text-dim); font-size: 0.82rem; margin: 6px 0 16px; }
|
|
158
|
+
#live-spark { width: 100%; height: 70px; display: block; margin-bottom: 18px; }
|
|
159
|
+
.feed-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-dim); margin-bottom: 8px; }
|
|
160
|
+
.feed { max-height: 340px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
|
|
161
|
+
.feed-row { display: flex; align-items: center; gap: 12px; padding: 9px 11px; border-radius: 10px; transition: background .2s; }
|
|
162
|
+
.feed-row:hover { background: var(--surface-2); }
|
|
163
|
+
.feed-row .fl { font-size: 1.1rem; flex-shrink: 0; }
|
|
164
|
+
.feed-row .fw { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
165
|
+
.feed-row .path { font-weight: 600; font-size: 0.88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
166
|
+
.feed-row .goal { font-weight: 600; font-size: 0.86rem; color: var(--accent); }
|
|
167
|
+
.feed-row .fm { font-size: 0.74rem; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
168
|
+
.feed-row .ft { font-size: 0.74rem; color: var(--text-dim); flex-shrink: 0; font-variant-numeric: tabular-nums; }
|
|
169
|
+
.feed::-webkit-scrollbar { width: 6px; }
|
|
170
|
+
.feed::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 3px; }
|
|
171
|
+
|
|
172
|
+
.hide { display: none !important; }
|
|
173
|
+
|
|
174
|
+
@media (max-width: 760px) { .stats { grid-template-columns: repeat(2, 1fr); } .panels { grid-template-columns: 1fr; } }
|
|
175
|
+
|
|
176
|
+
/* ── funnels ── */
|
|
177
|
+
.funnels { display: flex; flex-direction: column; gap: 18px; }
|
|
178
|
+
.funnel-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
|
|
179
|
+
.funnel-del { background: none; border: none; color: var(--muted, #888); cursor: pointer; font-size: 1.1rem; }
|
|
180
|
+
.funnel-del:hover { color: #f87171; }
|
|
181
|
+
.funnel-step { display: grid; grid-template-columns: 200px 1fr; gap: 10px; align-items: center; margin: 4px 0; }
|
|
182
|
+
.funnel-label { font-size: .82rem; color: var(--muted, #aaa); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
183
|
+
.funnel-bar-wrap { position: relative; display: flex; align-items: center; gap: 8px; }
|
|
184
|
+
.funnel-bar { height: 16px; border-radius: 4px; background: var(--accent); min-width: 3px; transition: width .4s; }
|
|
185
|
+
.funnel-nums { font-size: .78rem; color: var(--muted, #aaa); white-space: nowrap; }
|
|
186
|
+
.funnel-nums .drop { color: #f87171; }
|
|
187
|
+
.step-row { display: grid; grid-template-columns: 90px 1fr; gap: 8px; margin: 6px 0; }
|
|
188
|
+
@media (max-width: 640px) { .funnel-step { grid-template-columns: 1fr; gap: 2px; } }
|
|
189
|
+
|
|
190
|
+
/* ── 13.10.2026: comparison bar, commerce cards, shop insights ── */
|
|
191
|
+
.compare { display: flex; flex-wrap: wrap; gap: 10px 28px; padding: 12px 18px; margin: 0 0 16px; font-size: .86rem; }
|
|
192
|
+
.compare .k { color: var(--text-dim); margin-right: 8px; text-transform: uppercase; letter-spacing: .05em; font-size: .72rem; }
|
|
193
|
+
.compare .up { color: var(--green); font-weight: 600; }
|
|
194
|
+
.compare .down { color: var(--red); font-weight: 600; }
|
|
195
|
+
.commerce { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px; margin: 0 0 16px; }
|
|
196
|
+
.hint { font-size: .74rem; font-weight: 400; margin-left: 6px; }
|
|
197
|
+
p.hint { margin: -4px 0 8px; }
|
|
198
|
+
.pay-note { margin: 10px 2px 0; font-size: .78rem; color: var(--text-dim); }
|
|
199
|
+
.muted { color: var(--text-dim); }
|
|
200
|
+
.yoy { display: block; margin-top: 4px; font-size: .72rem; color: var(--text-dim); }
|
|
201
|
+
.yoy.up { color: var(--green); } .yoy.down { color: var(--red); }
|
|
202
|
+
|
|
203
|
+
/* embed dialog with website / app tabs (13.10.2026) */
|
|
204
|
+
.modal.modal-wide { max-width: 620px; max-height: calc(100vh - 40px); overflow-y: auto; }
|
|
205
|
+
.snip-tabs { display: flex; justify-content: center; gap: 6px; margin: 0 0 14px; }
|
|
206
|
+
.snip-step { text-align: left; font-size: .8rem; margin: 6px 0 0 !important; }
|
|
207
|
+
.snip-hint { text-align: left; font-size: .78rem; margin: -2px 0 12px !important; }
|
|
208
|
+
.snip-hint code, .snip-step code { color: var(--cyan); }
|
|
209
|
+
.snip-hint a { color: var(--cyan); }
|
|
210
|
+
.modal-wide .snippet { white-space: pre-wrap; word-break: normal; overflow-wrap: anywhere; }
|
|
211
|
+
.snip-tabs { display: inline-flex !important; background: var(--bg2); border: 1px solid var(--line); border-radius: 8px; padding: 3px; gap: 2px !important; }
|
|
212
|
+
.snip-tabs button { background: transparent; border: none; color: var(--text-dim); padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; font-weight: 550; font-family: inherit; transition: all .15s; }
|
|
213
|
+
.snip-tabs button.active { background: var(--surface-2); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-strong); }
|
|
214
|
+
|
|
215
|
+
/* 14.10.2026 professional theme: layout polish */
|
|
216
|
+
.container { max-width: 1200px; padding: 0 24px 48px; }
|
|
217
|
+
.stats { gap: 12px; }
|
|
218
|
+
.panels { gap: 12px; }
|
|
219
|
+
.chart-card { padding: 18px 20px; }
|
|
220
|
+
.trend { font-size: 0.74rem; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
221
|
+
.funnel-label, .funnel-nums { color: var(--text-dim); }
|
|
222
|
+
.empty { color: var(--text-faint); }
|
|
223
|
+
.footer-note { margin-top: 28px; font-size: .74rem; color: var(--text-faint); text-align: center; }
|
|
224
|
+
.footer-note a { color: var(--text-dim); }
|
|
225
|
+
|
|
226
|
+
/* ── 14.10.2026 v2: modern polish (Fabian: „hübscher, moderner“) ── */
|
|
227
|
+
:root {
|
|
228
|
+
--accent: #6d8cff;
|
|
229
|
+
--accent-2: #a78bfa;
|
|
230
|
+
--accent-soft: rgba(109, 140, 255, 0.13);
|
|
231
|
+
--accent-line: rgba(109, 140, 255, 0.42);
|
|
232
|
+
--chart-visitors: #6d8cff;
|
|
233
|
+
--chart-pageviews: #a78bfa;
|
|
234
|
+
--bg: #0b0d12;
|
|
235
|
+
--surface: rgba(23, 26, 34, 0.72);
|
|
236
|
+
--surface-2: #1e222c;
|
|
237
|
+
--line: rgba(255, 255, 255, 0.07);
|
|
238
|
+
--line-strong: rgba(255, 255, 255, 0.12);
|
|
239
|
+
--radius: 16px;
|
|
240
|
+
--shadow: 0 1px 0 rgba(255,255,255,.04) inset, 0 10px 30px -12px rgba(0,0,0,.55);
|
|
241
|
+
}
|
|
242
|
+
body {
|
|
243
|
+
background:
|
|
244
|
+
radial-gradient(1100px 520px at 12% -10%, rgba(109,140,255,.10), transparent 60%),
|
|
245
|
+
radial-gradient(900px 480px at 100% 0%, rgba(167,139,250,.07), transparent 55%),
|
|
246
|
+
var(--bg);
|
|
247
|
+
background-attachment: fixed;
|
|
248
|
+
font-size: 14.5px; letter-spacing: -0.006em;
|
|
249
|
+
}
|
|
250
|
+
/* sticky glass header */
|
|
251
|
+
header {
|
|
252
|
+
position: sticky; top: 0; z-index: 40; margin: 0 -24px 22px; padding: 14px 24px;
|
|
253
|
+
background: rgba(11,13,18,.72); backdrop-filter: saturate(160%) blur(14px); -webkit-backdrop-filter: saturate(160%) blur(14px);
|
|
254
|
+
border-bottom: 1px solid var(--line);
|
|
255
|
+
}
|
|
256
|
+
.brand .logo { width: 32px; height: 32px; border-radius: 9px; box-shadow: 0 0 0 1px var(--line-strong), 0 6px 20px -6px rgba(109,140,255,.55); }
|
|
257
|
+
.brand h1 { font-size: 1.08rem; font-weight: 700; letter-spacing: -.02em; }
|
|
258
|
+
/* cards: soft glass, gentle lift */
|
|
259
|
+
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); backdrop-filter: blur(10px); transition: border-color .2s, transform .2s, box-shadow .2s; }
|
|
260
|
+
.card:hover { border-color: var(--line-strong); }
|
|
261
|
+
.stats .card:hover, .commerce .card:hover { transform: translateY(-1px); box-shadow: var(--shadow), 0 14px 34px -18px rgba(109,140,255,.35); }
|
|
262
|
+
/* KPI cards: big tabular numbers, trend as pill, mini sparkline */
|
|
263
|
+
.stat { padding: 18px 20px 14px; min-height: 124px; }
|
|
264
|
+
.stat .label { font-size: .76rem; font-weight: 500; color: var(--text-dim); letter-spacing: 0; text-transform: none; }
|
|
265
|
+
.stat .value { font-size: 2rem; font-weight: 700; letter-spacing: -.035em; line-height: 1.1; }
|
|
266
|
+
.trend { display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: 999px; font-size: .72rem; font-weight: 600; margin-top: 8px; }
|
|
267
|
+
.trend.up { color: #4ade80; background: rgba(74,222,128,.10); }
|
|
268
|
+
.trend.down { color: #fb7185; background: rgba(251,113,133,.10); }
|
|
269
|
+
.trend:empty { display: none; }
|
|
270
|
+
.spark { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: 38px; pointer-events: none; opacity: .9; }
|
|
271
|
+
.stat > .label, .stat > .value, .stat > .trend, .stat > .yoy { position: relative; z-index: 1; }
|
|
272
|
+
/* first KPI gets a subtle accent edge */
|
|
273
|
+
.stats .stat:first-child { background: linear-gradient(160deg, rgba(109,140,255,.16), rgba(23,26,34,.72) 55%); border-color: rgba(109,140,255,.28); }
|
|
274
|
+
.commerce .stat:first-child { background: linear-gradient(160deg, rgba(74,222,128,.12), rgba(23,26,34,.72) 55%); border-color: rgba(74,222,128,.22); }
|
|
275
|
+
/* segmented controls */
|
|
276
|
+
.periods { background: rgba(255,255,255,.03); border-color: var(--line); border-radius: 12px; padding: 4px; }
|
|
277
|
+
.periods button { border-radius: 9px; padding: 7px 13px; }
|
|
278
|
+
.periods button.active { background: var(--surface-2); box-shadow: 0 1px 0 rgba(255,255,255,.06) inset, 0 2px 8px rgba(0,0,0,.35); }
|
|
279
|
+
.btn { border-radius: 10px; padding: 8px 14px; box-shadow: 0 6px 18px -8px rgba(109,140,255,.7); }
|
|
280
|
+
.btn:hover { background: #5b7cf5; border-color: #5b7cf5; }
|
|
281
|
+
.btn.ghost { box-shadow: none; background: rgba(255,255,255,.03); }
|
|
282
|
+
.btn.ghost:hover { background: rgba(255,255,255,.06); }
|
|
283
|
+
select, input { border-radius: 10px; background: rgba(255,255,255,.03); }
|
|
284
|
+
.realtime { background: rgba(74,222,128,.07); border-color: rgba(74,222,128,.2); color: #b8f5cd; }
|
|
285
|
+
.realtime .dot { box-shadow: 0 0 0 3px rgba(74,222,128,.18); }
|
|
286
|
+
/* chart card */
|
|
287
|
+
.chart-card { padding: 20px 22px 14px; }
|
|
288
|
+
.chart-head strong { font-size: .95rem; font-weight: 650; }
|
|
289
|
+
#chart { height: 240px; }
|
|
290
|
+
.chart-head .legend .leg { border-radius: 999px; padding: 4px 10px; }
|
|
291
|
+
.swatch { width: 8px; height: 8px; border-radius: 50%; }
|
|
292
|
+
/* panels */
|
|
293
|
+
.panel h3 { font-size: .9rem; font-weight: 650; letter-spacing: -.01em; }
|
|
294
|
+
.panel h3 .em { filter: none; opacity: .9; }
|
|
295
|
+
.bar { border-radius: 8px; padding: 8px 11px; }
|
|
296
|
+
.bar .fill { border-radius: 8px; background: linear-gradient(90deg, rgba(109,140,255,.20), rgba(167,139,250,.08)); }
|
|
297
|
+
.bar:hover .fill { background: linear-gradient(90deg, rgba(109,140,255,.30), rgba(167,139,250,.14)); }
|
|
298
|
+
.bar .val { color: var(--text); font-weight: 600; opacity: .85; }
|
|
299
|
+
.panel h3 .tabs { border-radius: 9px; background: rgba(255,255,255,.03); }
|
|
300
|
+
.panel h3 .tabs button { border-radius: 7px; }
|
|
301
|
+
/* modals */
|
|
302
|
+
.modal { background: rgba(23,26,34,.92); backdrop-filter: blur(18px); border-radius: 20px; box-shadow: 0 30px 80px rgba(0,0,0,.6), 0 0 0 1px var(--line-strong); }
|
|
303
|
+
.modal-overlay { backdrop-filter: blur(6px); }
|
|
304
|
+
/* login screen gets a hero glow */
|
|
305
|
+
#login { background: radial-gradient(700px 420px at 50% 30%, rgba(109,140,255,.16), transparent 65%), rgba(6,7,10,.86); }
|
|
306
|
+
#login .modal .logo { width: 56px; height: 56px; border-radius: 15px; box-shadow: 0 10px 30px -8px rgba(109,140,255,.7); }
|
|
307
|
+
/* fade-in on load */
|
|
308
|
+
@keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
|
309
|
+
#dash > * { animation: rise .35s ease both; }
|
|
310
|
+
#dash > *:nth-child(2) { animation-delay: .04s; } #dash > *:nth-child(3) { animation-delay: .08s; } #dash > *:nth-child(4) { animation-delay: .12s; }
|
|
311
|
+
.chip { border-radius: 999px; }
|
|
312
|
+
@media (max-width: 760px) { header { margin: 0 -16px 18px; padding: 12px 16px; } .container { padding: 0 16px 40px; } .stat .value { font-size: 1.6rem; } }
|
|
313
|
+
.stats .stat { padding-bottom: 40px; min-height: 0; }
|
|
314
|
+
.commerce .stat { min-height: 0; }
|
|
315
|
+
.spark { height: 32px; opacity: .8; }
|
|
316
|
+
/* page paths read better in a tight mono with tabular feel */
|
|
317
|
+
#p-pages .bar .name, #p-404 .bar .name, .feed-row .path { font-family: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace; font-size: .8rem; letter-spacing: -.01em; }
|
|
318
|
+
|
|
319
|
+
/* ── 14.10.2026 v3: own colour (ink teal + coral), icons instead of emoji ── */
|
|
320
|
+
:root {
|
|
321
|
+
--accent: #2dd4bf; /* teal: primary */
|
|
322
|
+
--accent-strong: #14b8a6;
|
|
323
|
+
--accent-2: #ff7a5c; /* coral: highlights, the "one dot" */
|
|
324
|
+
--accent-soft: rgba(45, 212, 191, 0.12);
|
|
325
|
+
--accent-line: rgba(45, 212, 191, 0.40);
|
|
326
|
+
--chart-visitors: #2dd4bf;
|
|
327
|
+
--chart-pageviews: #ff9f85;
|
|
328
|
+
--bg: #071114;
|
|
329
|
+
--surface: rgba(14, 28, 32, 0.72);
|
|
330
|
+
--surface-2: #11252a;
|
|
331
|
+
--line: rgba(180, 255, 240, 0.07);
|
|
332
|
+
--line-strong: rgba(180, 255, 240, 0.13);
|
|
333
|
+
--text: #e7f3f1;
|
|
334
|
+
--text-dim: #8aa5a2;
|
|
335
|
+
--text-faint: #5b7471;
|
|
336
|
+
--cyan: #7ee8d8;
|
|
337
|
+
}
|
|
338
|
+
body {
|
|
339
|
+
background:
|
|
340
|
+
radial-gradient(1100px 560px at 8% -12%, rgba(45,212,191,.13), transparent 60%),
|
|
341
|
+
radial-gradient(800px 460px at 104% -4%, rgba(255,122,92,.08), transparent 55%),
|
|
342
|
+
var(--bg);
|
|
343
|
+
background-attachment: fixed;
|
|
344
|
+
}
|
|
345
|
+
header { background: rgba(7,17,20,.74); }
|
|
346
|
+
.brand .logo { box-shadow: 0 0 0 1px var(--line-strong), 0 8px 22px -8px rgba(45,212,191,.6); }
|
|
347
|
+
.btn { background: var(--accent-strong); border-color: var(--accent-strong); color: #04211d; font-weight: 650; box-shadow: 0 6px 18px -8px rgba(45,212,191,.75); }
|
|
348
|
+
.btn:hover { background: var(--accent); border-color: var(--accent); }
|
|
349
|
+
.btn.ghost { color: var(--text); }
|
|
350
|
+
.stats .stat:first-child { background: linear-gradient(160deg, rgba(45,212,191,.16), rgba(14,28,32,.72) 55%); border-color: rgba(45,212,191,.28); }
|
|
351
|
+
.commerce .stat:first-child { background: linear-gradient(160deg, rgba(255,122,92,.14), rgba(14,28,32,.72) 55%); border-color: rgba(255,122,92,.26); }
|
|
352
|
+
.stats .card:hover, .commerce .card:hover { box-shadow: var(--shadow), 0 14px 34px -18px rgba(45,212,191,.35); }
|
|
353
|
+
.bar .fill { background: linear-gradient(90deg, rgba(45,212,191,.20), rgba(45,212,191,.05)); }
|
|
354
|
+
.bar:hover .fill { background: linear-gradient(90deg, rgba(45,212,191,.30), rgba(255,122,92,.10)); }
|
|
355
|
+
.bar .cr { color: var(--accent-2); }
|
|
356
|
+
.realtime { background: rgba(45,212,191,.08); border-color: rgba(45,212,191,.22); color: #b9f5ea; }
|
|
357
|
+
.realtime .dot { background: var(--accent); box-shadow: 0 0 0 3px rgba(45,212,191,.2); }
|
|
358
|
+
.chip { background: var(--accent-soft); border-color: var(--accent-line); }
|
|
359
|
+
.trend.up { color: #5eead4; background: rgba(45,212,191,.12); }
|
|
360
|
+
.trend.down { color: #ff9f85; background: rgba(255,122,92,.12); }
|
|
361
|
+
.feed-row .goal { color: var(--accent-2); }
|
|
362
|
+
.funnel-bar { background: linear-gradient(90deg, var(--accent), #5eead4); }
|
|
363
|
+
#login { background: radial-gradient(700px 420px at 50% 30%, rgba(45,212,191,.18), transparent 65%), rgba(4,10,12,.88); }
|
|
364
|
+
#login .modal .logo { box-shadow: 0 12px 32px -8px rgba(45,212,191,.7); }
|
|
365
|
+
:focus-visible { outline-color: var(--accent); }
|
|
366
|
+
/* icons */
|
|
367
|
+
.ico { width: 16px; height: 16px; flex: 0 0 auto; vertical-align: -3px; display: inline-block; }
|
|
368
|
+
.ico.ui { color: var(--text-dim); }
|
|
369
|
+
.ico.h { width: 17px; height: 17px; color: var(--accent); }
|
|
370
|
+
.ico.big { width: 30px; height: 30px; color: var(--accent); }
|
|
371
|
+
.ico.brand { width: 15px; height: 15px; }
|
|
372
|
+
.btn .ico { width: 15px; height: 15px; margin-right: 6px; vertical-align: -2px; color: currentColor; }
|
|
373
|
+
.btn.ghost .ico { color: var(--text-dim); }
|
|
374
|
+
.snip-tabs button .ico { margin-right: 6px; width: 14px; height: 14px; vertical-align: -2px; }
|
|
375
|
+
.flag { width: 18px; height: 13.5px; border-radius: 3px; object-fit: cover; box-shadow: 0 0 0 1px rgba(255,255,255,.08); flex: 0 0 auto; }
|
|
376
|
+
.bar .name { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
|
|
377
|
+
.bar .name .txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
378
|
+
.chip .flag, .chip .ico { margin-right: 4px; }
|
|
379
|
+
.chip button .ico { width: 13px; height: 13px; margin: 0; }
|
|
380
|
+
.range .arrow .ico { width: 14px; height: 14px; }
|
|
381
|
+
.feed-row .fl { display: inline-flex; width: 20px; justify-content: center; }
|
|
382
|
+
.pay-note .ico { width: 14px; height: 14px; vertical-align: -2px; }
|
|
383
|
+
.modal .logo.em { filter: none; opacity: 1; display: grid; place-items: center; width: 56px; height: 56px; margin: 0 auto 14px; border-radius: 16px; background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent-line); }
|
|
384
|
+
|
|
385
|
+
#p-pages .bar .name .txt, #p-404 .bar .name .txt, .feed-row .path { font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace; font-size: .78rem; font-weight: 400; color: #cfe3e0; }
|
|
386
|
+
#p-pages .bar .name, #p-404 .bar .name { font-family: inherit; }
|
|
387
|
+
|
|
388
|
+
/* ── header v4: floating card, exactly as wide as the content ── */
|
|
389
|
+
header {
|
|
390
|
+
position: sticky; top: 12px; z-index: 40;
|
|
391
|
+
margin: 14px 0 22px; padding: 12px 14px 12px 16px;
|
|
392
|
+
border: 1px solid var(--line-strong); border-radius: 16px;
|
|
393
|
+
background: linear-gradient(180deg, rgba(20,40,44,.78), rgba(10,22,25,.78));
|
|
394
|
+
backdrop-filter: saturate(160%) blur(16px); -webkit-backdrop-filter: saturate(160%) blur(16px);
|
|
395
|
+
box-shadow: 0 1px 0 rgba(255,255,255,.04) inset, 0 18px 40px -24px rgba(0,0,0,.8), 0 0 0 1px rgba(45,212,191,.04);
|
|
396
|
+
}
|
|
397
|
+
header::after { content: ""; position: absolute; left: 18px; right: 18px; bottom: -1px; height: 1px; background: linear-gradient(90deg, transparent, rgba(45,212,191,.45), rgba(255,122,92,.35), transparent); pointer-events: none; }
|
|
398
|
+
.brand { gap: 12px; }
|
|
399
|
+
.brand .logo { width: 36px; height: 36px; border-radius: 10px; }
|
|
400
|
+
.brand h1 { font-size: 1.12rem; }
|
|
401
|
+
.brand .subtitle { font-size: .76rem; color: var(--text-faint); margin-top: 1px; }
|
|
402
|
+
.header-right { gap: 8px; }
|
|
403
|
+
.header-right .realtime, .header-right select, .header-right .btn { height: 36px; border-radius: 10px; }
|
|
404
|
+
.header-right select { background-color: rgba(255,255,255,.03); border: 1px solid var(--line-strong); padding: 0 34px 0 12px; }
|
|
405
|
+
.header-right .btn.ghost { background: rgba(255,255,255,.03); border-color: var(--line-strong); }
|
|
406
|
+
.header-right .btn.ghost:hover { background: rgba(45,212,191,.08); border-color: var(--accent-line); }
|
|
407
|
+
.header-right .realtime { display: inline-flex; align-items: center; gap: 7px; padding: 0 12px; }
|
|
408
|
+
.realtime .dot { animation: taci-pulse 2.4s ease-in-out infinite; }
|
|
409
|
+
@keyframes taci-pulse { 0%,100% { box-shadow: 0 0 0 3px rgba(45,212,191,.18); } 50% { box-shadow: 0 0 0 6px rgba(45,212,191,.05); } }
|
|
410
|
+
@media (prefers-reduced-motion: reduce) { .realtime .dot { animation: none; } }
|
|
411
|
+
@media (max-width: 760px) { header { margin: 10px 0 18px; top: 8px; padding: 10px 12px; } .brand .subtitle { display: none; } }
|
|
412
|
+
|
|
413
|
+
/* native dropdown lists follow the dark theme (otherwise light text on a white popup) */
|
|
414
|
+
select { color-scheme: dark; }
|
|
415
|
+
select option { background: #0c1a1d; color: var(--text); }
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/* Tacilytics tracker — cookieless, ~2KB. (tacit: we count, we don't tell.) Embed:
|
|
2
|
+
<script defer data-domain="example.com" src="https://your-server/tacilytics.js"></script>
|
|
3
|
+
(/nyx.js is served as an alias for sites set up before the rename — same file.)
|
|
4
|
+
|
|
5
|
+
Optional data-* flags on the <script> tag:
|
|
6
|
+
data-honor-dnt respect Do-Not-Track / Global Privacy Control (skip all tracking)
|
|
7
|
+
data-track-outbound auto-record clicks on links to other domains as events
|
|
8
|
+
data-track-404 record a "404" event when the page is an error page
|
|
9
|
+
(value = CSS selector that only exists on error pages, e.g. "body.is-act-error")
|
|
10
|
+
data-track-search record "Search" events: value = query-string parameter, e.g. "search"
|
|
11
|
+
(results counted via data-search-results selector, default ".product-box")
|
|
12
|
+
Purchases: tacilytics('Purchase', { value: 49.90, currency: 'EUR', items: 3 }) on the thank-you page
|
|
13
|
+
Custom events: tacilytics('Signup') · tacilytics('Signup', { plan: 'pro' })
|
|
14
|
+
window.nyx / window.nyxConfig still work as aliases. */
|
|
15
|
+
(function () {
|
|
16
|
+
try {
|
|
17
|
+
var s = document.currentScript || document.querySelector('script[data-domain][src*="tacilytics.js"], script[data-domain][src*="nyx.js"]');
|
|
18
|
+
var domain = s && s.getAttribute('data-domain');
|
|
19
|
+
if (!s || !domain) return;
|
|
20
|
+
|
|
21
|
+
// Privacy signals (opt-in): if the visitor asked not to be tracked, honour it and do nothing.
|
|
22
|
+
if (s.hasAttribute('data-honor-dnt')) {
|
|
23
|
+
var dnt = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
|
|
24
|
+
if (navigator.globalPrivacyControl === true || dnt === '1' || dnt === 'yes') return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var endpoint = new URL(s.src).origin + '/api/collect';
|
|
28
|
+
var tz = '';
|
|
29
|
+
try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch (e) {}
|
|
30
|
+
var lang = (navigator.language || '').slice(0, 12);
|
|
31
|
+
var qs = new URLSearchParams(location.search);
|
|
32
|
+
var first = true;
|
|
33
|
+
|
|
34
|
+
function post(payload) {
|
|
35
|
+
// ignore local/preview hosts
|
|
36
|
+
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1' || location.protocol === 'file:') return;
|
|
37
|
+
try {
|
|
38
|
+
var body = JSON.stringify(payload);
|
|
39
|
+
// text/plain keeps it a CORS-"simple" request, so the cross-origin beacon
|
|
40
|
+
// sends without a preflight (which sendBeacon cannot do).
|
|
41
|
+
if (navigator.sendBeacon && navigator.sendBeacon(endpoint, new Blob([body], { type: 'text/plain' }))) return;
|
|
42
|
+
fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: body, keepalive: true, mode: 'no-cors' });
|
|
43
|
+
} catch (e) {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function send(name, props) {
|
|
47
|
+
var payload = {
|
|
48
|
+
d: domain,
|
|
49
|
+
p: location.pathname,
|
|
50
|
+
r: name ? '' : (first ? document.referrer : ''),
|
|
51
|
+
tz: tz,
|
|
52
|
+
l: lang,
|
|
53
|
+
s: qs.get('utm_source') || '',
|
|
54
|
+
um: qs.get('utm_medium') || '',
|
|
55
|
+
uc: qs.get('utm_campaign') || '',
|
|
56
|
+
};
|
|
57
|
+
if (name) { payload.n = name; if (props && typeof props === 'object') payload.pr = props; }
|
|
58
|
+
else first = false;
|
|
59
|
+
post(payload);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── engagement time + scroll depth: report on leave ──
|
|
63
|
+
var acc = 0, since = (document.visibilityState === 'visible') ? Date.now() : 0, engPath = location.pathname;
|
|
64
|
+
var maxScroll = 0;
|
|
65
|
+
function measureScroll() {
|
|
66
|
+
var de = document.documentElement, b = document.body || de;
|
|
67
|
+
var h = Math.max(de.scrollHeight, b.scrollHeight), vh = window.innerHeight || de.clientHeight;
|
|
68
|
+
var y = window.pageYOffset || de.scrollTop || 0;
|
|
69
|
+
// short pages that fit the screen count as fully seen
|
|
70
|
+
var pct = h <= vh ? 100 : Math.round(((y + vh) / h) * 100);
|
|
71
|
+
if (pct > maxScroll) maxScroll = Math.min(100, pct);
|
|
72
|
+
}
|
|
73
|
+
var scrollTimer = 0;
|
|
74
|
+
window.addEventListener('scroll', function () { if (!scrollTimer) scrollTimer = setTimeout(function () { scrollTimer = 0; measureScroll(); }, 200); }, { passive: true });
|
|
75
|
+
window.addEventListener('load', measureScroll);
|
|
76
|
+
function engaged() { return Math.round((acc + (since ? Date.now() - since : 0)) / 1000); }
|
|
77
|
+
function flushEngagement() {
|
|
78
|
+
var secs = engaged();
|
|
79
|
+
measureScroll();
|
|
80
|
+
if (secs >= 1 || maxScroll > 0) post({ d: domain, p: engPath, e: 'engagement', dur: secs, sc: maxScroll });
|
|
81
|
+
}
|
|
82
|
+
function resetEngagement() { acc = 0; since = Date.now(); engPath = location.pathname; maxScroll = 0; }
|
|
83
|
+
document.addEventListener('visibilitychange', function () {
|
|
84
|
+
if (document.visibilityState === 'hidden') { if (since) { acc += Date.now() - since; since = 0; } flushEngagement(); }
|
|
85
|
+
else if (!since) { since = Date.now(); }
|
|
86
|
+
});
|
|
87
|
+
window.addEventListener('pagehide', flushEngagement);
|
|
88
|
+
|
|
89
|
+
// ── SPA navigation: count a pageview on pushState / replaceState / back-forward ──
|
|
90
|
+
var lastPath = location.pathname;
|
|
91
|
+
function onNav() {
|
|
92
|
+
if (location.pathname === lastPath) return;
|
|
93
|
+
flushEngagement(); // close out the page we're leaving
|
|
94
|
+
lastPath = location.pathname;
|
|
95
|
+
resetEngagement(); // start timing the new page
|
|
96
|
+
send();
|
|
97
|
+
}
|
|
98
|
+
var push = history.pushState;
|
|
99
|
+
history.pushState = function () { push.apply(this, arguments); onNav(); if (window.__tacilyticsSearchCheck) window.__tacilyticsSearchCheck(); };
|
|
100
|
+
var replace = history.replaceState;
|
|
101
|
+
history.replaceState = function () { replace.apply(this, arguments); onNav(); if (window.__tacilyticsSearchCheck) window.__tacilyticsSearchCheck(); };
|
|
102
|
+
window.addEventListener('popstate', onNav);
|
|
103
|
+
|
|
104
|
+
// ── custom events / goals: tacilytics('Signup') · tacilytics('Signup', { plan: 'pro' }) ──
|
|
105
|
+
window.tacilytics = function (name, props) { if (name && typeof name === 'string') send(name.slice(0, 80), props); };
|
|
106
|
+
window.nyx = window.tacilytics; // alias for installs from before the rename
|
|
107
|
+
|
|
108
|
+
// ── optional automatic outbound-link tracking ──
|
|
109
|
+
if (s.hasAttribute('data-track-outbound')) {
|
|
110
|
+
document.addEventListener('click', function (ev) {
|
|
111
|
+
var a = ev.target && ev.target.closest && ev.target.closest('a[href]');
|
|
112
|
+
if (!a) return;
|
|
113
|
+
try {
|
|
114
|
+
var u = new URL(a.href, location.href);
|
|
115
|
+
if ((u.protocol === 'http:' || u.protocol === 'https:') && u.hostname !== location.hostname) {
|
|
116
|
+
window.tacilytics('Outbound Link', { url: u.hostname + u.pathname });
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {}
|
|
119
|
+
}, true);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── 404 pages: one event with the broken URL and where the visitor came from ──
|
|
123
|
+
var cfg = window.tacilyticsConfig || window.nyxConfig || {};
|
|
124
|
+
var sel404 = s.getAttribute('data-track-404') || cfg.track404 || '';
|
|
125
|
+
function check404() {
|
|
126
|
+
try {
|
|
127
|
+
if (sel404 && document.querySelector(sel404)) {
|
|
128
|
+
var from = '';
|
|
129
|
+
try { var ru = new URL(document.referrer); from = ru.hostname === location.hostname ? ru.pathname : ru.hostname; } catch (e) {}
|
|
130
|
+
window.tacilytics('404', { url: location.pathname, from: from });
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── site search: query + number of results ──
|
|
136
|
+
// classic results page: data-track-search="search" (?search=…)
|
|
137
|
+
// overlay search in the URL hash: tacilyticsConfig.searchHash = { param: 'mbai', key: 'query' } (#mbai=query%3D…)
|
|
138
|
+
// tacilyticsConfig.searchResults / searchEmpty: CSS selectors to count hits / detect "no results"
|
|
139
|
+
var searchParam = s.getAttribute('data-track-search') || cfg.searchParam || '';
|
|
140
|
+
var searchHash = cfg.searchHash || null;
|
|
141
|
+
var resSel = s.getAttribute('data-search-results') || cfg.searchResults || '.product-box';
|
|
142
|
+
var emptySel = cfg.searchEmpty || '';
|
|
143
|
+
var lastQ = '', searchTimer = 0;
|
|
144
|
+
function reportSearch(q) {
|
|
145
|
+
q = (q || '').trim().toLowerCase().slice(0, 60);
|
|
146
|
+
if (!q || q === lastQ) return;
|
|
147
|
+
clearTimeout(searchTimer);
|
|
148
|
+
// wait until the results have rendered (and the visitor stopped typing)
|
|
149
|
+
searchTimer = setTimeout(function () {
|
|
150
|
+
if (q === lastQ) return;
|
|
151
|
+
lastQ = q;
|
|
152
|
+
var n = 0;
|
|
153
|
+
try {
|
|
154
|
+
if (emptySel && document.querySelector(emptySel)) n = 0;
|
|
155
|
+
else n = document.querySelectorAll(resSel).length;
|
|
156
|
+
} catch (e) {}
|
|
157
|
+
window.tacilytics('Search', { q: q, results: n });
|
|
158
|
+
}, 2500);
|
|
159
|
+
}
|
|
160
|
+
function searchFromHash() {
|
|
161
|
+
if (!searchHash) return '';
|
|
162
|
+
var h = new URLSearchParams(location.hash.replace(/^#/, '')).get(searchHash.param);
|
|
163
|
+
if (!h) return '';
|
|
164
|
+
return new URLSearchParams(h).get(searchHash.key || 'query') || '';
|
|
165
|
+
}
|
|
166
|
+
function checkSearch() {
|
|
167
|
+
if (searchParam) reportSearch(new URLSearchParams(location.search).get(searchParam) || '');
|
|
168
|
+
if (searchHash) reportSearch(searchFromHash());
|
|
169
|
+
}
|
|
170
|
+
if (searchHash) {
|
|
171
|
+
window.addEventListener('hashchange', function () { reportSearch(searchFromHash()); });
|
|
172
|
+
window.__tacilyticsSearchCheck = window.__nyxSearchCheck = function () { reportSearch(searchFromHash()); }; // overlay updates the hash via history API
|
|
173
|
+
}
|
|
174
|
+
function afterLoad(fn) { if (document.readyState === 'complete' || document.readyState === 'interactive') setTimeout(fn, 0); else document.addEventListener('DOMContentLoaded', fn); }
|
|
175
|
+
afterLoad(function () { check404(); checkSearch(); });
|
|
176
|
+
|
|
177
|
+
// ── initial pageview ──
|
|
178
|
+
if (document.visibilityState === 'prerender') {
|
|
179
|
+
document.addEventListener('visibilitychange', function f() {
|
|
180
|
+
if (document.visibilityState !== 'prerender') { document.removeEventListener('visibilitychange', f); resetEngagement(); send(); }
|
|
181
|
+
});
|
|
182
|
+
} else { send(); }
|
|
183
|
+
} catch (e) {}
|
|
184
|
+
})();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
## 0.2.0
|
|
2
|
+
- Renamed to Tacilytics: package `tacilytics`, classes `Tacilytics`, `TacilyticsBeacon`, `TacilyticsRouteObserver`.
|
|
3
|
+
- `package:tacilytics/nyx_analytics.dart` keeps the old names as deprecated aliases.
|
|
4
|
+
|
|
5
|
+
## 0.1.0
|
|
6
|
+
- First release: screen views, events, search, purchase, route observer, consent switch, per-launch session id.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nyx & Fabian Budde
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|