@scalemule/news-publication 0.0.45 → 0.0.47

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.
Files changed (34) hide show
  1. package/README.md +69 -0
  2. package/dist/{chunk-OIEO2YVG.js → chunk-2XSW7JZS.js} +1 -1
  3. package/dist/chunk-66GI2IX6.js +65 -0
  4. package/dist/{chunk-2IYKWGVM.js → chunk-GOWOYMSG.js} +13 -45
  5. package/dist/{chunk-UTBV6JJX.js → chunk-JV76ZEIA.js} +12 -27
  6. package/dist/{chunk-72PKW2HM.js → chunk-UL2Q3N26.js} +1 -1
  7. package/dist/{chunk-GVLJ3HAI.js → chunk-WAC6ATNP.js} +1 -1
  8. package/dist/cookie-consent-banner.cjs +13 -78
  9. package/dist/cookie-consent-banner.js +2 -2
  10. package/dist/cookie-settings-view.cjs +11 -26
  11. package/dist/cookie-settings-view.js +2 -2
  12. package/dist/index.cjs +405 -180
  13. package/dist/index.d.cts +143 -4
  14. package/dist/index.d.ts +143 -4
  15. package/dist/index.js +318 -117
  16. package/dist/network-bar.js +2 -2
  17. package/dist/network-copyright.js +2 -2
  18. package/dist/network.js +1 -1
  19. package/dist/{snapshot-CxuyMF39.d.ts → snapshot-DmRw8G_B.d.ts} +1 -1
  20. package/dist/{snapshot-C0IGpt-B.d.cts → snapshot-Dx4Lv-lB.d.cts} +1 -1
  21. package/dist/snapshot-file.d.cts +2 -2
  22. package/dist/snapshot-file.d.ts +2 -2
  23. package/dist/sponsorship-view.cjs +75 -0
  24. package/dist/sponsorship-view.d.cts +11 -0
  25. package/dist/sponsorship-view.d.ts +11 -0
  26. package/dist/sponsorship-view.js +62 -0
  27. package/dist/sponsorships-CtnBe3S8.d.cts +137 -0
  28. package/dist/sponsorships-CtnBe3S8.d.ts +137 -0
  29. package/dist/{types-DFDCZHbS.d.cts → types-BJFCWWi0.d.cts} +1 -1
  30. package/dist/{types-DFDCZHbS.d.ts → types-BJFCWWi0.d.ts} +1 -1
  31. package/dist/verified-card.d.cts +1 -1
  32. package/dist/verified-card.d.ts +1 -1
  33. package/package.json +11 -4
  34. package/sponsorship.css +78 -0
package/README.md CHANGED
@@ -73,6 +73,67 @@ The React front page still lives in the Walnut Creek Times repository. This pack
73
73
 
74
74
  Patch only (`0.0.x` or `0.1.x`) until a stable release is explicitly approved. Publishing is manual: `npm publish` from this directory.
75
75
 
