@revclamp/google-ads-transparency-mcp-actor 0.0.0-stage → 1.0.0
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/.actor/actor.json +17 -0
- package/.actor/input_schema.json +76 -0
- package/.well-known/glama.json +6 -0
- package/LICENSE +21 -0
- package/README.md +39 -2
- package/actor_config.json +29 -0
- package/assets/icon-180.png +0 -0
- package/assets/icon-32.png +0 -0
- package/assets/icon.svg +26 -0
- package/assets/playground.css +468 -0
- package/assets/playground.js +795 -0
- package/glama.json +6 -0
- package/index.mjs +222 -0
- package/package.json +38 -4
- package/server.json +36 -0
- package/src/apify_actor.mjs +1714 -0
- package/src/egress_guard.mjs +69 -0
- package/src/mcp_server.mjs +1100 -0
- package/src/rpc_client.mjs +3632 -0
- package/src/web_preview_server.mjs +420 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"actorSpecification": 1,
|
|
3
|
+
"name": "google-ads-transparency-mcp-actor",
|
|
4
|
+
"title": "RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor)",
|
|
5
|
+
"description": "Find verified Google Ads advertiser entities from any brand or domain (e.g. nike.com -> Nike, Inc.) and extract active and historical ad creatives (Text, Image, Video), preview URLs, and run dates with zero browser or proxy overhead. Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC.",
|
|
6
|
+
"version": "1.0",
|
|
7
|
+
"buildTag": "latest",
|
|
8
|
+
"minMemoryMbytes": 256,
|
|
9
|
+
"maxMemoryMbytes": 512,
|
|
10
|
+
"defaultRunOptions": {
|
|
11
|
+
"build": "latest",
|
|
12
|
+
"timeoutSecs": 25,
|
|
13
|
+
"memoryMbytes": 256
|
|
14
|
+
},
|
|
15
|
+
"input": "./input_schema.json",
|
|
16
|
+
"dockerfile": "../Dockerfile"
|
|
17
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor)",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"schemaVersion": 1,
|
|
5
|
+
"properties": {
|
|
6
|
+
"query": {
|
|
7
|
+
"title": "Brand Name or Domain",
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Brand name or domain to match against verified Google Ads Transparency advertiser entities (e.g. 'nike.com', 'Stripe', 'shopify.com').",
|
|
10
|
+
"maxLength": 512,
|
|
11
|
+
"editor": "textfield",
|
|
12
|
+
"example": "nike.com"
|
|
13
|
+
},
|
|
14
|
+
"advertiserId": {
|
|
15
|
+
"title": "Advertiser ID (Optional)",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Optional Google Ads Transparency Advertiser ID (e.g. 'AR16735076323512287233'). When provided, queries this exact advertiser directly.",
|
|
18
|
+
"editor": "textfield"
|
|
19
|
+
},
|
|
20
|
+
"countryCode": {
|
|
21
|
+
"title": "Country Filter (Optional)",
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Optional 2-letter ISO country code filter (e.g. 'US'). Leave blank for all regions.",
|
|
24
|
+
"editor": "textfield"
|
|
25
|
+
},
|
|
26
|
+
"formatFilter": {
|
|
27
|
+
"title": "Ad Format",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Filter returned ad creatives by format (ALL, TEXT, IMAGE, or VIDEO).",
|
|
30
|
+
"enum": ["ALL", "TEXT", "IMAGE", "VIDEO"],
|
|
31
|
+
"default": "ALL"
|
|
32
|
+
},
|
|
33
|
+
"includeSubsidiaries": {
|
|
34
|
+
"title": "Include Regional Subsidiaries",
|
|
35
|
+
"type": "boolean",
|
|
36
|
+
"description": "When enabled, matches up to 3 verified regional legal entities for the same brand (e.g. Stripe, Inc. US and Stripe Payments Europe, Limited IE).",
|
|
37
|
+
"default": true
|
|
38
|
+
},
|
|
39
|
+
"includeCreativeDetails": {
|
|
40
|
+
"title": "Include Creative Region & Legal Details",
|
|
41
|
+
"type": "boolean",
|
|
42
|
+
"description": "When enabled, fetches detailed regional presence and legal entity records for up to 10 returned creatives (default 5).",
|
|
43
|
+
"default": false
|
|
44
|
+
},
|
|
45
|
+
"includeAdCopy": {
|
|
46
|
+
"title": "Include Static Ad Copy (Optional)",
|
|
47
|
+
"type": "boolean",
|
|
48
|
+
"description": "When enabled alongside Include Creative Region & Legal Details, extracts static headline, body text, CTA, and destination domain from allowlisted preview scripts without eval.",
|
|
49
|
+
"default": false
|
|
50
|
+
},
|
|
51
|
+
"maxEntities": {
|
|
52
|
+
"title": "Max Advertiser Entities",
|
|
53
|
+
"type": "integer",
|
|
54
|
+
"description": "Maximum number of verified advertiser entities to query when regional subsidiaries are included (1 to 3).",
|
|
55
|
+
"minimum": 1,
|
|
56
|
+
"maximum": 3,
|
|
57
|
+
"default": 3
|
|
58
|
+
},
|
|
59
|
+
"maxResults": {
|
|
60
|
+
"title": "Max Creatives to Extract",
|
|
61
|
+
"type": "integer",
|
|
62
|
+
"description": "Maximum number of deduplicated ad creatives to extract in this run (1 to 200).",
|
|
63
|
+
"minimum": 1,
|
|
64
|
+
"maximum": 200,
|
|
65
|
+
"default": 40
|
|
66
|
+
},
|
|
67
|
+
"pageToken": {
|
|
68
|
+
"title": "Next-Page Cursor Token (Optional)",
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "Optional continuation token returned by a previous run to resume from the next creative.",
|
|
71
|
+
"maxLength": 2048,
|
|
72
|
+
"editor": "textfield"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"required": []
|
|
76
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RevClamp Enterprise (https://revclamp.com)
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Disclaimer:** Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC.
|
|
4
|
+
|
|
5
|
+
Find verified Google Ads advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates directly from the Google Ads Transparency Center (`adstransparency.google.com`) with zero browser or residential proxy overhead. Available as both an Apify Pay-per-Event Actor and a Model Context Protocol (MCP) server (`@revclamp/google-ads-transparency-mcp-actor`).
|
|
6
|
+
|
|
7
|
+
- **Canonical Publisher Website:** [https://revclamp.com](https://revclamp.com)
|
|
8
|
+
- **Official MCP Package:** `@revclamp/google-ads-transparency-mcp-actor` (`com.revclamp/google-ads-transparency-mcp-actor`)
|
|
9
|
+
|
|
10
|
+
## Key Capabilities
|
|
11
|
+
|
|
12
|
+
1. **Smart Advertiser Entity Matching (`SearchSuggestions`):**
|
|
13
|
+
- Matches internationalized domains (`nike.com`, `stripe.com`, `shopify.co.uk`, `brand.myshopify.com`, `on-running.com`, `bankofamerica.com`, `thenorthface.com`) and multi-token or Unicode brand queries (CJK/Hangul corporate names, `"The Coca-Cola Company"`, `L'Oréal`) to verified Google Ads Transparency legal entities (`AR...`).
|
|
14
|
+
- Prevents substring lookalike mismatches where querying `nike.com` selects `NIKETA'S COMPANY S.R.L.` instead of `Nike, Inc.` (`AR16735076323512287233`), and enforces a strict leading-word identity gate so middle-word lookalikes (`Big Apple Pizza LLC`) or unrelated same-first-word companies (`Nike AI Solutions`) are never admitted as corporate subsidiaries.
|
|
15
|
+
- Supports regional subsidiary inclusion (`includeSubsidiaries: true`, up to 3 verified regional corporate entities such as `Stripe, Inc.` US + `Stripe Payments Europe, Limited` IE).
|
|
16
|
+
2. **Paginated Creative & Asset Extraction (`SearchCreatives` & `LookupService/GetCreativeById`):**
|
|
17
|
+
- Maps ISO-2 `countryCode` filters to Google Ads Geo Target Criterion IDs (`US -> 2840`, `GB -> 2826`, `DE -> 2276`, `SG -> 2702`, `VN -> 2704`) and extracts normalized `TEXT`, `IMAGE`, and `VIDEO` ad creatives with decoded `previewUrl`, `archiveImageUrls`, `firstSeenAtIso`, `lastSeenAtIso`, `daysActive`, `timestampsRepaired`, `regionalPresence`, and `transparencyUrl`.
|
|
18
|
+
- Preserves mid-page pagination resume via opaque continuation cursors (`rcp1_...`, `maxLength: 2048`, with query-scope hash `qh`, raw page length `rl`, and anchor `creativeId` `ac`) and automatically rewinds `pagination.nextPageToken` whenever `maxResults`, Apify `CHARGE_LIMIT_REACHED`, `DEADLINE_EXCEEDED_BEFORE_CHARGE`, or the 30,000-byte MCP response ceiling truncates a page. Pass the returned `pagination.nextPageToken` back as `pageToken` in the next Actor input or MCP tool call to resume without duplicate charges or skipped creatives.
|
|
19
|
+
3. **Zero-Charge Schema Guard, Rate-Limit Safety & Crash-Safe Apify Billing:**
|
|
20
|
+
- Separates legitimate empty results (`EMPTY_RESULT`) from upstream HTTP errors (`UPSTREAM_ERROR`), rate-limiting/CAPTCHA challenges (`UPSTREAM_RATE_LIMITED`), and protobuf-JSON schema changes (`SCHEMA_DRIFT_HALT`), halting before any Pay-per-Event billing charge is emitted.
|
|
21
|
+
- Integrates natively with Apify Cloud (`ACTOR_RUN_ID`, `ACTOR_DEFAULT_DATASET_ID`, `ACTOR_DEFAULT_KEY_VALUE_STORE_ID`, `ACTOR_MEMORY_MBYTES`, with fallback to `APIFY_*` legacy aliases), writes the run summary and `pagination.nextPageToken` to the default Key-Value Store `OUTPUT` record on every exit path as well as stdout, and tracks `inFlightIds`, `chargedIds`, `deliveredIds`, `unknownOutcomeIds`, and `stagedItemsById` with bounded `pushData` retry so transient failures or same-run resumptions never double-charge or lose staged paid payloads.
|
|
22
|
+
|
|
23
|
+
## CLI & Verification
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Run MCP stdio server
|
|
27
|
+
npx @revclamp/google-ads-transparency-mcp-actor --mcp-stdio
|
|
28
|
+
|
|
29
|
+
# Start local interactive web playground on http://127.0.0.1:4410
|
|
30
|
+
npm run playground
|
|
31
|
+
|
|
32
|
+
# Run full unit, golden-corpus, metamorphic, mutation, and CDP E2E test suites
|
|
33
|
+
npm test
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Terms of Use, Data Provenance & Independent Tool Disclaimer
|
|
37
|
+
|
|
38
|
+
- **Public Transparency Records Only:** RevClamp AdPulse queries publicly accessible transparency disclosures published on the Google Ads Transparency Center (`adstransparency.google.com`) for research, brand-safety auditing, regulatory compliance, and competitive ad intelligence.
|
|
39
|
+
- **No Circumvention or Personal Data:** This tool operates strictly against unauthenticated public transparency endpoints, enforces a strict hostname allowlist (`adstransparency.google.com`, `displayads-formats.googleusercontent.com`, `tpc.googlesyndication.com`), strips cookies and credentials on egress, and halts immediately (`UPSTREAM_RATE_LIMITED`) if upstream rate limits or CAPTCHA challenges (`HTTP 429` / `/sorry/`) are encountered.
|
|
40
|
+
- **Trademark & Affiliation Notice:** "Google" and "Google Ads" are trademarks of Google LLC. RevClamp AdPulse is an independent software tool developed by RevClamp and is not affiliated with, sponsored by, or endorsed by Google LLC.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"actorType": "apify_actor",
|
|
3
|
+
"pricingModel": {
|
|
4
|
+
"type": "pay_per_event",
|
|
5
|
+
"priceUsdc": 0.0012,
|
|
6
|
+
"costCeilingUsdc": 0.0001,
|
|
7
|
+
"platformFeeRate": 0.2,
|
|
8
|
+
"minMarginMultiplier": 2.5
|
|
9
|
+
},
|
|
10
|
+
"resourceLimits": {
|
|
11
|
+
"maxTimeoutSec": 25,
|
|
12
|
+
"maxMemoryMb": 256,
|
|
13
|
+
"maxPayloadBytes": 30000
|
|
14
|
+
},
|
|
15
|
+
"allowedEgressUrls": [
|
|
16
|
+
"https://adstransparency.google.com/anji/_/rpc/SearchService/SearchSuggestions",
|
|
17
|
+
"https://adstransparency.google.com/anji/_/rpc/SearchService/SearchCreatives",
|
|
18
|
+
"https://adstransparency.google.com/anji/_/rpc/LookupService/GetCreativeById",
|
|
19
|
+
"https://displayads-formats.googleusercontent.com/ads/preview/content.js",
|
|
20
|
+
"https://tpc.googlesyndication.com/archive/simgad/1"
|
|
21
|
+
],
|
|
22
|
+
"manifest": {
|
|
23
|
+
"name": "google-ads-transparency-mcp-actor",
|
|
24
|
+
"version": "1.0.0",
|
|
25
|
+
"title": "RevClamp AdPulse - For Google Ads Transparency Center (MCP & Apify Actor)",
|
|
26
|
+
"websiteUrl": "https://revclamp.com",
|
|
27
|
+
"description": "Find verified Google Ads advertiser entities and extract active or historical ad creatives (Text, Image, Video), preview links, and run dates without browser or proxy overhead. Independent intelligence tool by RevClamp; not affiliated with or endorsed by Google LLC."
|
|
28
|
+
}
|
|
29
|
+
}
|
|
Binary file
|
|
Binary file
|
package/assets/icon.svg
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-labelledby="rc-title">
|
|
2
|
+
<title id="rc-title">RevClamp AdPulse</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="rc-fill" gradientUnits="userSpaceOnUse" x1="32" y1="6" x2="32" y2="58">
|
|
5
|
+
<stop offset="0" stop-color="#111C38"/>
|
|
6
|
+
<stop offset="1" stop-color="#0B1020"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="rc-edge" gradientUnits="userSpaceOnUse" x1="11" y1="6" x2="53" y2="58">
|
|
9
|
+
<stop offset="0" stop-color="#10B981"/>
|
|
10
|
+
<stop offset="1" stop-color="#06B6D4"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<rect width="64" height="64" rx="14" fill="#0B1020"/>
|
|
14
|
+
<path d="M32 6L53 13.5V31C53 44 44.5 52.5 32 58C19.5 52.5 11 44 11 31V13.5Z" fill="url(#rc-fill)" stroke="url(#rc-edge)" stroke-width="2" stroke-linejoin="round"/>
|
|
15
|
+
<path d="M38.5 36.5L44.5 42.5" stroke="#F59E0B" stroke-width="4" stroke-linecap="round" fill="none"/>
|
|
16
|
+
<circle cx="30.5" cy="28.5" r="12.5" fill="#0B1020"/>
|
|
17
|
+
<path d="M30.5 17.5V39.5M19.5 28.5H41.5" stroke="#06B6D4" stroke-opacity="0.35" stroke-width="1.2" fill="none"/>
|
|
18
|
+
<circle cx="30.5" cy="28.5" r="6" stroke="#06B6D4" stroke-opacity="0.5" stroke-width="1.2" fill="none"/>
|
|
19
|
+
<path d="M30.5 28.5V17.5A11 11 0 0 1 40.03 23Z" fill="#10B981" fill-opacity="0.38"/>
|
|
20
|
+
<path d="M30.5 28.5V17.5" stroke="#10B981" stroke-width="1.6" stroke-linecap="round" fill="none"/>
|
|
21
|
+
<circle cx="34" cy="22.4" r="4" fill="#F59E0B" fill-opacity="0.25"/>
|
|
22
|
+
<circle cx="34" cy="22.4" r="2.4" fill="#F59E0B"/>
|
|
23
|
+
<circle cx="25.5" cy="33" r="4.2" fill="#10B981"/>
|
|
24
|
+
<path d="M23.4 33.2L24.9 34.7L27.7 31.4" stroke="#0B1020" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
25
|
+
<circle cx="30.5" cy="28.5" r="12.5" stroke="#F8FAFC" stroke-width="2.5" fill="none"/>
|
|
26
|
+
</svg>
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg-obsidian: #0b1020;
|
|
3
|
+
--bg-panel: #111c38;
|
|
4
|
+
--bg-card: #162344;
|
|
5
|
+
--border-subtle: #243560;
|
|
6
|
+
--text-primary: #f8fafc;
|
|
7
|
+
--text-secondary: #cbd5e1;
|
|
8
|
+
--text-muted: #94a3b8;
|
|
9
|
+
--accent-emerald: #10b981;
|
|
10
|
+
--accent-emerald-hover: #059669;
|
|
11
|
+
--accent-cyan: #06b6d4;
|
|
12
|
+
--accent-amber: #f59e0b;
|
|
13
|
+
--danger-rose: #f43f5e;
|
|
14
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
15
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
*,
|
|
19
|
+
*::before,
|
|
20
|
+
*::after {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
html {
|
|
25
|
+
scroll-padding-top: 80px;
|
|
26
|
+
background-color: var(--bg-obsidian);
|
|
27
|
+
color: var(--text-primary);
|
|
28
|
+
font-family: var(--font-sans);
|
|
29
|
+
line-height: 1.5;
|
|
30
|
+
-webkit-text-size-adjust: 100%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
body {
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
min-height: 100vh;
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
overflow-x: hidden;
|
|
40
|
+
background: radial-gradient(circle at 15% 0%, #15254b 0%, var(--bg-obsidian) 65%);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.site-header {
|
|
44
|
+
background: rgba(11, 16, 32, 0.96);
|
|
45
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
46
|
+
padding: 12px 16px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.header-inner {
|
|
50
|
+
max-width: 1360px;
|
|
51
|
+
margin: 0 auto;
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-wrap: wrap;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: space-between;
|
|
56
|
+
gap: 12px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.brand-group {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: 12px;
|
|
63
|
+
min-width: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.brand-logo {
|
|
67
|
+
width: 36px;
|
|
68
|
+
height: 36px;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
border-radius: 8px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.brand-titles {
|
|
74
|
+
min-width: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.brand-title {
|
|
78
|
+
margin: 0;
|
|
79
|
+
font-size: 1.05rem;
|
|
80
|
+
font-weight: 700;
|
|
81
|
+
letter-spacing: -0.01em;
|
|
82
|
+
color: var(--text-primary);
|
|
83
|
+
overflow-wrap: anywhere;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.brand-subtitle {
|
|
87
|
+
margin: 2px 0 0;
|
|
88
|
+
font-size: 0.8rem;
|
|
89
|
+
color: var(--text-secondary);
|
|
90
|
+
overflow-wrap: anywhere;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.header-badges {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-wrap: wrap;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: 8px;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.badge {
|
|
101
|
+
display: inline-flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
padding: 4px 10px;
|
|
104
|
+
border-radius: 999px;
|
|
105
|
+
font-size: 0.75rem;
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
border: 1px solid var(--border-subtle);
|
|
108
|
+
background: var(--bg-panel);
|
|
109
|
+
color: var(--text-secondary);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.badge-emerald {
|
|
113
|
+
border-color: rgba(16, 185, 129, 0.45);
|
|
114
|
+
background: rgba(16, 185, 129, 0.14);
|
|
115
|
+
color: #6ee7b7;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.badge-cyan {
|
|
119
|
+
border-color: rgba(6, 182, 212, 0.45);
|
|
120
|
+
background: rgba(6, 182, 212, 0.14);
|
|
121
|
+
color: #67e8f9;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.badge-amber {
|
|
125
|
+
border-color: rgba(245, 158, 11, 0.45);
|
|
126
|
+
background: rgba(245, 158, 11, 0.14);
|
|
127
|
+
color: #fde68a;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.workbench-main {
|
|
131
|
+
flex: 1;
|
|
132
|
+
width: 100%;
|
|
133
|
+
max-width: 1360px;
|
|
134
|
+
margin: 0 auto;
|
|
135
|
+
padding: 16px;
|
|
136
|
+
display: grid;
|
|
137
|
+
grid-template-columns: 1fr;
|
|
138
|
+
gap: 16px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@media (min-width: 720px) {
|
|
142
|
+
.workbench-main {
|
|
143
|
+
grid-template-columns: minmax(290px, 380px) minmax(0, 1fr);
|
|
144
|
+
align-items: start;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.panel {
|
|
149
|
+
background: var(--bg-panel);
|
|
150
|
+
border: 1px solid var(--border-subtle);
|
|
151
|
+
border-radius: 12px;
|
|
152
|
+
padding: 16px;
|
|
153
|
+
min-width: 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.panel-heading {
|
|
157
|
+
margin: 0 0 12px;
|
|
158
|
+
font-size: 0.95rem;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
color: var(--text-primary);
|
|
161
|
+
display: flex;
|
|
162
|
+
flex-wrap: wrap;
|
|
163
|
+
align-items: center;
|
|
164
|
+
justify-content: space-between;
|
|
165
|
+
gap: 8px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.panel-heading-title {
|
|
169
|
+
margin: 0;
|
|
170
|
+
font-size: 0.95rem;
|
|
171
|
+
font-weight: 600;
|
|
172
|
+
color: var(--text-primary);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.export-actions-row {
|
|
176
|
+
display: flex;
|
|
177
|
+
flex-wrap: wrap;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 8px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.btn:disabled {
|
|
183
|
+
opacity: 0.5;
|
|
184
|
+
cursor: not-allowed;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.form-stack {
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
gap: 12px;
|
|
191
|
+
min-width: 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.field-group {
|
|
195
|
+
display: flex;
|
|
196
|
+
flex-direction: column;
|
|
197
|
+
gap: 4px;
|
|
198
|
+
min-width: 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.field-label {
|
|
202
|
+
font-size: 0.82rem;
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
color: var(--text-secondary);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.field-hint {
|
|
208
|
+
font-size: 0.74rem;
|
|
209
|
+
color: var(--text-muted);
|
|
210
|
+
overflow-wrap: anywhere;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.input-control,
|
|
214
|
+
.select-control {
|
|
215
|
+
width: 100%;
|
|
216
|
+
min-height: 44px;
|
|
217
|
+
padding: 8px 12px;
|
|
218
|
+
border-radius: 8px;
|
|
219
|
+
border: 1px solid var(--border-subtle);
|
|
220
|
+
background: var(--bg-obsidian);
|
|
221
|
+
color: var(--text-primary);
|
|
222
|
+
font-size: 0.9rem;
|
|
223
|
+
font-family: var(--font-sans);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.input-control:focus,
|
|
227
|
+
.select-control:focus,
|
|
228
|
+
.btn:focus {
|
|
229
|
+
outline: 2px solid var(--accent-cyan);
|
|
230
|
+
outline-offset: 2px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.checkbox-row {
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
gap: 10px;
|
|
237
|
+
min-height: 44px;
|
|
238
|
+
padding: 4px 0;
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
font-size: 0.85rem;
|
|
241
|
+
color: var(--text-secondary);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.checkbox-input {
|
|
245
|
+
width: 22px;
|
|
246
|
+
height: 22px;
|
|
247
|
+
accent-color: var(--accent-emerald);
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
flex-shrink: 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.button-row {
|
|
253
|
+
display: flex;
|
|
254
|
+
flex-wrap: wrap;
|
|
255
|
+
gap: 10px;
|
|
256
|
+
margin-top: 4px;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.btn {
|
|
260
|
+
min-height: 44px;
|
|
261
|
+
min-width: 44px;
|
|
262
|
+
padding: 10px 16px;
|
|
263
|
+
border-radius: 8px;
|
|
264
|
+
border: 1px solid transparent;
|
|
265
|
+
font-size: 0.88rem;
|
|
266
|
+
font-weight: 600;
|
|
267
|
+
cursor: pointer;
|
|
268
|
+
display: inline-flex;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
gap: 8px;
|
|
272
|
+
text-decoration: none;
|
|
273
|
+
transition: background-color 0.15s ease, border-color 0.15s ease;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.btn-primary {
|
|
277
|
+
background: var(--accent-emerald);
|
|
278
|
+
color: #04130e;
|
|
279
|
+
flex: 1;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.btn-primary:hover {
|
|
283
|
+
background: var(--accent-emerald-hover);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.btn-secondary {
|
|
287
|
+
background: var(--bg-card);
|
|
288
|
+
border-color: var(--border-subtle);
|
|
289
|
+
color: var(--text-primary);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.btn-secondary:hover {
|
|
293
|
+
border-color: var(--accent-cyan);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.btn[ hidden ],
|
|
297
|
+
.hidden {
|
|
298
|
+
display: none !important;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.status-banner {
|
|
302
|
+
padding: 10px 12px;
|
|
303
|
+
border-radius: 8px;
|
|
304
|
+
border: 1px solid var(--border-subtle);
|
|
305
|
+
background: var(--bg-obsidian);
|
|
306
|
+
font-size: 0.84rem;
|
|
307
|
+
color: var(--text-secondary);
|
|
308
|
+
margin-bottom: 12px;
|
|
309
|
+
overflow-wrap: anywhere;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.status-banner[data-state="ok"] {
|
|
313
|
+
border-color: rgba(16, 185, 129, 0.5);
|
|
314
|
+
color: #a7f3d0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.status-banner[data-state="warn"] {
|
|
318
|
+
border-color: rgba(245, 158, 11, 0.55);
|
|
319
|
+
color: #fde68a;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.status-banner[data-state="error"] {
|
|
323
|
+
border-color: rgba(244, 63, 94, 0.55);
|
|
324
|
+
color: #fecdd3;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.telemetry-grid {
|
|
328
|
+
display: grid;
|
|
329
|
+
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
|
|
330
|
+
gap: 10px;
|
|
331
|
+
margin-bottom: 14px;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.telemetry-card {
|
|
335
|
+
background: var(--bg-obsidian);
|
|
336
|
+
border: 1px solid var(--border-subtle);
|
|
337
|
+
border-radius: 8px;
|
|
338
|
+
padding: 10px;
|
|
339
|
+
min-width: 0;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.telemetry-label {
|
|
343
|
+
font-size: 0.72rem;
|
|
344
|
+
text-transform: uppercase;
|
|
345
|
+
letter-spacing: 0.04em;
|
|
346
|
+
color: var(--text-muted);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.telemetry-value {
|
|
350
|
+
margin-top: 4px;
|
|
351
|
+
font-size: 0.92rem;
|
|
352
|
+
font-weight: 700;
|
|
353
|
+
color: var(--text-primary);
|
|
354
|
+
font-family: var(--font-mono);
|
|
355
|
+
overflow-wrap: anywhere;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.cards-stack {
|
|
359
|
+
display: flex;
|
|
360
|
+
flex-direction: column;
|
|
361
|
+
gap: 12px;
|
|
362
|
+
min-width: 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.creative-card {
|
|
366
|
+
background: var(--bg-card);
|
|
367
|
+
border: 1px solid var(--border-subtle);
|
|
368
|
+
border-radius: 10px;
|
|
369
|
+
padding: 14px;
|
|
370
|
+
min-width: 0;
|
|
371
|
+
display: flex;
|
|
372
|
+
flex-direction: column;
|
|
373
|
+
gap: 8px;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.creative-card-header {
|
|
377
|
+
display: flex;
|
|
378
|
+
flex-wrap: wrap;
|
|
379
|
+
align-items: center;
|
|
380
|
+
justify-content: space-between;
|
|
381
|
+
gap: 8px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.creative-title {
|
|
385
|
+
font-weight: 700;
|
|
386
|
+
font-size: 0.92rem;
|
|
387
|
+
color: var(--text-primary);
|
|
388
|
+
overflow-wrap: anywhere;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.creative-meta {
|
|
392
|
+
font-size: 0.8rem;
|
|
393
|
+
color: var(--text-secondary);
|
|
394
|
+
font-family: var(--font-mono);
|
|
395
|
+
overflow-wrap: anywhere;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.creative-ad-copy {
|
|
399
|
+
background: rgba(11, 16, 32, 0.72);
|
|
400
|
+
border: 1px solid var(--border-subtle);
|
|
401
|
+
border-radius: 8px;
|
|
402
|
+
padding: 10px 12px;
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
gap: 6px;
|
|
406
|
+
min-width: 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.creative-ad-copy-header {
|
|
410
|
+
display: flex;
|
|
411
|
+
flex-wrap: wrap;
|
|
412
|
+
align-items: center;
|
|
413
|
+
gap: 6px;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.creative-ad-headline {
|
|
417
|
+
font-size: 0.88rem;
|
|
418
|
+
font-weight: 600;
|
|
419
|
+
color: var(--text-primary);
|
|
420
|
+
overflow-wrap: anywhere;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.creative-ad-body {
|
|
424
|
+
font-size: 0.82rem;
|
|
425
|
+
color: var(--text-secondary);
|
|
426
|
+
overflow-wrap: anywhere;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.creative-ad-dest {
|
|
430
|
+
font-size: 0.76rem;
|
|
431
|
+
font-family: var(--font-mono);
|
|
432
|
+
color: var(--text-muted);
|
|
433
|
+
overflow-wrap: anywhere;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.creative-links {
|
|
437
|
+
display: flex;
|
|
438
|
+
flex-wrap: wrap;
|
|
439
|
+
gap: 8px;
|
|
440
|
+
margin-top: 4px;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.raw-json-box {
|
|
444
|
+
margin-top: 14px;
|
|
445
|
+
background: var(--bg-obsidian);
|
|
446
|
+
border: 1px solid var(--border-subtle);
|
|
447
|
+
border-radius: 8px;
|
|
448
|
+
padding: 12px;
|
|
449
|
+
font-family: var(--font-mono);
|
|
450
|
+
font-size: 0.78rem;
|
|
451
|
+
color: var(--text-secondary);
|
|
452
|
+
max-height: 360px;
|
|
453
|
+
overflow-y: auto;
|
|
454
|
+
overflow-x: auto;
|
|
455
|
+
white-space: pre-wrap;
|
|
456
|
+
overflow-wrap: anywhere;
|
|
457
|
+
word-break: break-word;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.site-footer {
|
|
461
|
+
border-top: 1px solid var(--border-subtle);
|
|
462
|
+
background: rgba(11, 16, 32, 0.95);
|
|
463
|
+
padding: 14px 16px;
|
|
464
|
+
text-align: center;
|
|
465
|
+
font-size: 0.78rem;
|
|
466
|
+
color: var(--text-muted);
|
|
467
|
+
overflow-wrap: anywhere;
|
|
468
|
+
}
|