updated store assets
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
TEMPLATE="$ROOT_DIR/store-assets/google-ads/google-ads-creatives.html"
|
||||
OUT_DIR="$ROOT_DIR/store-assets/google-ads"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
creatives=(
|
||||
"pqs-square-01-scanner 1200 1200"
|
||||
"pqs-square-02-results 1200 1200"
|
||||
"pqs-square-03-risk 1200 1200"
|
||||
"pqs-square-04-image-scan 1200 1200"
|
||||
"pqs-square-05-contact 1200 1200"
|
||||
"pqs-square-06-history 1200 1200"
|
||||
"pqs-square-07-batch 1200 1200"
|
||||
"pqs-square-08-settings 1200 1200"
|
||||
"pqs-horizontal-01-scanner-results 1200 628"
|
||||
"pqs-horizontal-02-gallery 1200 628"
|
||||
"pqs-horizontal-03-risk 1200 628"
|
||||
"pqs-horizontal-04-history 1200 628"
|
||||
"pqs-horizontal-05-wifi 1200 628"
|
||||
"pqs-horizontal-06-event 1200 628"
|
||||
"pqs-horizontal-07-tablet 1200 628"
|
||||
"pqs-horizontal-08-settings 1200 628"
|
||||
"pqs-vertical-01-scanner 1200 1500"
|
||||
"pqs-vertical-02-gallery 1200 1500"
|
||||
"pqs-vertical-03-event 1200 1500"
|
||||
"pqs-vertical-04-privacy 1200 1500"
|
||||
)
|
||||
|
||||
for creative in "${creatives[@]}"; do
|
||||
read -r id width height <<< "$creative"
|
||||
google-chrome \
|
||||
--headless \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--hide-scrollbars \
|
||||
--allow-file-access-from-files \
|
||||
--window-size="${width},${height}" \
|
||||
--screenshot="$OUT_DIR/${id}.png" \
|
||||
"file://$TEMPLATE?creative=$id" >/dev/null
|
||||
printf '%s %sx%s\n' "$id" "$width" "$height"
|
||||
done
|
||||
@@ -0,0 +1,862 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Private QR Scanner Google Ads Creatives</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--deep: #07111f;
|
||||
--navy: #0b1220;
|
||||
--teal-900: #123b3f;
|
||||
--teal-800: #155e63;
|
||||
--teal-700: #0f766e;
|
||||
--teal-300: #2dd4bf;
|
||||
--mint: #dff7f2;
|
||||
--bg: #f6fbfa;
|
||||
--surface: #ffffff;
|
||||
--soft-blue: #f2f7ff;
|
||||
--text: #0b1220;
|
||||
--muted: #607080;
|
||||
--line: #dce8e6;
|
||||
--amber: #ffc857;
|
||||
--coral: #ff6b5f;
|
||||
--sky: #55b9ff;
|
||||
--violet: #8b7cf6;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
#stage {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: var(--w);
|
||||
height: var(--h);
|
||||
isolation: isolate;
|
||||
background: var(--base);
|
||||
}
|
||||
|
||||
.band,
|
||||
.panel,
|
||||
.rail,
|
||||
.tile-field,
|
||||
.scan-beam,
|
||||
.code-stack,
|
||||
.ticket-strip,
|
||||
.wifi-mark,
|
||||
.shield,
|
||||
.chips,
|
||||
.history-steps,
|
||||
.grid-plate,
|
||||
.device,
|
||||
.tablet {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.band {
|
||||
inset: auto -8% -18% -8%;
|
||||
height: 44%;
|
||||
background: var(--band);
|
||||
transform: skewY(-7deg);
|
||||
transform-origin: center;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.band.top {
|
||||
inset: -20% -8% auto -8%;
|
||||
height: 42%;
|
||||
transform: skewY(-6deg);
|
||||
}
|
||||
|
||||
.rail {
|
||||
background:
|
||||
linear-gradient(90deg, transparent 0 12%, rgba(255,255,255,.22) 12% 13%, transparent 13% 28%, rgba(255,255,255,.2) 28% 29%, transparent 29%),
|
||||
linear-gradient(135deg, rgba(45,212,191,.2), rgba(85,185,255,.08));
|
||||
border: 1px solid rgba(255,255,255,.16);
|
||||
transform: rotate(var(--r));
|
||||
border-radius: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 22px;
|
||||
background: color-mix(in srgb, var(--panel-bg), transparent 0%);
|
||||
border: 1px solid var(--panel-border);
|
||||
box-shadow: 0 28px 70px rgba(7, 17, 31, .16);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tile-field {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 7px;
|
||||
opacity: .7;
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.tile-field span {
|
||||
border-radius: 2px;
|
||||
background: var(--tile);
|
||||
opacity: var(--o, .75);
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
height: 8px;
|
||||
background: linear-gradient(90deg, transparent, var(--teal-300), transparent);
|
||||
box-shadow: 0 0 34px rgba(45, 212, 191, .95);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.code-stack {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.code-card {
|
||||
height: 74px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, .88);
|
||||
border: 1px solid rgba(96, 112, 128, .14);
|
||||
box-shadow: 0 18px 44px rgba(7, 17, 31, .12);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code-card::before,
|
||||
.code-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
right: 18px;
|
||||
height: 10px;
|
||||
border-radius: 99px;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.code-card::before {
|
||||
top: 18px;
|
||||
width: 54%;
|
||||
}
|
||||
|
||||
.code-card::after {
|
||||
top: 40px;
|
||||
width: 78%;
|
||||
background: color-mix(in srgb, var(--teal-300), white 70%);
|
||||
}
|
||||
|
||||
.ticket-strip {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ticket {
|
||||
width: 272px;
|
||||
height: 96px;
|
||||
border-radius: 16px;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(7,17,31,.12) 0 2px, transparent 2px 16px),
|
||||
var(--surface);
|
||||
border: 1px solid rgba(96,112,128,.18);
|
||||
box-shadow: 0 20px 48px rgba(7,17,31,.16);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ticket::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 20px auto 20px 24px;
|
||||
width: 54px;
|
||||
background:
|
||||
linear-gradient(var(--text) 0 0) 0 0 / 16px 16px,
|
||||
linear-gradient(var(--text) 0 0) 26px 0 / 16px 16px,
|
||||
linear-gradient(var(--text) 0 0) 0 26px / 16px 16px,
|
||||
linear-gradient(var(--teal-300) 0 0) 26px 26px / 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.ticket::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 96px;
|
||||
top: 24px;
|
||||
width: 132px;
|
||||
height: 42px;
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(var(--line), var(--line)) 0 0 / 100% 10px,
|
||||
linear-gradient(color-mix(in srgb, var(--teal-300), white 70%), color-mix(in srgb, var(--teal-300), white 70%)) 0 28px / 70% 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.wifi-mark {
|
||||
width: var(--s);
|
||||
height: var(--s);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.wifi-mark::before,
|
||||
.wifi-mark::after,
|
||||
.wifi-dot {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border: solid var(--wifi-color);
|
||||
border-width: 10px 10px 0 0;
|
||||
border-radius: 0 100% 0 0;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: 100% 100%;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.wifi-mark::before {
|
||||
inset: 0 0 auto auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.wifi-mark::after {
|
||||
inset: 28% 28% auto auto;
|
||||
width: 44%;
|
||||
height: 44%;
|
||||
}
|
||||
|
||||
.wifi-dot {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 18%;
|
||||
height: 18%;
|
||||
background: var(--wifi-color);
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.shield {
|
||||
width: var(--s);
|
||||
height: calc(var(--s) * 1.25);
|
||||
background: color-mix(in srgb, var(--shield-fill), white 10%);
|
||||
clip-path: polygon(50% 0, 100% 18%, 100% 55%, 50% 100%, 0 55%, 0 18%);
|
||||
opacity: .9;
|
||||
box-shadow: 0 28px 60px rgba(7,17,31,.18);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.shield::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 30%;
|
||||
top: 30%;
|
||||
width: 40%;
|
||||
height: 26%;
|
||||
border: solid var(--navy);
|
||||
border-width: 0 0 14px 14px;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.chips {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.chip {
|
||||
width: var(--chip-w);
|
||||
height: 44px;
|
||||
border-radius: 12px;
|
||||
background: var(--chip-bg);
|
||||
box-shadow: 0 14px 34px rgba(7,17,31,.12);
|
||||
border: 1px solid rgba(255,255,255,.48);
|
||||
}
|
||||
|
||||
.history-steps {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.step {
|
||||
width: var(--step-w);
|
||||
height: 64px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255,255,255,.9);
|
||||
border: 1px solid rgba(96,112,128,.16);
|
||||
box-shadow: 0 18px 44px rgba(7,17,31,.12);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
top: 18px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(var(--navy) 0 0) 6px 6px / 8px 8px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 6px / 8px 8px,
|
||||
linear-gradient(var(--teal-300) 0 0) 6px 18px / 8px 8px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 18px / 8px 8px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.step::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 62px;
|
||||
top: 21px;
|
||||
width: 52%;
|
||||
height: 22px;
|
||||
border-radius: 99px;
|
||||
background: var(--line);
|
||||
box-shadow: 0 28px 0 -6px color-mix(in srgb, var(--teal-300), white 72%);
|
||||
}
|
||||
|
||||
.grid-plate {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 8px;
|
||||
padding: 18px;
|
||||
border-radius: 22px;
|
||||
background: rgba(255,255,255,.82);
|
||||
border: 1px solid rgba(96,112,128,.12);
|
||||
box-shadow: 0 26px 64px rgba(7,17,31,.14);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.grid-plate span {
|
||||
border-radius: 4px;
|
||||
background: var(--text);
|
||||
}
|
||||
|
||||
.grid-plate span:nth-child(5n),
|
||||
.grid-plate span:nth-child(8n) {
|
||||
background: var(--teal-300);
|
||||
}
|
||||
|
||||
.device {
|
||||
width: var(--dw);
|
||||
aspect-ratio: 1080 / 1920;
|
||||
padding: var(--bezel, 15px);
|
||||
border-radius: var(--radius, 46px);
|
||||
background:
|
||||
linear-gradient(150deg, rgba(255,255,255,.28), transparent 18%),
|
||||
linear-gradient(180deg, #111827, #030712);
|
||||
box-shadow:
|
||||
0 42px 90px rgba(7, 17, 31, .34),
|
||||
inset 0 0 0 1px rgba(255, 255, 255, .18);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.device::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 50%;
|
||||
width: 23%;
|
||||
height: 12px;
|
||||
border-radius: 99px;
|
||||
background: #020617;
|
||||
transform: translateX(-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.device img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: calc(var(--radius, 46px) - 12px);
|
||||
background: #07111f;
|
||||
}
|
||||
|
||||
.tablet {
|
||||
width: var(--tw);
|
||||
aspect-ratio: 1440 / 2560;
|
||||
padding: 18px;
|
||||
border-radius: 34px;
|
||||
background: linear-gradient(180deg, #111827, #030712);
|
||||
box-shadow:
|
||||
0 42px 90px rgba(7, 17, 31, .3),
|
||||
inset 0 0 0 1px rgba(255,255,255,.16);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tablet img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 20px;
|
||||
background: #07111f;
|
||||
}
|
||||
|
||||
.soft-light {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(120deg, rgba(255,255,255,.32), transparent 26%),
|
||||
linear-gradient(0deg, rgba(255,255,255,.12), transparent 40%);
|
||||
mix-blend-mode: soft-light;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fallback-list {
|
||||
padding: 28px;
|
||||
font: 20px/1.4 system-ui, sans-serif;
|
||||
color: #111827;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main id="stage"></main>
|
||||
<script>
|
||||
const screenshot = (kind, n) => `../private-qr-scanner-${kind}-screenshot-${n}.png`;
|
||||
|
||||
const creatives = [
|
||||
{
|
||||
id: "pqs-square-01-scanner",
|
||||
size: [1200, 1200],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(255,255,255,.12)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "tileField", x: 64, y: 700, w: 300, h: 300, r: -8, tile: "rgba(223,247,242,.72)" },
|
||||
{ type: "gridPlate", x: 756, y: 128, w: 264, h: 264 },
|
||||
{ type: "beam", x: 188, y: 548, w: 824, r: -2 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 392, y: 94, w: 410, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-02-results",
|
||||
size: [1200, 1200],
|
||||
theme: ["#f6fbfa", "#dff7f2", "#ffffff", "rgba(21,94,99,.16)", "#0f766e"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "codeStack", x: 104, y: 184, w: 374, count: 4 },
|
||||
{ type: "chips", x: 770, y: 210, chipW: 250, colors: ["#dff7f2", "#f2f7ff", "#ffffff"] },
|
||||
{ type: "phone", img: screenshot("phone", 2), x: 392, y: 86, w: 414, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-03-risk",
|
||||
size: [1200, 1200],
|
||||
theme: ["#101722", "#2b2f42", "#ffffff", "rgba(255,200,87,.24)", "#ffc857"],
|
||||
elements: [
|
||||
{ type: "rail", x: -110, y: 210, w: 1440, h: 180, r: -18 },
|
||||
{ type: "shield", x: 764, y: 654, s: 220, fill: "#ffc857" },
|
||||
{ type: "tileField", x: 770, y: 112, w: 260, h: 260, r: 7, tile: "rgba(255,200,87,.78)" },
|
||||
{ type: "phone", img: screenshot("phone", 3), x: 384, y: 84, w: 430, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-04-image-scan",
|
||||
size: [1200, 1200],
|
||||
theme: ["#eef7ff", "#d7efff", "#ffffff", "rgba(85,185,255,.2)", "#55b9ff"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "panel", x: 118, y: 712, w: 452, h: 264 },
|
||||
{ type: "gridPlate", x: 748, y: 178, w: 260, h: 260 },
|
||||
{ type: "beam", x: 184, y: 602, w: 820, r: 4 },
|
||||
{ type: "phone", img: screenshot("phone", 4), x: 390, y: 88, w: 420, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-05-contact",
|
||||
size: [1200, 1200],
|
||||
theme: ["#fff7f3", "#ffe3dc", "#ffffff", "rgba(255,107,95,.18)", "#ff6b5f"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "chips", x: 136, y: 192, chipW: 288, colors: ["#ffffff", "#ffe3dc", "#ffffff", "#dff7f2"] },
|
||||
{ type: "codeStack", x: 744, y: 698, w: 330, count: 3 },
|
||||
{ type: "phone", img: screenshot("phone", 5), x: 390, y: 84, w: 418, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-06-history",
|
||||
size: [1200, 1200],
|
||||
theme: ["#f7f5ff", "#e9e4ff", "#ffffff", "rgba(139,124,246,.2)", "#8b7cf6"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "history", x: 104, y: 228, w: 330, count: 4 },
|
||||
{ type: "tileField", x: 790, y: 690, w: 250, h: 250, r: -4, tile: "rgba(139,124,246,.55)" },
|
||||
{ type: "phone", img: screenshot("phone", 6), x: 392, y: 82, w: 416, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-07-batch",
|
||||
size: [1200, 1200],
|
||||
theme: ["#07111f", "#0f766e", "#dff7f2", "rgba(45,212,191,.16)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "rail", x: -120, y: 802, w: 1450, h: 186, r: 16 },
|
||||
{ type: "ticketStrip", x: 104, y: 182, count: 3, r: -4 },
|
||||
{ type: "beam", x: 220, y: 576, w: 760, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 7), x: 392, y: 86, w: 416, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-08-settings",
|
||||
size: [1200, 1200],
|
||||
theme: ["#ffffff", "#f2f7ff", "#ffffff", "rgba(96,112,128,.14)", "#155e63"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "shield", x: 142, y: 198, s: 210, fill: "#dff7f2" },
|
||||
{ type: "chips", x: 778, y: 692, chipW: 276, colors: ["#dff7f2", "#f2f7ff", "#ffffff"] },
|
||||
{ type: "phone", img: screenshot("phone", 8), x: 394, y: 86, w: 412, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-01-scanner-results",
|
||||
size: [1200, 628],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(255,255,255,.13)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "tileField", x: 88, y: 104, w: 248, h: 248, r: -5, tile: "rgba(223,247,242,.68)" },
|
||||
{ type: "codeStack", x: 790, y: 124, w: 318, count: 3 },
|
||||
{ type: "beam", x: 108, y: 310, w: 984, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 382, y: 32, w: 256, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 2), x: 568, y: 74, w: 216, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-02-gallery",
|
||||
size: [1200, 628],
|
||||
theme: ["#eef7ff", "#d7efff", "#ffffff", "rgba(85,185,255,.2)", "#55b9ff"],
|
||||
elements: [
|
||||
{ type: "rail", x: -120, y: 86, w: 1440, h: 154, r: -10 },
|
||||
{ type: "panel", x: 742, y: 120, w: 326, h: 286 },
|
||||
{ type: "gridPlate", x: 128, y: 158, w: 214, h: 214 },
|
||||
{ type: "phone", img: screenshot("phone", 4), x: 446, y: 34, w: 254, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-03-risk",
|
||||
size: [1200, 628],
|
||||
theme: ["#101722", "#2b2f42", "#ffffff", "rgba(255,200,87,.22)", "#ffc857"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "shield", x: 824, y: 216, s: 176, fill: "#ffc857" },
|
||||
{ type: "tileField", x: 118, y: 148, w: 214, h: 214, r: 8, tile: "rgba(255,200,87,.7)" },
|
||||
{ type: "phone", img: screenshot("phone", 3), x: 466, y: 34, w: 248, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-04-history",
|
||||
size: [1200, 628],
|
||||
theme: ["#f7f5ff", "#e9e4ff", "#ffffff", "rgba(139,124,246,.2)", "#8b7cf6"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "history", x: 112, y: 140, w: 306, count: 3 },
|
||||
{ type: "chips", x: 788, y: 158, chipW: 300, colors: ["#ffffff", "#e9e4ff", "#dff7f2"] },
|
||||
{ type: "phone", img: screenshot("phone", 6), x: 474, y: 34, w: 246, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-05-wifi",
|
||||
size: [1200, 628],
|
||||
theme: ["#ffffff", "#dff7f2", "#ffffff", "rgba(15,118,110,.18)", "#0f766e"],
|
||||
elements: [
|
||||
{ type: "rail", x: -116, y: 390, w: 1432, h: 154, r: 9 },
|
||||
{ type: "wifi", x: 150, y: 164, s: 198, color: "#0f766e" },
|
||||
{ type: "gridPlate", x: 828, y: 172, w: 206, h: 206 },
|
||||
{ type: "phone", img: screenshot("phone", 5), x: 466, y: 34, w: 250, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-06-event",
|
||||
size: [1200, 628],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(45,212,191,.16)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "ticketStrip", x: 102, y: 118, count: 3, r: -3 },
|
||||
{ type: "beam", x: 196, y: 306, w: 806, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 7), x: 474, y: 34, w: 250, r: 0 },
|
||||
{ type: "ticketStrip", x: 842, y: 152, count: 2, r: 5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-07-tablet",
|
||||
size: [1200, 628],
|
||||
theme: ["#f6fbfa", "#dff7f2", "#ffffff", "rgba(21,94,99,.16)", "#155e63"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "codeStack", x: 108, y: 144, w: 330, count: 3 },
|
||||
{ type: "tablet", img: screenshot("7in-tablet", 2), x: 492, y: 18, w: 258, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 712, y: 62, w: 212, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-08-settings",
|
||||
size: [1200, 628],
|
||||
theme: ["#ffffff", "#f2f7ff", "#ffffff", "rgba(96,112,128,.14)", "#607080"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "shield", x: 154, y: 168, s: 176, fill: "#dff7f2" },
|
||||
{ type: "chips", x: 796, y: 156, chipW: 290, colors: ["#ffffff", "#dff7f2", "#f2f7ff"] },
|
||||
{ type: "phone", img: screenshot("phone", 8), x: 474, y: 34, w: 246, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-01-scanner",
|
||||
size: [1200, 1500],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(255,255,255,.12)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "tileField", x: 126, y: 900, w: 280, h: 280, r: -6, tile: "rgba(223,247,242,.68)" },
|
||||
{ type: "gridPlate", x: 760, y: 180, w: 256, h: 256 },
|
||||
{ type: "beam", x: 160, y: 700, w: 880, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 366, y: 152, w: 470, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-02-gallery",
|
||||
size: [1200, 1500],
|
||||
theme: ["#eef7ff", "#d7efff", "#ffffff", "rgba(85,185,255,.2)", "#55b9ff"],
|
||||
elements: [
|
||||
{ type: "rail", x: -146, y: 248, w: 1510, h: 188, r: -14 },
|
||||
{ type: "panel", x: 128, y: 1004, w: 380, h: 248 },
|
||||
{ type: "gridPlate", x: 782, y: 240, w: 230, h: 230 },
|
||||
{ type: "phone", img: screenshot("phone", 4), x: 366, y: 154, w: 470, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-03-event",
|
||||
size: [1200, 1500],
|
||||
theme: ["#07111f", "#0f766e", "#dff7f2", "rgba(45,212,191,.16)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "ticketStrip", x: 122, y: 178, count: 3, r: -4 },
|
||||
{ type: "ticketStrip", x: 800, y: 902, count: 3, r: 4 },
|
||||
{ type: "beam", x: 186, y: 722, w: 828, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 7), x: 366, y: 152, w: 470, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-04-privacy",
|
||||
size: [1200, 1500],
|
||||
theme: ["#ffffff", "#f2f7ff", "#ffffff", "rgba(96,112,128,.14)", "#155e63"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "shield", x: 122, y: 214, s: 230, fill: "#dff7f2" },
|
||||
{ type: "history", x: 782, y: 866, w: 292, count: 3 },
|
||||
{ type: "chips", x: 136, y: 1020, chipW: 300, colors: ["#dff7f2", "#f2f7ff", "#ffffff"] },
|
||||
{ type: "phone", img: screenshot("phone", 8), x: 366, y: 154, w: 470, r: 0 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function px(v) {
|
||||
return `${v}px`;
|
||||
}
|
||||
|
||||
function setBox(el, item) {
|
||||
if (item.x !== undefined) el.style.left = px(item.x);
|
||||
if (item.y !== undefined) el.style.top = px(item.y);
|
||||
if (item.w !== undefined) el.style.width = px(item.w);
|
||||
if (item.h !== undefined) el.style.height = px(item.h);
|
||||
if (item.r !== undefined) el.style.setProperty("--r", `${item.r}deg`);
|
||||
}
|
||||
|
||||
function tilePattern(count) {
|
||||
return Array.from({ length: count }, (_, index) => {
|
||||
const span = document.createElement("span");
|
||||
if ([1, 4, 7, 10, 13, 16, 20, 24, 31, 36, 38, 43, 48, 51, 57, 64, 67, 71, 78].includes(index)) {
|
||||
span.style.visibility = "hidden";
|
||||
}
|
||||
if (index % 11 === 0) span.style.setProperty("--o", ".38");
|
||||
return span;
|
||||
});
|
||||
}
|
||||
|
||||
function renderElement(item) {
|
||||
let el;
|
||||
|
||||
if (item.type === "band") {
|
||||
el = document.createElement("div");
|
||||
el.className = `band ${item.cls || ""}`;
|
||||
}
|
||||
|
||||
if (item.type === "rail") {
|
||||
el = document.createElement("div");
|
||||
el.className = "rail";
|
||||
setBox(el, item);
|
||||
}
|
||||
|
||||
if (item.type === "panel") {
|
||||
el = document.createElement("div");
|
||||
el.className = "panel";
|
||||
setBox(el, item);
|
||||
}
|
||||
|
||||
if (item.type === "tileField") {
|
||||
el = document.createElement("div");
|
||||
el.className = "tile-field";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--tile", item.tile);
|
||||
tilePattern(81).forEach((span) => el.appendChild(span));
|
||||
}
|
||||
|
||||
if (item.type === "gridPlate") {
|
||||
el = document.createElement("div");
|
||||
el.className = "grid-plate";
|
||||
setBox(el, item);
|
||||
tilePattern(36).forEach((span) => el.appendChild(span));
|
||||
}
|
||||
|
||||
if (item.type === "beam") {
|
||||
el = document.createElement("div");
|
||||
el.className = "scan-beam";
|
||||
setBox(el, { ...item, h: 8 });
|
||||
}
|
||||
|
||||
if (item.type === "codeStack") {
|
||||
el = document.createElement("div");
|
||||
el.className = "code-stack";
|
||||
setBox(el, item);
|
||||
for (let i = 0; i < item.count; i += 1) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "code-card";
|
||||
card.style.width = px(item.w - i * 18);
|
||||
card.style.marginLeft = px(i % 2 ? 22 : 0);
|
||||
el.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "ticketStrip") {
|
||||
el = document.createElement("div");
|
||||
el.className = "ticket-strip";
|
||||
setBox(el, item);
|
||||
for (let i = 0; i < item.count; i += 1) {
|
||||
const ticket = document.createElement("div");
|
||||
ticket.className = "ticket";
|
||||
ticket.style.marginLeft = px(i % 2 ? 28 : 0);
|
||||
el.appendChild(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "wifi") {
|
||||
el = document.createElement("div");
|
||||
el.className = "wifi-mark";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--s", px(item.s));
|
||||
el.style.setProperty("--wifi-color", item.color);
|
||||
const dot = document.createElement("div");
|
||||
dot.className = "wifi-dot";
|
||||
el.appendChild(dot);
|
||||
}
|
||||
|
||||
if (item.type === "shield") {
|
||||
el = document.createElement("div");
|
||||
el.className = "shield";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--s", px(item.s));
|
||||
el.style.setProperty("--shield-fill", item.fill);
|
||||
}
|
||||
|
||||
if (item.type === "chips") {
|
||||
el = document.createElement("div");
|
||||
el.className = "chips";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--chip-w", px(item.chipW));
|
||||
item.colors.forEach((color, index) => {
|
||||
const chip = document.createElement("div");
|
||||
chip.className = "chip";
|
||||
chip.style.setProperty("--chip-bg", color);
|
||||
chip.style.marginLeft = px(index % 2 ? 24 : 0);
|
||||
el.appendChild(chip);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.type === "history") {
|
||||
el = document.createElement("div");
|
||||
el.className = "history-steps";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--step-w", px(item.w));
|
||||
for (let i = 0; i < item.count; i += 1) {
|
||||
const step = document.createElement("div");
|
||||
step.className = "step";
|
||||
step.style.marginLeft = px(i % 2 ? 26 : 0);
|
||||
el.appendChild(step);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "phone") {
|
||||
el = document.createElement("div");
|
||||
el.className = "device";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--dw", px(item.w));
|
||||
const img = document.createElement("img");
|
||||
img.src = item.img;
|
||||
img.alt = "";
|
||||
el.appendChild(img);
|
||||
}
|
||||
|
||||
if (item.type === "tablet") {
|
||||
el = document.createElement("div");
|
||||
el.className = "tablet";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--tw", px(item.w));
|
||||
const img = document.createElement("img");
|
||||
img.src = item.img;
|
||||
img.alt = "";
|
||||
el.appendChild(img);
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function renderCreative(creative) {
|
||||
const stage = document.getElementById("stage");
|
||||
const [w, h] = creative.size;
|
||||
document.documentElement.style.setProperty("--w", px(w));
|
||||
document.documentElement.style.setProperty("--h", px(h));
|
||||
stage.style.setProperty("--base", creative.theme[0]);
|
||||
stage.style.setProperty("--band", creative.theme[1]);
|
||||
stage.style.setProperty("--panel-bg", creative.theme[2]);
|
||||
stage.style.setProperty("--panel-border", creative.theme[3]);
|
||||
stage.style.setProperty("--tile", creative.theme[4]);
|
||||
document.title = creative.id;
|
||||
|
||||
creative.elements.forEach((item) => stage.appendChild(renderElement(item)));
|
||||
|
||||
const light = document.createElement("div");
|
||||
light.className = "soft-light";
|
||||
stage.appendChild(light);
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
const requested = params.get("creative");
|
||||
const creative = creatives.find((item) => item.id === requested);
|
||||
|
||||
if (creative) {
|
||||
renderCreative(creative);
|
||||
window.__creativeReady = true;
|
||||
} else {
|
||||
const list = document.createElement("div");
|
||||
list.className = "fallback-list";
|
||||
list.innerHTML = `<strong>Available creatives</strong><br>${creatives.map((item) => item.id).join("<br>")}`;
|
||||
document.body.appendChild(list);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,945 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="ad.orientation" content="portrait,landscape">
|
||||
<title>Private QR Scanner</title>
|
||||
<link rel="preload" as="image" href="assets/app-icon.png">
|
||||
<link rel="preload" as="image" href="assets/phone-scanner.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-result.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-warning.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-image.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-history.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-batch.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-settings.jpg">
|
||||
<script type="text/javascript" src="https://tpc.googlesyndication.com/pagead/gadgets/html5/api/exitapi.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--deep: #07111f;
|
||||
--navy: #0b1220;
|
||||
--teal-900: #123b3f;
|
||||
--teal-800: #155e63;
|
||||
--teal-700: #0f766e;
|
||||
--teal-300: #2dd4bf;
|
||||
--mint: #dff7f2;
|
||||
--paper: #f6fbfa;
|
||||
--surface: #ffffff;
|
||||
--soft-blue: #f2f7ff;
|
||||
--line: #dce8e6;
|
||||
--muted: #607080;
|
||||
--amber: #ffc857;
|
||||
--coral: #ff6b5f;
|
||||
--sky: #55b9ff;
|
||||
--violet: #8b7cf6;
|
||||
--shadow: rgba(7, 17, 31, 0.28);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
color: var(--navy);
|
||||
background: var(--paper);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
letter-spacing: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
user-select: none;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
button {
|
||||
font: inherit;
|
||||
letter-spacing: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.ad {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255,255,255,0.96), rgba(242,247,255,0.96) 40%, rgba(223,247,242,0.92)),
|
||||
var(--paper);
|
||||
}
|
||||
|
||||
.band {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
left: -16vw;
|
||||
right: -16vw;
|
||||
bottom: -13vh;
|
||||
height: 40vh;
|
||||
background: linear-gradient(135deg, var(--teal-800), var(--teal-900));
|
||||
transform: skewY(-8deg);
|
||||
}
|
||||
|
||||
.band-top {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
left: -18vw;
|
||||
right: -18vw;
|
||||
top: -16vh;
|
||||
height: 34vh;
|
||||
background: linear-gradient(135deg, rgba(85,185,255,0.25), rgba(45,212,191,0.26));
|
||||
transform: skewY(-7deg);
|
||||
}
|
||||
|
||||
.qr-field,
|
||||
.ticket-stack,
|
||||
.scan-beam,
|
||||
.shield,
|
||||
.wifi,
|
||||
.mini-list {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.qr-field {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 5px;
|
||||
width: 132px;
|
||||
height: 132px;
|
||||
padding: 9px;
|
||||
border-radius: 16px;
|
||||
background: rgba(255,255,255,0.72);
|
||||
box-shadow: 0 18px 46px rgba(7,17,31,0.12);
|
||||
transform: rotate(-8deg);
|
||||
}
|
||||
|
||||
.qr-field span {
|
||||
border-radius: 2px;
|
||||
background: var(--navy);
|
||||
}
|
||||
|
||||
.qr-field span:nth-child(3n),
|
||||
.qr-field span:nth-child(11n) {
|
||||
background: var(--teal-300);
|
||||
}
|
||||
|
||||
.qr-field span:nth-child(5n),
|
||||
.qr-field span:nth-child(8n),
|
||||
.qr-field span:nth-child(13n) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ticket-stack {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
width: 168px;
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
|
||||
.ticket {
|
||||
height: 54px;
|
||||
border-radius: 12px;
|
||||
background: var(--surface);
|
||||
border: 1px solid rgba(96,112,128,0.16);
|
||||
box-shadow: 0 14px 34px rgba(7,17,31,0.12);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ticket:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 13px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background:
|
||||
linear-gradient(var(--navy) 0 0) 0 0 / 10px 10px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 0 / 10px 10px,
|
||||
linear-gradient(var(--teal-300) 0 0) 0 18px / 10px 10px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 18px / 10px 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.ticket:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 56px;
|
||||
top: 16px;
|
||||
width: 84px;
|
||||
height: 20px;
|
||||
border-radius: 999px;
|
||||
background: var(--line);
|
||||
box-shadow: 0 18px 0 -5px rgba(45,212,191,0.38);
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
width: 52vw;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, transparent, var(--teal-300), transparent);
|
||||
box-shadow: 0 0 30px rgba(45,212,191,0.9);
|
||||
opacity: 0.92;
|
||||
animation: beamPulse 1.7s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes beamPulse {
|
||||
0% { transform: translateY(-10px); opacity: 0.45; }
|
||||
50% { transform: translateY(10px); opacity: 1; }
|
||||
100% { transform: translateY(-10px); opacity: 0.45; }
|
||||
}
|
||||
|
||||
.shield {
|
||||
width: 80px;
|
||||
height: 100px;
|
||||
background: var(--amber);
|
||||
clip-path: polygon(50% 0, 100% 18%, 100% 55%, 50% 100%, 0 55%, 0 18%);
|
||||
box-shadow: 0 16px 34px rgba(7,17,31,0.16);
|
||||
}
|
||||
|
||||
.shield:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 30%;
|
||||
top: 30%;
|
||||
width: 40%;
|
||||
height: 24%;
|
||||
border: solid var(--navy);
|
||||
border-width: 0 0 8px 8px;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.wifi {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.wifi:before,
|
||||
.wifi:after,
|
||||
.wifi-dot {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: solid var(--teal-700);
|
||||
border-width: 8px 8px 0 0;
|
||||
border-radius: 0 100% 0 0;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
.wifi:before {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
}
|
||||
|
||||
.wifi:after {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.wifi-dot {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--teal-700);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.mini-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
width: 166px;
|
||||
}
|
||||
|
||||
.mini-row {
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.84);
|
||||
box-shadow: 0 12px 28px rgba(7,17,31,0.1);
|
||||
border: 1px solid rgba(96,112,128,0.12);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mini-row:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 11px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 5px;
|
||||
background: var(--teal-300);
|
||||
}
|
||||
|
||||
.mini-row:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 42px;
|
||||
top: 13px;
|
||||
width: 92px;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.brand,
|
||||
.copy,
|
||||
.phone-wrap,
|
||||
.panel {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 22px rgba(7,17,31,0.15);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.brand strong {
|
||||
display: block;
|
||||
color: var(--navy);
|
||||
font-size: 16px;
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
display: block;
|
||||
margin-top: 3px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
margin: 0;
|
||||
color: var(--navy);
|
||||
font-size: 32px;
|
||||
line-height: 1.02;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.copy p {
|
||||
margin: 12px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.34;
|
||||
max-width: 330px;
|
||||
}
|
||||
|
||||
.pills {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
max-width: 330px;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255,255,255,0.74);
|
||||
color: var(--teal-900);
|
||||
border: 1px solid rgba(96,112,128,0.14);
|
||||
font-size: 12px;
|
||||
line-height: 1.15;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 10px 24px rgba(7,17,31,0.07);
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
border-radius: 32px;
|
||||
padding: 11px;
|
||||
background: linear-gradient(180deg, #111827, #030712);
|
||||
box-shadow:
|
||||
0 34px 72px rgba(7,17,31,0.34),
|
||||
inset 0 0 0 1px rgba(255,255,255,0.16);
|
||||
}
|
||||
|
||||
.phone-wrap:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 50%;
|
||||
width: 58px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: #020617;
|
||||
transform: translateX(-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.phone-screen {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 23px;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 20px;
|
||||
padding: 16px;
|
||||
background: rgba(255,255,255,0.88);
|
||||
border: 1px solid rgba(96,112,128,0.14);
|
||||
box-shadow: 0 24px 54px rgba(7,17,31,0.16);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.panel-kicker {
|
||||
color: var(--teal-700);
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 8px 0 0;
|
||||
color: var(--navy);
|
||||
font-size: 20px;
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.panel p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.32;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
background: var(--soft-blue);
|
||||
color: var(--navy);
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
box-shadow: inset 0 0 0 1px rgba(96,112,128,0.16);
|
||||
}
|
||||
|
||||
.dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: #b7c7d8;
|
||||
}
|
||||
|
||||
.dot.active {
|
||||
width: 20px;
|
||||
background: var(--teal-700);
|
||||
}
|
||||
|
||||
.cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 46px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 18px;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, var(--teal-700), var(--teal-800));
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
box-shadow: 0 18px 34px rgba(15,118,110,0.28);
|
||||
}
|
||||
|
||||
.glint {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
background:
|
||||
linear-gradient(120deg, rgba(255,255,255,0.34), transparent 24%),
|
||||
linear-gradient(0deg, rgba(255,255,255,0.14), transparent 42%);
|
||||
mix-blend-mode: soft-light;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) {
|
||||
.brand {
|
||||
left: 26px;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 28px;
|
||||
right: 28px;
|
||||
top: 92px;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
max-width: 300px;
|
||||
font-size: 31px;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
left: 50%;
|
||||
top: 276px;
|
||||
width: 226px;
|
||||
height: 402px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
left: 22px;
|
||||
right: 22px;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.qr-field {
|
||||
right: 16px;
|
||||
top: 188px;
|
||||
}
|
||||
|
||||
.ticket-stack {
|
||||
left: 20px;
|
||||
top: 386px;
|
||||
}
|
||||
|
||||
.shield {
|
||||
right: 44px;
|
||||
bottom: 214px;
|
||||
}
|
||||
|
||||
.wifi {
|
||||
left: 32px;
|
||||
bottom: 184px;
|
||||
}
|
||||
|
||||
.mini-list {
|
||||
right: 22px;
|
||||
top: 508px;
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
left: 24vw;
|
||||
top: 496px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) {
|
||||
.brand {
|
||||
left: 34px;
|
||||
top: 30px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 36px;
|
||||
top: 100px;
|
||||
width: 32vw;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
max-width: 330px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 178px;
|
||||
height: 316px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
right: 26px;
|
||||
top: 54px;
|
||||
width: 30vw;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.qr-field {
|
||||
left: 38vw;
|
||||
top: 34px;
|
||||
width: 116px;
|
||||
height: 116px;
|
||||
}
|
||||
|
||||
.ticket-stack {
|
||||
right: 32px;
|
||||
bottom: 22px;
|
||||
}
|
||||
|
||||
.shield {
|
||||
right: 22vw;
|
||||
bottom: 40px;
|
||||
}
|
||||
|
||||
.wifi {
|
||||
left: 28vw;
|
||||
bottom: 24px;
|
||||
}
|
||||
|
||||
.mini-list {
|
||||
right: 25vw;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
left: 28vw;
|
||||
top: 50%;
|
||||
width: 45vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 340px) and (orientation: portrait) {
|
||||
.brand {
|
||||
top: 16px;
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
.brand div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.brand strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 20px;
|
||||
right: 158px;
|
||||
top: 74px;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.copy p,
|
||||
.pills {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
left: calc(100% - 86px);
|
||||
top: 96px;
|
||||
width: 132px;
|
||||
height: 235px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
bottom: 12px;
|
||||
padding: 13px;
|
||||
}
|
||||
|
||||
.panel p,
|
||||
.controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.cta {
|
||||
min-height: 40px;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.qr-field,
|
||||
.mini-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
top: 232px;
|
||||
width: 58vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-height: 360px) and (orientation: landscape) {
|
||||
.brand {
|
||||
left: 22px;
|
||||
top: 16px;
|
||||
}
|
||||
|
||||
.brand div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.brand span,
|
||||
.pills,
|
||||
.copy p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 22px;
|
||||
top: 74px;
|
||||
width: 34vw;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
width: 140px;
|
||||
height: 249px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
right: 18px;
|
||||
top: 18px;
|
||||
width: 30vw;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.panel p,
|
||||
.controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.panel p {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cta {
|
||||
min-height: 40px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.qr-field,
|
||||
.ticket-stack,
|
||||
.mini-list {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ad" id="ad">
|
||||
<div class="band-top"></div>
|
||||
<div class="band"></div>
|
||||
|
||||
<div class="qr-field" aria-hidden="true">
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
</div>
|
||||
|
||||
<div class="ticket-stack" aria-hidden="true">
|
||||
<div class="ticket"></div>
|
||||
<div class="ticket"></div>
|
||||
<div class="ticket"></div>
|
||||
</div>
|
||||
|
||||
<div class="mini-list" aria-hidden="true">
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-row"></div>
|
||||
</div>
|
||||
|
||||
<div class="shield" aria-hidden="true"></div>
|
||||
<div class="wifi" aria-hidden="true"><div class="wifi-dot"></div></div>
|
||||
<div class="scan-beam" aria-hidden="true"></div>
|
||||
|
||||
<div class="brand">
|
||||
<img src="assets/app-icon.png" alt="">
|
||||
<div>
|
||||
<strong>Private QR Scanner</strong>
|
||||
<span>No ads. No tracking.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="copy" aria-label="Private QR Scanner campaign">
|
||||
<h1>Scan QR codes privately</h1>
|
||||
<p>Read codes on device, inspect links, and keep optional history local.</p>
|
||||
<div class="pills">
|
||||
<span class="pill">QR and barcodes</span>
|
||||
<span class="pill">Image scan</span>
|
||||
<span class="pill">Local warnings</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="phone-wrap" aria-hidden="true">
|
||||
<img class="phone-screen" id="phoneScreen" src="assets/phone-scanner.jpg" alt="">
|
||||
</div>
|
||||
|
||||
<section class="panel" aria-label="Feature preview">
|
||||
<div class="panel-kicker" id="stepNumber">Step 1 of 7</div>
|
||||
<h2 id="stepTitle">Live camera scanning</h2>
|
||||
<p id="stepCopy">Aim at a QR code or barcode and review clear results before taking action.</p>
|
||||
<div class="controls">
|
||||
<button class="control-btn" id="prevButton" type="button" aria-label="Previous feature">‹</button>
|
||||
<div class="dots" id="dots" aria-hidden="true"></div>
|
||||
<button class="control-btn" id="nextButton" type="button" aria-label="Next feature">›</button>
|
||||
</div>
|
||||
<button class="cta" id="cta" type="button">Try the app</button>
|
||||
</section>
|
||||
|
||||
<div class="glint"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var slides = [
|
||||
{
|
||||
image: "assets/phone-scanner.jpg",
|
||||
title: "Live camera scanning",
|
||||
copy: "Aim at a QR code or barcode and review clear results before taking action."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-result.jpg",
|
||||
title: "Useful result actions",
|
||||
copy: "Copy, share, open links, add contacts, or use the action that matches the scan."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-warning.jpg",
|
||||
title: "Local link warnings",
|
||||
copy: "Pause before opening unusual links with on-device URL risk checks."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-image.jpg",
|
||||
title: "Scan from an image",
|
||||
copy: "Choose a saved picture and detect QR codes or barcodes inside it."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-history.jpg",
|
||||
title: "Optional local history",
|
||||
copy: "Keep scan records only when you want them, stored locally on your device."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-batch.jpg",
|
||||
title: "Batch scanning",
|
||||
copy: "Capture multiple codes in one flow for events, tickets, or fast workflows."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-settings.jpg",
|
||||
title: "Simple controls",
|
||||
copy: "Choose history, warnings, feedback, and app behavior in one clear settings view."
|
||||
}
|
||||
];
|
||||
|
||||
var index = 0;
|
||||
var phoneScreen = document.getElementById("phoneScreen");
|
||||
var stepNumber = document.getElementById("stepNumber");
|
||||
var stepTitle = document.getElementById("stepTitle");
|
||||
var stepCopy = document.getElementById("stepCopy");
|
||||
var dots = document.getElementById("dots");
|
||||
var prevButton = document.getElementById("prevButton");
|
||||
var nextButton = document.getElementById("nextButton");
|
||||
var cta = document.getElementById("cta");
|
||||
|
||||
function makeDots() {
|
||||
var fragment = document.createDocumentFragment();
|
||||
var i;
|
||||
for (i = 0; i < slides.length; i += 1) {
|
||||
var dot = document.createElement("span");
|
||||
dot.className = "dot";
|
||||
fragment.appendChild(dot);
|
||||
}
|
||||
dots.appendChild(fragment);
|
||||
}
|
||||
|
||||
function renderSlide() {
|
||||
var slide = slides[index];
|
||||
var dotNodes = dots.getElementsByClassName("dot");
|
||||
var i;
|
||||
phoneScreen.src = slide.image;
|
||||
stepNumber.innerHTML = "Step " + (index + 1) + " of " + slides.length;
|
||||
stepTitle.innerHTML = slide.title;
|
||||
stepCopy.innerHTML = slide.copy;
|
||||
for (i = 0; i < dotNodes.length; i += 1) {
|
||||
dotNodes[i].className = i === index ? "dot active" : "dot";
|
||||
}
|
||||
}
|
||||
|
||||
function move(delta) {
|
||||
index = (index + delta + slides.length) % slides.length;
|
||||
renderSlide();
|
||||
}
|
||||
|
||||
function exitAd() {
|
||||
if (window.ExitApi && typeof window.ExitApi.exit === "function") {
|
||||
window.ExitApi.exit();
|
||||
}
|
||||
}
|
||||
|
||||
makeDots();
|
||||
renderSlide();
|
||||
prevButton.addEventListener("click", function(event) {
|
||||
event.stopPropagation();
|
||||
move(-1);
|
||||
});
|
||||
nextButton.addEventListener("click", function(event) {
|
||||
event.stopPropagation();
|
||||
move(1);
|
||||
});
|
||||
cta.addEventListener("click", function(event) {
|
||||
event.stopPropagation();
|
||||
exitAd();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 170 KiB |