76
+ ## Local briefing foundation
77
+
78
+ `composeLocalBriefing(candidates, options)` is an opt-in, pure selector for an existing
79
+ publication brief. It consumes `BriefingCandidate` projections from an authorized
80
+ server adapter and returns a bounded list using existing `NewsItem` text. It does
81
+ not fetch or generate news, verify claims, store preferences, or change the current
82
+ `composeEdition` path. A server adapter and publication pilot are still required;
83
+ there is no new live BaseNews endpoint in this package.
84
+
85
+ This is an article-backed presentation helper, not the canonical BaseNews schema.
86
+ BaseNews events, issues and opportunities must also work without an article.
87
+ Its eligibility rules apply only to this optional brief. They must never become
88
+ new publishing gates for articles, questionnaires, jokes, jobs, classifieds,
89
+ celebrations or other existing publication features. Their current owner services
90
+ and workflows continue independently, including when intelligence processing is
91
+ unavailable. Underlying capture, trust and agent APIs take priority over UI rollout.
92
+
93
+ Supply the recipient `sm_application_id`, `publication_id`, resolved
94
+ `coverage_area_ids`, and optional explicit community/topic/entity/issue preferences.
95
+ `personalized: false` ignores all preferences. `limit` is a maximum from 1–12
96
+ (default 6); zero useful records yields an empty briefing. Render summaries as
97
+ text, with original reporting links, verification labels and source attribution.
98
+ `why_it_matters` is optional editorial text; reason codes explain selection and
99
+ are not factual claims about an item's consequences.
100
+
101
+ The server must derive scope from authentication, authorize destination grants,
102
+ resolve geographic relationships, enforce evidence/summary redistribution rights,
103
+ and send public, approved projections. Reuse existing Content Intelligence
104
+ classification, coverage profiles, editorial decisions and current evaluation
105
+ receipts in that adapter; this presentation selector does not replace its generic
106
+ relevance engine or editorial ledger. This helper is **not an authorization or
107
+ input-validation boundary**. Do not pass client-supplied trust/approval fields,
108
+ private artifacts, or raw community submissions. Unverified and community-submitted
109
+ information retains those labels even after editorial approval.
110
+
111
+ Each candidate references an existing canonical development, occurrence or reporting
112
+ record via a namespaced `canonical_key`, with its revision. Do not use a whole issue
113
+ ID as the key: separate developments must survive. Include withdrawal/supersession
114
+ revisions when combining cached inventories; the newest revision wins before
115
+ eligibility and ranking. Supply one selected edition per canonical revision;
116
+ conflicting copies of that revision are omitted for reconciliation, not selected
117
+ by input order. A feed lacking tombstones cannot prove that cached records
118
+ are still current. The caller must invalidate caches on corrections and grant
119
+ revocation. Treat personalized HTTP responses as `private, no-store`.
120
+
121
+ Policy 1 uses a civic priority tier, followed by geography × 4, practical value × 3,
122
+ explicit follows × 3, topics × 2, reporting recency and editorial discovery value.
123
+ All seven components and reason codes are returned. Recency describes reporting,
124
+ not event time or urgency. Civic importance comes from editors, not section names.
125
+ Keep a visible link to all important coverage whenever `remaining_important_count`
126
+ is positive. Eligibility still applies to civic items: stale or unauthorized facts
127
+ cannot bypass trust gates.
128
+
129
+ `relevant_until` is required. Confirmed labels also require a public evidence link,
130
+ `last_verified_at` and a future `review_due_at`. Expired review/relevance deadlines
131
+ exclude records; missing freshness information stays `unknown`. No date is inferred
132
+ from `updated_at`, no confidence score promotes verification, and no fallback story
133
+ fills a quota. The entire approved excerpt must end with sentence punctuation and
134
+ fit within 280 Unicode code points; otherwise it is omitted for editorial repair.
135
+ Later qualifications and corrections are never cut off to make a shorter summary.
136
+
76
137
 
77
138
  ### Reporting credit and sources
78
139
 
@@ -176,3 +237,11 @@ Moderate through **Newsroom → Community Maps → Comments**. Approval requires
176
237
  explicit guideline review and the current revision. Existing pumpkin reactions
177
238
  remain configurable alongside voting. The UI links to `/account?next=...`; the
178
239
  publication must validate that return path and preserve it through sign-in.
