@networkpro/web 1.11.0 → 1.12.3

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.
@@ -9,28 +9,17 @@ This file is part of Network Pro.
9
9
  <script>
10
10
  import { base } from "$app/paths";
11
11
  import { onMount } from "svelte";
12
- import { trackingStatus } from "$lib/stores/trackingStatus.js";
13
- import {
14
- /** @type {(type: 'enable' | 'disable') => void} */
15
- setTrackingPreference,
16
- /** @type {() => void} */
17
- clearTrackingPreferences,
18
- } from "$lib/utils/trackingCookies.js";
19
- import { CONSTANTS } from "$lib";
20
-
21
- console.log(CONSTANTS.COMPANY_INFO.APP_NAME);
22
12
 
23
- /** @type {typeof CONSTANTS.CONTACT} */
24
- const CONTACT = CONSTANTS.CONTACT;
25
-
26
- /** @type {typeof CONSTANTS.PAGE} */
27
- const PAGE = CONSTANTS.PAGE;
13
+ import {
14
+ trackingPreferences,
15
+ setOptIn,
16
+ setOptOut,
17
+ clearPrefs,
18
+ } from "$lib/stores/trackingPreferences.js";
28
19
 
29
- /** @type {typeof CONSTANTS.NAV} */
30
- const NAV = CONSTANTS.NAV;
20
+ import { CONSTANTS } from "$lib";
31
21
 
32
- /** @type {string} */
33
- const spaceStyle = "spacer";
22
+ const { CONTACT, PAGE, NAV } = CONSTANTS;
34
23
 
35
24
  /** @type {string} */
36
25
  const privacyPolicy = `${base}/privacy`;
@@ -41,70 +30,55 @@ This file is part of Network Pro.
41
30
  /** @type {string} */
42
31
  const classSmall = "small-text";
43
32
 
33
+ /** @type {string} */
34
+ const spaceStyle = "spacer";
35
+
44
36
  /** @type {boolean} */
45
- let optedOut = false;
37
+ let optedOut;
46
38
 
47
39
  /** @type {boolean} */
48
- let optedIn = false;
40
+ let optedIn;
49
41
 
50
- /**
51
- * Refreshes tracking preferences state and updates the reactive store.
52
- * Uses dynamic import to prevent SSR from loading browser-only dependencies.
53
- *
54
- * @returns {Promise<void>}
55
- */
56
- async function refreshTrackingStatus() {
57
- /** @type {typeof import("$lib/utils/trackingStatus.js")} */
58
- const tracking = await import("$lib/utils/trackingStatus.js");
59
-
60
- const prefs = tracking.getTrackingPreferences();
61
- optedOut = prefs.optedOut;
62
- optedIn = prefs.optedIn;
63
- trackingStatus.set(prefs.status);
64
- }
42
+ /** @type {string} */
43
+ let trackingStatus;
65
44
 
66
- /**
67
- * Initializes tracking state on component mount (runs only in browser).
68
- */
69
- onMount(() => {
70
- refreshTrackingStatus();
71
- });
45
+ // Reactive assignments from the store
46
+ $: ({ optedOut, optedIn, status: trackingStatus } = $trackingPreferences);
72
47
 
73
48
  /**
74
- * Toggles opt-out tracking state, updates cookie and tracking store.
75
- *
76
- * @param {boolean} value - Whether the user is opting out
77
- * @returns {void}
49
+ * Toggle tracking opt-out setting.
50
+ * @param {boolean} value
78
51
  */
79
52
  function toggleTracking(value) {
80
- optedOut = value;
81
- if (optedOut) {
53
+ if (value) {
82
54
  console.log("[Tracking] User opted out");
83
- setTrackingPreference("disable");
55
+ setOptOut();
84
56
  } else {
85
57
  console.log("[Tracking] User cleared opt-out");
86
- clearTrackingPreferences();
58
+ clearPrefs();
87
59
  }
88
- refreshTrackingStatus();
89
60
  }
90
61
 
91
62
  /**
92
- * Toggles opt-in tracking state, updates cookie and tracking store.
93
- *
94
- * @param {boolean} value - Whether the user is opting in
95
- * @returns {void}
63
+ * Toggle tracking opt-in setting.
64
+ * @param {boolean} value
96
65
  */
97
66
  function toggleOptIn(value) {
98
- optedIn = value;
99
- if (optedIn) {
67
+ if (value) {
100
68
  console.log("[Tracking] User opted in");
101
- setTrackingPreference("enable");
69
+ setOptIn();
102
70
  } else {
103
71
  console.log("[Tracking] User cleared opt-in");
104
- clearTrackingPreferences();
72
+ clearPrefs();
105
73
  }
106
- refreshTrackingStatus();
107
74
  }
75
+
76
+ onMount(() => {
77
+ console.log(
78
+ "[PrivacyDashboard] Tracking status:",
79
+ $trackingPreferences.status,
80
+ );
81
+ });
108
82
  </script>
109
83
 
110
84
  <section id="top">
@@ -167,43 +141,46 @@ This file is part of Network Pro.
167
141
 
168
142
  &nbsp;
169
143
 
170
- {#if $trackingStatus !== "⏳ Checking tracking preferences..."}
144
+ {#if trackingStatus && trackingStatus !== "⏳ Checking tracking preferences..."}
171
145
  <p id="tracking-status" aria-live="polite">
172
146
  <strong>Tracking Status:</strong>
173
- {$trackingStatus}
147
+ {trackingStatus}
174
148
  </p>
175
149
  {:else}
176
150
  <p id="tracking-status" aria-live="polite">
177
- <strong>Tracking Status:</strong>
178
- <em>Loading…</em>
151
+ <strong>Tracking Status:</strong> <em>Loading…</em>
179
152
  </p>
180
153
  {/if}
181
154
 
182
- <!-- Opt-out checkbox -->
183
- <label>
184
- <input
185
- type="checkbox"
186
- checked={optedOut}
187
- disabled={optedIn}
188
- aria-describedby="tracking-status"
189
- on:change={(e) =>
190
- toggleTracking(/** @type {HTMLInputElement} */ (e.target).checked)} />
191
- <strong>&nbsp;Disable analytics tracking (opt-out)</strong>
192
- </label>
193
-
194
- <br />
195
-
196
- <!-- Opt-in checkbox -->
197
- <label>
198
- <input
199
- type="checkbox"
200
- checked={optedIn}
201
- disabled={optedOut}
202
- aria-describedby="tracking-status"
203
- on:change={(e) =>
204
- toggleOptIn(/** @type {HTMLInputElement} */ (e.target).checked)} />
205
- <strong>&nbsp;Enable analytics tracking (opt-in)</strong>
206
- </label>
155
+ <fieldset>
156
+ <legend class="sr-only">Tracking Preference Controls</legend>
157
+
158
+ <!-- Opt-out checkbox -->
159
+ <label>
160
+ <input
161
+ type="checkbox"
162
+ checked={optedOut}
163
+ disabled={optedIn}
164
+ aria-describedby="tracking-status"
165
+ on:change={(e) =>
166
+ toggleTracking(/** @type {HTMLInputElement} */ (e.target).checked)} />
167
+ <strong>&nbsp;Disable analytics tracking (opt-out)</strong>
168
+ </label>
169
+
170
+ <br />
171
+
172
+ <!-- Opt-in checkbox -->
173
+ <label>
174
+ <input
175
+ type="checkbox"
176
+ checked={optedIn}
177
+ disabled={optedOut}
178
+ aria-describedby="tracking-status"
179
+ on:change={(e) =>
180
+ toggleOptIn(/** @type {HTMLInputElement} */ (e.target).checked)} />
181
+ <strong>&nbsp;Enable analytics tracking (opt-in)</strong>
182
+ </label>
183
+ </fieldset>
207
184
 
208
185
  <div class={spaceStyle}></div>
209
186
 
@@ -12,9 +12,9 @@ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
12
12
  */
13
13
 
14
14
  import {
15
- shouldRemindUserToReconsent,
16
- shouldTrackUser,
17
- } from "$lib/utils/privacy.js";
15
+ remindUserToReconsent,
16
+ trackingPreferences,
17
+ } from "$lib/stores/trackingPreferences.js";
18
18
  import { get, writable } from "svelte/store";
19
19
 
20
20
  /**
@@ -44,11 +44,11 @@ export async function initPostHog() {
44
44
  if (initialized || typeof window === "undefined") return;
45
45
  initialized = true;
46
46
 
47
- const allowTracking = shouldTrackUser();
48
- trackingEnabled.set(allowTracking);
49
- showReminder.set(shouldRemindUserToReconsent());
47
+ const { enabled } = get(trackingPreferences);
48
+ trackingEnabled.set(enabled);
49
+ showReminder.set(get(remindUserToReconsent)); // ✅ use derived store instead
50
50
 
51
- if (!allowTracking) {
51
+ if (!enabled) {
52
52
  console.log("[PostHog] Tracking is disabled — skipping init.");
53
53
  return;
54
54
  }
@@ -63,7 +63,7 @@ export async function initPostHog() {
63
63
  capture_pageview: false,
64
64
  person_profiles: "identified_only",
65
65
  loaded: (phInstance) => {
66
- if (!allowTracking) {
66
+ if (!enabled) {
67
67
  console.log(
68
68
  "[PostHog] ⛔ User opted out — calling opt_out_capturing()",
69
69
  );
@@ -0,0 +1,222 @@
1
+ /* ==========================================================================
2
+ src/lib/stores/trackingPreferences.js
3
+
4
+ Copyright © 2025 Network Pro Strategies (Network Pro™)
5
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
6
+ This file is part of Network Pro.
7
+ ========================================================================== */
8
+
9
+ /**
10
+ * NOTE: Legacy logic from `trackingCookies.js` was merged here in June 2025.
11
+ * That file has been removed to consolidate stateful tracking logic into a
12
+ * reactive store with SSR compatibility.
13
+ */
14
+
15
+ /**
16
+ * @file trackingPreferences.js
17
+ * @description Reactive store for tracking preferences derived from
18
+ * cookies and browser signals (DNT / GPC). Safe for SSR.
19
+ * @module src/lib/stores
20
+ */
21
+
22
+ import { browser } from "$app/environment";
23
+ import { derived, writable } from "svelte/store";
24
+
25
+ /**
26
+ * @typedef {object} TrackingState
27
+ * @property {boolean} optedIn - User explicitly opted in via cookie
28
+ * @property {boolean} optedOut - User explicitly opted out via cookie
29
+ * @property {boolean} dnt - Do Not Track browser signal
30
+ * @property {boolean} gpc - Global Privacy Control browser signal
31
+ * @property {boolean} enabled - Whether tracking is permitted
32
+ * @property {string} status - Human-readable description of tracking state
33
+ */
34
+
35
+ /**
36
+ * @returns {string} Raw document.cookie or empty string (SSR-safe)
37
+ */
38
+ function readCookies() {
39
+ return browser ? document.cookie || "" : "";
40
+ }
41
+
42
+ /**
43
+ * Check if a specific cookie exists
44
+ * @param {string} name
45
+ * @returns {boolean}
46
+ */
47
+ function cookieExists(name) {
48
+ return readCookies().includes(`${name}=true`);
49
+ }
50
+
51
+ /**
52
+ * Set a cookie with boolean true, 1-year duration
53
+ * @param {string} name
54
+ * @returns {void}
55
+ */
56
+ function setCookie(name) {
57
+ if (browser) {
58
+ document.cookie = `${name}=true; path=/; max-age=31536000; samesite=lax`;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Remove a cookie by setting zero max-age
64
+ * @param {string} name
65
+ * @returns {void}
66
+ */
67
+ function removeCookie(name) {
68
+ if (browser) {
69
+ document.cookie = `${name}=; Max-Age=0; path=/; samesite=lax`;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Write tracking_consent_timestamp cookie
75
+ * @returns {void}
76
+ */
77
+ function setConsentTimestamp() {
78
+ if (browser) {
79
+ document.cookie = `tracking_consent_timestamp=${Date.now()}; path=/; max-age=31536000; samesite=lax`;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Remove tracking_consent_timestamp cookie
85
+ * @returns {void}
86
+ */
87
+ function removeConsentTimestamp() {
88
+ if (browser) {
89
+ document.cookie = `tracking_consent_timestamp=; Max-Age=0; path=/; samesite=lax`;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * @returns {{ dnt: boolean, gpc: boolean }}
95
+ */
96
+ function getPrivacySignals() {
97
+ if (!browser) return { dnt: false, gpc: false };
98
+
99
+ const dnt = navigator.doNotTrack === "1";
100
+ // @ts-expect-error: Non-standard GPC property
101
+ const gpc = navigator.globalPrivacyControl === true;
102
+
103
+ return { dnt, gpc };
104
+ }
105
+
106
+ /**
107
+ * @param {object} args
108
+ * @param {boolean} args.optedOut
109
+ * @param {boolean} args.optedIn
110
+ * @param {boolean} args.dnt
111
+ * @param {boolean} args.gpc
112
+ * @returns {string}
113
+ */
114
+ function deriveStatus({ optedOut, optedIn, dnt, gpc }) {
115
+ if (optedOut) return "🔒 Tracking disabled (manual opt-out)";
116
+ if (optedIn) return "✅ Tracking enabled (manual opt-in)";
117
+ if (dnt || gpc) return "🛑 Tracking disabled (via browser signal)";
118
+ return "⚙️ Using default settings (tracking enabled)";
119
+ }
120
+
121
+ /**
122
+ * @returns {TrackingState}
123
+ */
124
+ function computePreferences() {
125
+ const optedOut = cookieExists("disable_tracking");
126
+ const optedIn = cookieExists("enable_tracking");
127
+ const { dnt, gpc } = getPrivacySignals();
128
+
129
+ const enabled = optedIn || (!optedOut && !dnt && !gpc);
130
+ const status = deriveStatus({ optedOut, optedIn, dnt, gpc });
131
+
132
+ return { optedIn, optedOut, dnt, gpc, enabled, status };
133
+ }
134
+
135
+ // --- Writable store ---
136
+ /** @type {import('svelte/store').Writable<TrackingState>} */
137
+ export const trackingPreferences = writable(
138
+ browser
139
+ ? computePreferences()
140
+ : {
141
+ optedIn: false,
142
+ optedOut: false,
143
+ dnt: false,
144
+ gpc: false,
145
+ enabled: false,
146
+ status: "⏳ Checking tracking preferences...",
147
+ },
148
+ );
149
+
150
+ /**
151
+ * Returns true if the user manually set a tracking preference cookie.
152
+ * @returns {boolean}
153
+ */
154
+ function hasUserManuallySetTrackingPreference() {
155
+ const cookies = readCookies();
156
+ return (
157
+ cookies.includes("enable_tracking=true") ||
158
+ cookies.includes("disable_tracking=true")
159
+ );
160
+ }
161
+
162
+ /**
163
+ * Determines if user should be reminded to reconsent (after 6 months).
164
+ *
165
+ * @param {TrackingState} $prefs - The current tracking preferences.
166
+ * @returns {boolean}
167
+ * @type {import("svelte/store").Readable<boolean>}
168
+ */
169
+ export const remindUserToReconsent = derived(trackingPreferences, (_prefs) => {
170
+ if (!browser) return false;
171
+ if (!hasUserManuallySetTrackingPreference()) return false;
172
+
173
+ const match = readCookies().match(/tracking_consent_timestamp=(\d+)/);
174
+ if (!match) return true;
175
+
176
+ const timestamp = Number(match[1]);
177
+ if (isNaN(timestamp)) return true;
178
+
179
+ const age = Date.now() - timestamp;
180
+ return age > 1000 * 60 * 60 * 24 * 180; // 6 months
181
+ });
182
+
183
+ /**
184
+ * Force-refresh current preferences
185
+ * @returns {void}
186
+ */
187
+ export function refreshTrackingPreferences() {
188
+ if (browser) trackingPreferences.set(computePreferences());
189
+ }
190
+
191
+ /**
192
+ * Enable tracking by setting opt-in cookie
193
+ * @returns {void}
194
+ */
195
+ export function setOptIn() {
196
+ setCookie("enable_tracking");
197
+ removeCookie("disable_tracking");
198
+ setConsentTimestamp();
199
+ refreshTrackingPreferences();
200
+ }
201
+
202
+ /**
203
+ * Disable tracking by setting opt-out cookie
204
+ * @returns {void}
205
+ */
206
+ export function setOptOut() {
207
+ setCookie("disable_tracking");
208
+ removeCookie("enable_tracking");
209
+ setConsentTimestamp();
210
+ refreshTrackingPreferences();
211
+ }
212
+
213
+ /**
214
+ * Clear all tracking preference cookies
215
+ * @returns {void}
216
+ */
217
+ export function clearPrefs() {
218
+ removeCookie("enable_tracking");
219
+ removeCookie("disable_tracking");
220
+ removeConsentTimestamp();
221
+ refreshTrackingPreferences();
222
+ }
@@ -506,3 +506,15 @@ footer .container {
506
506
  font-family: inherit; /* Ensure it uses the same font-family as normal text */
507
507
  font-style: normal; /* Remove italic for the description */
508
508
  }
509
+
510
+ .sr-only {
511
+ position: absolute;
512
+ width: 1px;
513
+ height: 1px;
514
+ padding: 0;
515
+ margin: -1px;
516
+ border: 0;
517
+ clip: rect(0, 0, 0, 0);
518
+ overflow: hidden;
519
+ white-space: nowrap;
520
+ }
@@ -3,4 +3,4 @@ Copyright © 2025 Network Pro Strategies (Network Pro™)
3
3
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
4
4
  This file is part of Network Pro.
5
5
  ========================================================================== */
6
- html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:.67em 0;font-size:2em}hr{box-sizing:content-box}pre{font-family:monospace;font-size:1em}a{background-color:#0000}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted buttontext}fieldset{padding:.35em .75em .625em}legend{color:inherit;box-sizing:border-box;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}details{display:block}summary{display:list-item}template{display:none}html{color:#222;scroll-behavior:smooth;font-size:1em;line-height:1.4}::-moz-selection{text-shadow:none;background:#191919}::selection{text-shadow:none;background:#191919}hr{border:0;border-top:1px solid #ccc;height:1px;margin:1em 0;padding:0;display:block;overflow:visible}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}body{color:#fafafa;background-color:#191919;margin:10px;font-family:Arial,Helvetica,sans-serif}a{text-decoration:none}a:link{color:#ffc627}a:hover,a:active{color:#ffc627;text-decoration:underline}a:focus{color:#191919;background-color:#ffc627}a:visited,a:visited:hover{color:#7f6227}.hidden,[hidden]{display:none!important}.visually-hidden{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visually-hidden.focusable:active,.visually-hidden.focusable:focus{clip:auto;width:auto;height:auto;white-space:inherit;margin:0;position:static;overflow:visible}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}@media print{*,:before,:after{color:#000!important;box-shadow:none!important;text-shadow:none!important;background:#fff!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}pre{white-space:pre-wrap!important}pre,blockquote{page-break-inside:avoid;border:1px solid #999}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.full-width-section{background-position:50%;background-size:cover;width:100%;max-width:1920px;margin:0 auto}.container{max-width:1200px;margin:0 auto;padding:0 12px}.readable{max-width:900px;margin:0 auto}header,footer{width:100%}header .container,footer .container{max-width:1200px;margin:0 auto;padding:20px 12px}.gh{border-collapse:collapse;border-spacing:0;margin:0 auto}.gh td,.gh th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.gh .gh-tcell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.gh,.gh col{width:auto!important}.gh-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.soc{border-collapse:collapse;border-spacing:0;margin:0 auto}.soc td,.soc th{border-collapse:collapse;word-break:normal;padding:8px;overflow:hidden}.soc .soc-fa{text-align:center;vertical-align:middle}@media screen and (width<=767px){.soc,.soc col{width:auto!important}.soc-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.foss{border-collapse:collapse;border-spacing:0}.foss td,.foss th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.foss .foss-cell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.foss,.foss col{width:auto!important}.foss-wrap{-webkit-overflow-scrolling:touch;overflow-x:auto}}.bnav{text-align:center;border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td,.bnav th{text-align:center;vertical-align:middle;word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav .bnav-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav,.bnav col{width:auto!important}.bnav-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.bnav2{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav2 td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav2 th{word-break:normal;border-style:none;padding:12px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav2 .bnav2-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav2,.bnav2 col{width:auto!important}.bnav2-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.pgp{border-collapse:collapse;border-spacing:0;margin:0 auto}.pgp td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp th{word-break:normal;border:1px solid #000;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp .pgp-col1{text-align:right;vertical-align:middle;padding-right:1rem}.pgp .pgp-col2{text-align:left;vertical-align:middle;padding-left:1rem}@media screen and (width<=767px){.pgp,.pgp col{width:auto!important}.pgp-wrap{-webkit-overflow-scrolling:touch;margin:2rem 0 auto;overflow-x:auto}}.logo{margin-left:auto;margin-right:auto;display:block}.index-title1{text-align:center;font-style:italic;font-weight:700}.index-title2{letter-spacing:-.015em;text-align:center;font-variant:small-caps;font-size:1.25rem;line-height:1.625rem}.index1{letter-spacing:-.035em;text-align:center;font-style:italic;font-weight:700;line-height:2.125rem}.index2{letter-spacing:-.035em;text-align:center;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.index3{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem}.index4{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem;text-decoration:underline}.subhead{letter-spacing:-.035em;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.bold{font-weight:700}.emphasis{font-style:italic}.uline{text-decoration:underline}.bolditalic{font-style:italic;font-weight:700}.bquote{border-left:3px solid #9e9e9e;margin-left:30px;padding-left:10px;font-style:italic}.small-text{font-size:.75rem;line-height:1.125rem}.large-text-center{text-align:center;font-size:1.25rem;line-height:1.75rem}.prewrap{white-space:pre-wrap;display:block}.hr-styled{width:75%;margin:auto}.center-text{text-align:center}.copyright{text-align:center;font-size:.75rem;line-height:1.125rem}.gold{color:#ffc627}.visited{color:#7f6227}.goldseparator{color:#ffc627;margin:0 .5rem}.center-nav{text-align:center;padding:5px;font-size:1rem;line-height:1.5rem}.block{resize:none;background:0 0;border:none;border-radius:0;outline:none;width:100%;font-size:.75rem;line-height:1.125rem}.fingerprint{white-space:pre-line;font-weight:700;display:block}.pgp-image{width:150px;height:150px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}.emoji{margin-right:8px}.headline{margin-bottom:4px;font-style:italic;font-weight:700;display:block}.label{font-family:inherit;font-weight:700}.description{font-family:inherit;font-style:normal;font-weight:400;display:inline}
6
+ html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:.67em 0;font-size:2em}hr{box-sizing:content-box}pre{font-family:monospace;font-size:1em}a{background-color:#0000}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted buttontext}fieldset{padding:.35em .75em .625em}legend{color:inherit;box-sizing:border-box;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}details{display:block}summary{display:list-item}template{display:none}html{color:#222;scroll-behavior:smooth;font-size:1em;line-height:1.4}::-moz-selection{text-shadow:none;background:#191919}::selection{text-shadow:none;background:#191919}hr{border:0;border-top:1px solid #ccc;height:1px;margin:1em 0;padding:0;display:block;overflow:visible}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}body{color:#fafafa;background-color:#191919;margin:10px;font-family:Arial,Helvetica,sans-serif}a{text-decoration:none}a:link{color:#ffc627}a:hover,a:active{color:#ffc627;text-decoration:underline}a:focus{color:#191919;background-color:#ffc627}a:visited,a:visited:hover{color:#7f6227}.hidden,[hidden]{display:none!important}.visually-hidden{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visually-hidden.focusable:active,.visually-hidden.focusable:focus{clip:auto;width:auto;height:auto;white-space:inherit;margin:0;position:static;overflow:visible}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}@media print{*,:before,:after{color:#000!important;box-shadow:none!important;text-shadow:none!important;background:#fff!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}pre{white-space:pre-wrap!important}pre,blockquote{page-break-inside:avoid;border:1px solid #999}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.full-width-section{background-position:50%;background-size:cover;width:100%;max-width:1920px;margin:0 auto}.container{max-width:1200px;margin:0 auto;padding:0 12px}.readable{max-width:900px;margin:0 auto}header,footer{width:100%}header .container,footer .container{max-width:1200px;margin:0 auto;padding:20px 12px}.gh{border-collapse:collapse;border-spacing:0;margin:0 auto}.gh td,.gh th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.gh .gh-tcell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.gh,.gh col{width:auto!important}.gh-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.soc{border-collapse:collapse;border-spacing:0;margin:0 auto}.soc td,.soc th{border-collapse:collapse;word-break:normal;padding:8px;overflow:hidden}.soc .soc-fa{text-align:center;vertical-align:middle}@media screen and (width<=767px){.soc,.soc col{width:auto!important}.soc-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.foss{border-collapse:collapse;border-spacing:0}.foss td,.foss th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.foss .foss-cell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.foss,.foss col{width:auto!important}.foss-wrap{-webkit-overflow-scrolling:touch;overflow-x:auto}}.bnav{text-align:center;border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td,.bnav th{text-align:center;vertical-align:middle;word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav .bnav-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav,.bnav col{width:auto!important}.bnav-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.bnav2{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav2 td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav2 th{word-break:normal;border-style:none;padding:12px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav2 .bnav2-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav2,.bnav2 col{width:auto!important}.bnav2-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.pgp{border-collapse:collapse;border-spacing:0;margin:0 auto}.pgp td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp th{word-break:normal;border:1px solid #000;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp .pgp-col1{text-align:right;vertical-align:middle;padding-right:1rem}.pgp .pgp-col2{text-align:left;vertical-align:middle;padding-left:1rem}@media screen and (width<=767px){.pgp,.pgp col{width:auto!important}.pgp-wrap{-webkit-overflow-scrolling:touch;margin:2rem 0 auto;overflow-x:auto}}.logo{margin-left:auto;margin-right:auto;display:block}.index-title1{text-align:center;font-style:italic;font-weight:700}.index-title2{letter-spacing:-.015em;text-align:center;font-variant:small-caps;font-size:1.25rem;line-height:1.625rem}.index1{letter-spacing:-.035em;text-align:center;font-style:italic;font-weight:700;line-height:2.125rem}.index2{letter-spacing:-.035em;text-align:center;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.index3{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem}.index4{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem;text-decoration:underline}.subhead{letter-spacing:-.035em;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.bold{font-weight:700}.emphasis{font-style:italic}.uline{text-decoration:underline}.bolditalic{font-style:italic;font-weight:700}.bquote{border-left:3px solid #9e9e9e;margin-left:30px;padding-left:10px;font-style:italic}.small-text{font-size:.75rem;line-height:1.125rem}.large-text-center{text-align:center;font-size:1.25rem;line-height:1.75rem}.prewrap{white-space:pre-wrap;display:block}.hr-styled{width:75%;margin:auto}.center-text{text-align:center}.copyright{text-align:center;font-size:.75rem;line-height:1.125rem}.gold{color:#ffc627}.visited{color:#7f6227}.goldseparator{color:#ffc627;margin:0 .5rem}.center-nav{text-align:center;padding:5px;font-size:1rem;line-height:1.5rem}.block{resize:none;background:0 0;border:none;border-radius:0;outline:none;width:100%;font-size:.75rem;line-height:1.125rem}.fingerprint{white-space:pre-line;font-weight:700;display:block}.pgp-image{width:150px;height:150px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}.emoji{margin-right:8px}.headline{margin-bottom:4px;font-style:italic;font-weight:700;display:block}.label{font-family:inherit;font-weight:700}.description{font-family:inherit;font-style:normal;font-weight:400;display:inline}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
@@ -14,7 +14,6 @@ This file is part of Network Pro.
14
14
  import { initPostHog, showReminder, capture } from "$lib/stores/posthog";
15
15
  import { registerServiceWorker } from "$lib/registerServiceWorker.js";
16
16
  import { browser } from "$app/environment";
17
- import { shouldTrackUser } from "$lib/utils/privacy.js";
18
17
 
19
18
  import ContainerSection from "$lib/components/ContainerSection.svelte";
20
19
  import Footer from "$lib/components/layout/Footer.svelte";
@@ -54,7 +53,6 @@ This file is part of Network Pro.
54
53
  console.log("ENV MODE =", import.meta.env.MODE);
55
54
  console.log("isDev =", isDev);
56
55
  console.log("debug param =", debug);
57
- console.log("shouldTrackUser =", shouldTrackUser()); // Now called statically
58
56
  }
59
57
 
60
58
  // Preload logo assets
@@ -32,6 +32,10 @@ const excludedAssets = [];
32
32
  const IGNORE_PATHS = new Set([
33
33
  "/docs/extensions.md",
34
34
  "/docs/Home.md",
35
+ "/docs/pgp-email.md",
36
+ "/docs/pgp-win.md",
37
+ "/docs/pgp.md",
38
+ "/docs/tracking.md",
35
39
  "/img/banner-1280x640.png",
36
40
  "/img/logo-transparent.png",
37
41
  "/img/logo.png",