240
+
241
+ ### Community partnerships
242
+
243
+ `SPONSORSHIP_OFFERINGS` defines preliminary USD rates and exact web/email deliverables. `Placement`, `SponsorParty`, `Campaign` and `SponsorshipReport` describe the publication-owned Blog API. `SponsorAcknowledgment` from `@scalemule/news-publication/sponsorship-view` and `sponsorship.css` use publication tokens, explicit labels and safe HTTPS links. They do not insert article ads, load advertising scripts, or measure preview renders.
244
+
245
+ Inventory is unavailable until a verified operational placement is active. `placementAvailable` enforces verification freshness and keeps paid briefings disabled. Campaign approval, authorization, allocation capacity and manual commercial references remain server responsibilities. Report HTML distinguishes observed counts from unavailable data and can be printed or rendered to PDF. No invoice or payment is initiated by this SDK.
246
+
247
+ The consent banner now offers a choice in every geography where optional measurement requires one. `measurementConsent` is the server gate for current, explicitly allowed categories and GPC/DNT. Positive permission is local: unsigned URL parameters and a binary network hub cannot grant all categories. Rendering sponsorship recognition does not require tracking consent; measuring it requires both performance and advertising permission.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { setupNetworkConsentDelegation, COOKIE_CONSENT_EVENT, listedPublications, decorateNetworkUrl } from './chunk-UTBV6JJX.js';
2
+ import { setupNetworkConsentDelegation, COOKIE_CONSENT_EVENT, listedPublications, decorateNetworkUrl } from './chunk-JV76ZEIA.js';
3
3
  import { useState, useId, useRef, useEffect } from 'react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
 
@@ -0,0 +1,65 @@
1
+ // src/sponsorships.ts
2
+ var SPONSORSHIP_POLICY = "Sponsors receive clearly identified recognition. Sponsorship does not influence reporting, event selection, factual information, rankings or editorial recommendations.";
3
+ var SPONSORSHIP_OFFERINGS = [
4
+ {
5
+ type: "community_partner",
6
+ name: "Publication Community Partner",
7
+ price_cents: 35e3,
8
+ duration: "One calendar month; exact start and end dates in the agreement.",
9
+ deliverables: "One acknowledgment on the publication's community partners page and one compact acknowledgment below the community calendar. No article placements.",
10
+ acknowledgments: "Two defined web locations; recurring display during the campaign, subject to reader repetition limits.",
11
+ creative: "Name, approved logo, up to 140 characters of copy and one HTTPS link.",
12
+ max_sponsors: 2,
13
+ product_path: "/community-partners"
14
+ },
15
+ {
16
+ type: "community_calendar",
17
+ name: "Community Calendar Sponsor",
18
+ price_cents: 25e3,
19
+ duration: "One calendar month; exact start and end dates in the agreement.",
20
+ deliverables: "One acknowledgment above the independently curated community events calendar.",
21
+ acknowledgments: "One web location; recurring display during the campaign, subject to reader repetition limits.",
22
+ creative: "Name, approved logo, up to 140 characters of copy and one HTTPS link.",
23
+ max_sponsors: 1,
24
+ product_path: "/this-week"
25
+ },
26
+ {
27
+ type: "daily_briefing",
28
+ name: "Daily Briefing Sponsor",
29
+ price_cents: 3e4,
30
+ duration: "One calendar month after verified newsletter delivery is operational.",
31
+ deliverables: "One acknowledgment per agreed briefing edition. Edition dates and count must be specified in the agreement before booking.",
32
+ acknowledgments: "One per contracted edition; no minimum send count is promised before launch.",
33
+ creative: "Name, optional logo, up to 140 characters of approved copy and one HTTPS link.",
34
+ max_sponsors: 1,
35
+ product_path: "/newsletter"
36
+ },
37
+ {
38
+ type: "community_guide",
39
+ name: "Community Guide Sponsor",
40
+ price_cents: null,
41
+ duration: "Fixed dates agreed for a named guide.",
42
+ deliverables: "One acknowledgment on one named, independently edited community guide; scope and updates agreed in writing.",
43
+ acknowledgments: "One web location per contracted guide.",
44
+ creative: "Name, approved logo, up to 140 characters of copy and one HTTPS link.",
45
+ max_sponsors: 1,
46
+ product_path: "/guides"
47
+ }
48
+ ];
49
+ function safeSponsorUrl(value) {
50
+ if (typeof value !== "string" || value.length > 2048) return void 0;
51
+ try {
52
+ const url = new URL(value);
53
+ if (url.protocol !== "https:" || url.username || url.password)
54
+ return void 0;
55
+ return url.toString();
56
+ } catch {
57
+ return void 0;
58
+ }
59
+ }
60
+ function placementAvailable(placement, now = Date.now()) {
61
+ const verified = placement.verified_at && Date.parse(placement.verified_at);
62
+ return placement.placement_type !== "daily_briefing" && placement.active && placement.availability === "available" && Boolean(safeSponsorUrl(placement.verification_url)) && typeof verified === "number" && Number.isFinite(verified) && verified <= now && now - verified <= 90 * 864e5;
63
+ }
64
+
65
+ export { SPONSORSHIP_OFFERINGS, SPONSORSHIP_POLICY, placementAvailable, safeSponsorUrl };
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { setupNetworkConsentDelegation, checkUrlConsentBridge, hasConsented, isGeoConsentRequired, queryNetworkConsentHub, DEFAULT_CONSENT_HUB_URL, acceptAllCookies, rejectOptionalCookies, COOKIE_CONSENT_EVENT, recordConsentToCompliance, setNetworkConsentHub } from './chunk-UTBV6JJX.js';
2
+ import { hasConsented, rejectOptionalCookies, DEFAULT_CONSENT_HUB_URL, isGpcActive, COOKIE_CONSENT_EVENT, acceptAllCookies, recordConsentToCompliance } from './chunk-JV76ZEIA.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
6
6
  function CookieConsentBanner({
7
- title = "Help Us Improve Our Website with Cookies",
7
+ title = "Your privacy choices",
8
8
  text,
9
9
  settingsUrl = "/cookies",
10
10
  className = "",
@@ -18,73 +18,41 @@ function CookieConsentBanner({
18
18
  const [visible, setVisible] = useState(false);
19
19
  useEffect(() => {
20
20
  setMounted(true);
21
- setupNetworkConsentDelegation();
22
- if (checkUrlConsentBridge()) {
23
- setVisible(false);
24
- return;
21
+ const url = new URL(window.location.href);
22
+ if (url.searchParams.has("sm_consent")) {
23
+ if (url.searchParams.get("sm_consent") === "0" && !hasConsented())
24
+ rejectOptionalCookies({ hubUrl: consentHubUrl });
25
+ url.searchParams.delete("sm_consent");
26
+ window.history.replaceState(window.history.state, "", url);
25
27
  }
26
- if (hasConsented()) {
27
- setVisible(false);
28
- return;
29
- }
30
- if (!forceShow && !isGeoConsentRequired(countryCode)) {
31
- setVisible(false);
32
- void queryNetworkConsentHub(consentHubUrl, 1200).then((networkStatus) => {
33
- if (networkStatus === "accepted") {
34
- acceptAllCookies();
35
- } else if (networkStatus === "rejected") {
36
- rejectOptionalCookies();
37
- }
38
- });
39
- return;
40
- }
41
- let cancelled = false;
42
- void queryNetworkConsentHub(consentHubUrl, 1e3).then((networkStatus) => {
43
- if (cancelled) return;
44
- if (networkStatus === "accepted") {
45
- acceptAllCookies();
46
- setVisible(false);
47
- } else if (networkStatus === "rejected") {
48
- rejectOptionalCookies();
49
- setVisible(false);
50
- } else {
51
- setVisible(true);
52
- }
53
- });
54
- return () => {
55
- cancelled = true;
56
- };
28
+ setVisible(!hasConsented() && !isGpcActive());
57
29
  }, [countryCode, forceShow, consentHubUrl]);
58
30
  useEffect(() => {
59
31
  const onConsentChange = () => {
60
- if (hasConsented()) {
61
- setVisible(false);
62
- }
32
+ setVisible(!hasConsented() && !isGpcActive());
63
33
  };
64
34
  window.addEventListener(COOKIE_CONSENT_EVENT, onConsentChange);
65
35
  return () => window.removeEventListener(COOKIE_CONSENT_EVENT, onConsentChange);
66
36
  }, []);
67
37
  if (!mounted || !visible) return null;
68
38
  const handleAccept = () => {
69
- const record = acceptAllCookies();
39
+ const record = acceptAllCookies({ hubUrl: consentHubUrl });
70
40
  setVisible(false);
71
41
  void recordConsentToCompliance(record, {
72
42
  endpoint: complianceEndpoint,
73
43
  publicationSlug
74
44
  });
75
- setNetworkConsentHub("accepted", consentHubUrl);
76
45
  };
77
46
  const handleReject = () => {
78
- const record = rejectOptionalCookies();
47
+ const record = rejectOptionalCookies({ hubUrl: consentHubUrl });
79
48
  setVisible(false);
80
49
  void recordConsentToCompliance(record, {
81
50
  endpoint: complianceEndpoint,
82
51
  publicationSlug
83
52
  });
84
- setNetworkConsentHub("rejected", consentHubUrl);
85
53
  };
86
54
  const defaultText = /* @__PURE__ */ jsxs(Fragment, { children: [
87
- "We use cookies and device data to analyze performance, support local sponsors, and remember your reading preferences across our news network. View",
55
+ "With your permission, we measure reading activity and sponsor acknowledgments. You can read the publication with optional cookies turned off. View",
88
56
  " ",
89
57
  /* @__PURE__ */ jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
90
58
  " ",
@@ -1,5 +1,5 @@
1
1
  import { recordConsentToCompliance as recordConsentToCompliance$1, getCookieConsent, decorateNetworkUrl } from '@scalemule/compliance';
2
- export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, queryNetworkConsentHub, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from '@scalemule/compliance';
2
+ export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, isGpcActive, queryNetworkConsentHub, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from '@scalemule/compliance';
3
3
 
4
4
  // src/cookies.ts
5
5
  async function recordConsentToCompliance(record, options) {
@@ -48,13 +48,6 @@ var COOKIE_CATEGORIES = [
48
48
  duration: "1 year",
49
49
  type: "HTTP Cookie"
50
50
  },
51
- {
52
- name: "napsite_session_v1",
53
- provider: "First-Party",
54
- purpose: "Maintains session continuity, security tokens, and server routing.",
55
- duration: "30 minutes (idle)",
56
- type: "Session Storage"
57
- },
58
51
  {
59
52
  name: "napsite_preview",
60
53
  provider: "First-Party",
@@ -68,9 +61,16 @@ var COOKIE_CATEGORIES = [
68
61
  id: "performance",
69
62
  title: "Performance & Analytics Cookies",
70
63
  shortDescription: "Help us measure reader engagement, story completion, and site reliability.",
71
- fullDescription: "These cookies collect anonymous information about how visitors navigate our news publication. They allow us to count article reads, understand scroll depth and reading completion, and identify broken links or slow-loading assets so we can keep our community reporting responsive and fast. All metrics are aggregated and never contain personal information.",
64
+ fullDescription: "With your permission, first-party identifiers support page-visit and reading measurements. Aggregate reports help us improve the publication. Measurements are incomplete when readers decline consent and are not a count of unique people.",
72
65
  required: false,
73
66
  cookies: [
67
+ {
68
+ name: "napsite_session_v1",
69
+ provider: "First-Party",
70
+ purpose: "Groups consented reading measurements within a session.",
71
+ duration: "30 minutes (idle)",
72
+ type: "Session Storage"
73
+ },
74
74
  {
75
75
  name: "napsite_visitor_v1",
76
76
  provider: "First-Party",
@@ -120,25 +120,10 @@ var COOKIE_CATEGORIES = [
120
120
  {
121
121
  id: "advertising",
122
122
  title: "Advertising & Local Sponsorship Cookies",
123
- shortDescription: "Support local business sponsors by capping repetition and measuring notices.",
124
- fullDescription: "Our journalism is supported by local business sponsors and community partners. These cookies ensure you do not see the same sponsor banner repeatedly (frequency capping) and help local merchants measure whether their announcement reached their community. They never track your activity across unrelated third-party websites or build external advertising profiles.",
123
+ shortDescription: "Measure clearly labeled community sponsorship views and link clicks.",
124
+ fullDescription: "With your permission, we measure visible sponsor acknowledgments and link clicks. Repetition limits use temporary page-session memory. We do not use third-party advertising trackers or build behavioral advertising profiles.",
125
125
  required: false,
126
- cookies: [
127
- {
128
- name: "napsite_sponsor_cap",
129
- provider: "First-Party",
130
- purpose: "Prevents repetitive display of identical local business announcements.",
131
- duration: "7 days",
132
- type: "Local Storage"
133
- },
134
- {
135
- name: "local_sponsor_ref",
136
- provider: "First-Party",
137
- purpose: "Attributes community inquiries directly to the sponsoring local merchant.",
138
- duration: "30 days",
139
- type: "Local Storage"
140
- }
141
- ]
126
+ cookies: []
142
127
  }
143
128
  ];
144
129
 
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-UTBV6JJX.js';
2
+ import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-JV76ZEIA.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,4 +1,4 @@
1
- import { NETWORK_PARENT } from './chunk-UTBV6JJX.js';
1
+ import { NETWORK_PARENT } from './chunk-JV76ZEIA.js';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
4
  function NetworkCopyrightNotice({
@@ -114,7 +114,7 @@ var listedPublications = localPublications.filter(
114
114
  listedPublications.filter(
115
115
  (publication) => publication.slug !== "bayareachronicle"
116
116
  );
117
- var NETWORK_HOSTNAMES = new Set(
117
+ new Set(
118
118
  localPublications.map((p) => {
119
119
  try {
120
120
  return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
@@ -123,46 +123,13 @@ var NETWORK_HOSTNAMES = new Set(
123
123
  }
124
124
  })
125
125
  );
126
- function isNetworkHostname(hostname) {
127
- if (!hostname) return false;
128
- const clean = hostname.replace(/^www\./, "").toLowerCase();
129
- return clean === "bayareanewsnetwork.com" || clean.endsWith(".bayareanewsnetwork.com") || NETWORK_HOSTNAMES.has(clean) || clean.endsWith(".bayareachronicle.com") || clean.endsWith(".napsite.com") || clean.endsWith(".site.scalemule.com");
130
- }
131
126
 
132
127
  // src/cookies.ts
133
128
  async function recordConsentToCompliance(record, options) {
134
129
  return compliance.recordConsentToCompliance(record, options);
135
130
  }
136
- var delegationInstalled = false;
137
- function setupNetworkConsentDelegation() {
138
- if (typeof window === "undefined" || delegationInstalled) return;
139
- delegationInstalled = true;
140
- document.addEventListener(
141
- "click",
142
- (e) => {
143
- const target = e.target;
144
- const anchor = target?.closest("a");
145
- if (!anchor || !anchor.href) return;
146
- try {
147
- const url = new URL(anchor.href, window.location.href);
148
- if (url.protocol !== "http:" && url.protocol !== "https:") return;
149
- if (isNetworkHostname(url.hostname)) {
150
- const consent = compliance.getCookieConsent();
151
- if (consent) {
152
- const decorated = compliance.decorateNetworkUrl(anchor.href);
153
- if (decorated !== anchor.href) {
154
- anchor.href = decorated;
155
- }
156
- }
157
- }
158
- } catch {
159
- }
160
- },
161
- { capture: true }
162
- );
163
- }
164
131
  function CookieConsentBanner({
165
- title = "Help Us Improve Our Website with Cookies",
132
+ title = "Your privacy choices",
166
133
  text,
167
134
  settingsUrl = "/cookies",
168
135
  className = "",
@@ -176,73 +143,41 @@ function CookieConsentBanner({
176
143
  const [visible, setVisible] = react.useState(false);
177
144
  react.useEffect(() => {
178
145
  setMounted(true);
179
- setupNetworkConsentDelegation();
180
- if (compliance.checkUrlConsentBridge()) {
181
- setVisible(false);
182
- return;
146
+ const url = new URL(window.location.href);
147
+ if (url.searchParams.has("sm_consent")) {
148
+ if (url.searchParams.get("sm_consent") === "0" && !compliance.hasConsented())
149
+ compliance.rejectOptionalCookies({ hubUrl: consentHubUrl });
150
+ url.searchParams.delete("sm_consent");
151
+ window.history.replaceState(window.history.state, "", url);
183
152
  }
184
- if (compliance.hasConsented()) {
185
- setVisible(false);
186
- return;
187
- }
188
- if (!forceShow && !compliance.isGeoConsentRequired(countryCode)) {
189
- setVisible(false);
190
- void compliance.queryNetworkConsentHub(consentHubUrl, 1200).then((networkStatus) => {
191
- if (networkStatus === "accepted") {
192
- compliance.acceptAllCookies();
193
- } else if (networkStatus === "rejected") {
194
- compliance.rejectOptionalCookies();
195
- }
196
- });
197
- return;
198
- }
199
- let cancelled = false;
200
- void compliance.queryNetworkConsentHub(consentHubUrl, 1e3).then((networkStatus) => {
201
- if (cancelled) return;
202
- if (networkStatus === "accepted") {
203
- compliance.acceptAllCookies();
204
- setVisible(false);
205
- } else if (networkStatus === "rejected") {
206
- compliance.rejectOptionalCookies();
207
- setVisible(false);
208
- } else {
209
- setVisible(true);
210
- }
211
- });
212
- return () => {
213
- cancelled = true;
214
- };
153
+ setVisible(!compliance.hasConsented() && !compliance.isGpcActive());
215
154
  }, [countryCode, forceShow, consentHubUrl]);
216
155
  react.useEffect(() => {
217
156
  const onConsentChange = () => {
218
- if (compliance.hasConsented()) {
219
- setVisible(false);
220
- }
157
+ setVisible(!compliance.hasConsented() && !compliance.isGpcActive());
221
158
  };
222
159
  window.addEventListener(compliance.COOKIE_CONSENT_EVENT, onConsentChange);
223
160
  return () => window.removeEventListener(compliance.COOKIE_CONSENT_EVENT, onConsentChange);
224
161
  }, []);
225
162
  if (!mounted || !visible) return null;
226
163
  const handleAccept = () => {
227
- const record = compliance.acceptAllCookies();
164
+ const record = compliance.acceptAllCookies({ hubUrl: consentHubUrl });
228
165
  setVisible(false);
229
166
  void recordConsentToCompliance(record, {
230
167
  endpoint: complianceEndpoint,
231
168
  publicationSlug
232
169
  });
233
- compliance.setNetworkConsentHub("accepted", consentHubUrl);
234
170
  };
235
171
  const handleReject = () => {
236
- const record = compliance.rejectOptionalCookies();
172
+ const record = compliance.rejectOptionalCookies({ hubUrl: consentHubUrl });
237
173
  setVisible(false);
238
174
  void recordConsentToCompliance(record, {
239
175
  endpoint: complianceEndpoint,
240
176
  publicationSlug
241
177
  });
242
- compliance.setNetworkConsentHub("rejected", consentHubUrl);
243
178
  };
244
179
  const defaultText = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
245
- "We use cookies and device data to analyze performance, support local sponsors, and remember your reading preferences across our news network. View",
180
+ "With your permission, we measure reading activity and sponsor acknowledgments. You can read the publication with optional cookies turned off. View",
246
181
  " ",
247
182
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
248
183
  " ",
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieConsentBanner } from './chunk-2IYKWGVM.js';
3
- import './chunk-UTBV6JJX.js';
2
+ export { CookieConsentBanner } from './chunk-GOWOYMSG.js';
3
+ import './chunk-JV76ZEIA.js';
@@ -143,13 +143,6 @@ var COOKIE_CATEGORIES = [
143
143
  duration: "1 year",
144
144
  type: "HTTP Cookie"
145
145
  },
146
- {
147
- name: "napsite_session_v1",
148
- provider: "First-Party",
149
- purpose: "Maintains session continuity, security tokens, and server routing.",
150
- duration: "30 minutes (idle)",
151
- type: "Session Storage"
152
- },
153
146
  {
154
147
  name: "napsite_preview",
155
148
  provider: "First-Party",
@@ -163,9 +156,16 @@ var COOKIE_CATEGORIES = [
163
156
  id: "performance",
164
157
  title: "Performance & Analytics Cookies",
165
158
  shortDescription: "Help us measure reader engagement, story completion, and site reliability.",
166
- fullDescription: "These cookies collect anonymous information about how visitors navigate our news publication. They allow us to count article reads, understand scroll depth and reading completion, and identify broken links or slow-loading assets so we can keep our community reporting responsive and fast. All metrics are aggregated and never contain personal information.",
159
+ fullDescription: "With your permission, first-party identifiers support page-visit and reading measurements. Aggregate reports help us improve the publication. Measurements are incomplete when readers decline consent and are not a count of unique people.",
167
160
  required: false,
168
161
  cookies: [
162
+ {
163
+ name: "napsite_session_v1",
164
+ provider: "First-Party",
165
+ purpose: "Groups consented reading measurements within a session.",
166
+ duration: "30 minutes (idle)",
167
+ type: "Session Storage"
168
+ },
169
169
  {
170
170
  name: "napsite_visitor_v1",
171
171
  provider: "First-Party",
@@ -215,25 +215,10 @@ var COOKIE_CATEGORIES = [
215
215
  {
216
216
  id: "advertising",
217
217
  title: "Advertising & Local Sponsorship Cookies",
218
- shortDescription: "Support local business sponsors by capping repetition and measuring notices.",
219
- fullDescription: "Our journalism is supported by local business sponsors and community partners. These cookies ensure you do not see the same sponsor banner repeatedly (frequency capping) and help local merchants measure whether their announcement reached their community. They never track your activity across unrelated third-party websites or build external advertising profiles.",
218
+ shortDescription: "Measure clearly labeled community sponsorship views and link clicks.",
219
+ fullDescription: "With your permission, we measure visible sponsor acknowledgments and link clicks. Repetition limits use temporary page-session memory. We do not use third-party advertising trackers or build behavioral advertising profiles.",
220
220
  required: false,
221
- cookies: [
222
- {
223
- name: "napsite_sponsor_cap",
224
- provider: "First-Party",
225
- purpose: "Prevents repetitive display of identical local business announcements.",
226
- duration: "7 days",
227
- type: "Local Storage"
228
- },
229
- {
230
- name: "local_sponsor_ref",
231
- provider: "First-Party",
232
- purpose: "Attributes community inquiries directly to the sponsoring local merchant.",
233
- duration: "30 days",
234
- type: "Local Storage"
235
- }
236
- ]
221
+ cookies: []
237
222
  }
238
223
  ];
239
224
  function CookieSettingsView({
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieSettingsView } from './chunk-72PKW2HM.js';
3
- import './chunk-UTBV6JJX.js';
2
+ export { CookieSettingsView } from './chunk-UL2Q3N26.js';
3
+ import './chunk-JV76ZEIA.js';