@tokenoftrust/cli 1.3.4-rc.3 → 1.3.4-rc.5

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 (41) hide show
  1. package/bin/tot.cjs +44 -16
  2. package/bin/tot.mjs +8 -0
  3. package/package.json +2 -1
  4. package/src/app-scaffold.mjs +84 -0
  5. package/src/banner.mjs +46 -0
  6. package/src/commands/app/dev.mjs +268 -0
  7. package/src/commands/app/index.mjs +35 -0
  8. package/src/commands/app/scaffold.mjs +57 -0
  9. package/src/commands/checkout.mjs +5 -0
  10. package/src/commands/dev.mjs +61 -14
  11. package/src/commands/start.mjs +76 -17
  12. package/src/commands/validate.mjs +4 -0
  13. package/src/dev-logs.mjs +34 -7
  14. package/src/obstacle-beacon.cjs +111 -0
  15. package/src/obstacle.mjs +35 -0
  16. package/src/validate.mjs +77 -8
  17. package/src/vendor/private-apps-devkit.mjs +490 -0
  18. package/template/private-app/.env.example +10 -0
  19. package/template/private-app/Dockerfile +12 -0
  20. package/template/private-app/README.md +45 -0
  21. package/template/private-app/fixtures/order.created.cloudevent.json +36 -0
  22. package/template/private-app/server.js +81 -0
  23. package/template/private-app/tot-app.json +29 -0
  24. package/template/sample-store/content/chrome.html +140 -0
  25. package/template/sample-store/content/chrome.json +86 -0
  26. package/template/sample-store/content/home.html +121 -0
  27. package/template/sample-store/content/home.json +50 -0
  28. package/template/sample-store/content/pages/about.json +10 -0
  29. package/template/sample-store/content/pages/privacy.json +10 -0
  30. package/template/sample-store/content/pages/shipping-returns.json +10 -0
  31. package/template/sample-store/content/pages-html/blogs/news.html +26 -0
  32. package/template/sample-store/content/pages-html/pages/about-us.html +44 -0
  33. package/template/sample-store/content/pages-html/pages/contact-us.html +48 -0
  34. package/template/sample-store/content/pages-html/pages/privacy-policy.html +27 -0
  35. package/template/sample-store/content/pages-html/pages/shipping-returns.html +25 -0
  36. package/template/sample-store/public/logo.svg +6 -0
  37. package/template/sample-store/public/pages/home.css +120 -0
  38. package/template/sample-store/public/pages/mkt.css +185 -0
  39. package/template/sample-store/public/pages/page.css +155 -0
  40. package/template/sample-store/public/themes/sample.css +76 -0
  41. package/template/sample-store/theme.json +38 -0
@@ -0,0 +1,26 @@
1
+ <head>
2
+ <title>News | Sample Vapor Co.</title>
3
+ <meta name="description" content="Sample blog index for the fictional Sample Vapor Co. demo store.">
4
+ <link rel="canonical" href="https://sample-store.example/blogs/news">
5
+ <meta name="robots" content="noindex,nofollow">
6
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/page.css">
7
+ </head>
8
+ <main id="main">
9
+ <section class="page-hero section--tight">
10
+ <div class="container">
11
+ <nav class="crumbs" aria-label="Breadcrumb"><a href="/">Home</a> / News</nav>
12
+ <span class="eyebrow">Sample content</span>
13
+ <h1>From the Blog</h1>
14
+ <p>Placeholder blog index for a fictional demo store.</p>
15
+ </div>
16
+ </section>
17
+ <section class="section">
18
+ <div class="container">
19
+ <div class="bloggrid">
20
+ <a class="blogcard" href="/blogs/news"><div class="blogcard__media"></div><h3>Sample blog post one</h3><span class="link-more">Read more →</span></a>
21
+ <a class="blogcard" href="/blogs/news"><div class="blogcard__media"></div><h3>Sample blog post two</h3><span class="link-more">Read more →</span></a>
22
+ <a class="blogcard" href="/blogs/news"><div class="blogcard__media"></div><h3>Sample blog post three</h3><span class="link-more">Read more →</span></a>
23
+ </div>
24
+ </div>
25
+ </section>
26
+ </main>
@@ -0,0 +1,44 @@
1
+ <head>
2
+ <title>About | Sample Vapor Co.</title>
3
+ <meta name="description" content="Sample Vapor Co. is a fictional demo store bundled with the Token of Trust local dev loop.">
4
+ <link rel="canonical" href="https://sample-store.example/pages/about-us">
5
+ <meta name="robots" content="noindex,nofollow">
6
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/page.css">
7
+ </head>
8
+ <main id="main">
9
+ <section class="page-hero page-hero--center section--tight">
10
+ <div class="container">
11
+ <nav class="crumbs" aria-label="Breadcrumb"><a href="/">Home</a> / About Us</nav>
12
+ <span class="eyebrow">Sample content</span>
13
+ <h1>A fictional demo store.</h1>
14
+ <p>Sample Vapor Co. is not a real merchant — it ships with the local dev loop as neutral sample content.</p>
15
+ </div>
16
+ </section>
17
+
18
+ <section class="section split" aria-labelledby="story-h">
19
+ <div class="container">
20
+ <div class="split__copy">
21
+ <h2 id="story-h">About this demo</h2>
22
+ <p>Sample Vapor Co. exists so you can see the renderer, the shared chrome, and the 21+ age-gate / nicotine-warning compliance affordances on neutral content — without any real store's data.</p>
23
+ <p>Edit these files under <code>content/</code> and save to watch the local dev server reload the page.</p>
24
+ <a class="btn btn-primary" href="/collections/e-liquid">Shop the sample catalog</a>
25
+ </div>
26
+ <div class="split__media"></div>
27
+ </div>
28
+ </section>
29
+
30
+ <section class="section section--surface" aria-labelledby="values-h">
31
+ <div class="container">
32
+ <div class="sec-head">
33
+ <h2 id="values-h">What this shows</h2>
34
+ <p>The pieces the demo exercises.</p>
35
+ </div>
36
+ <div class="value-grid">
37
+ <div class="value-card"><h3>Shared chrome</h3><p>Header, nav, footer, and compliance strips render from the tenant's chrome files.</p></div>
38
+ <div class="value-card"><h3>Compliance affordances</h3><p>The 21+ age gate and nicotine warnings are config-driven — no merchant data required.</p></div>
39
+ <div class="value-card"><h3>Hot reload</h3><p>Edit any file under <code>content/</code> and save to see it update live.</p></div>
40
+ <div class="value-card"><h3>Your own store next</h3><p>Check out a real tenant to replace this sample with your content.</p></div>
41
+ </div>
42
+ </div>
43
+ </section>
44
+ </main>
@@ -0,0 +1,48 @@
1
+ <head>
2
+ <title>Contact | Sample Vapor Co.</title>
3
+ <meta name="description" content="Sample contact page for the fictional Sample Vapor Co. demo store.">
4
+ <link rel="canonical" href="https://sample-store.example/pages/contact-us">
5
+ <meta name="robots" content="noindex,nofollow">
6
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/page.css">
7
+ </head>
8
+ <main id="main">
9
+
10
+ <section class="page-hero page-hero--center section--tight">
11
+ <div class="container">
12
+ <nav class="crumbs" aria-label="Breadcrumb"><a href="/">Home</a> / Contact Us</nav>
13
+ <h1>Contact Sample Vapor Co.</h1>
14
+ <p>This is a placeholder contact page for a fictional demo store. Nothing here submits anywhere.</p>
15
+ </div>
16
+ </section>
17
+
18
+ <section class="section" aria-labelledby="contact-h">
19
+ <h2 id="contact-h" class="sr-only">Get in touch</h2>
20
+ <div class="container">
21
+ <div class="contact-grid">
22
+ <div class="contact-card">
23
+ <h3>Support hours</h3>
24
+ <p>Sample copy — set your real support hours here.</p>
25
+ </div>
26
+ <div class="contact-card">
27
+ <h3>Customer support</h3>
28
+ <p>Sample copy — describe how customers reach you.</p>
29
+ </div>
30
+ <div class="contact-card">
31
+ <h3>Track your order</h3>
32
+ <p>Sample copy — link your order-tracking flow here.</p>
33
+ </div>
34
+ </div>
35
+
36
+ <form class="contact-form" action="/api/contact" method="post">
37
+ <div class="row2">
38
+ <input type="text" name="name" aria-label="Name" placeholder="Name" required>
39
+ <input type="email" name="email" aria-label="Email" placeholder="Email" required>
40
+ </div>
41
+ <input type="text" name="order" aria-label="Order number (optional)" placeholder="Order number (optional)">
42
+ <textarea name="comment" aria-label="How can we help?" placeholder="How can we help?"></textarea>
43
+ <button type="submit" class="btn btn-primary">Send Message</button>
44
+ </form>
45
+ </div>
46
+ </section>
47
+
48
+ </main>
@@ -0,0 +1,27 @@
1
+ <head>
2
+ <title>Privacy Policy | Sample Vapor Co.</title>
3
+ <meta name="description" content="Sample privacy policy for the fictional Sample Vapor Co. demo store.">
4
+ <link rel="canonical" href="https://sample-store.example/pages/privacy-policy">
5
+ <meta name="robots" content="noindex,nofollow">
6
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/page.css">
7
+ </head>
8
+ <main id="main">
9
+ <section class="page-hero section--tight">
10
+ <div class="container">
11
+ <nav class="crumbs" aria-label="Breadcrumb"><a href="/">Home</a> / Privacy Policy</nav>
12
+ <span class="eyebrow">Sample content</span>
13
+ <h1>Privacy Policy</h1>
14
+ </div>
15
+ </section>
16
+ <section class="section">
17
+ <div class="container prose">
18
+ <p><em>Sample Vapor Co. is a fictional demo store bundled with the Token of Trust local dev loop. The text below is neutral placeholder copy — it is not a real privacy policy.</em></p>
19
+ <h2>Information we collect</h2>
20
+ <p>A real store would describe the information it collects here. Replace this with your own copy.</p>
21
+ <h2>How we use information</h2>
22
+ <p>Describe how your store uses the information it collects, and any third parties involved.</p>
23
+ <h2>Your choices</h2>
24
+ <p>Describe the choices customers have about their information. Edit this file and save to see it reload.</p>
25
+ </div>
26
+ </section>
27
+ </main>
@@ -0,0 +1,25 @@
1
+ <head>
2
+ <title>Shipping &amp; Returns | Sample Vapor Co.</title>
3
+ <meta name="description" content="Sample shipping and returns copy for the fictional Sample Vapor Co. demo store.">
4
+ <link rel="canonical" href="https://sample-store.example/pages/shipping-returns">
5
+ <meta name="robots" content="noindex,nofollow">
6
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/page.css">
7
+ </head>
8
+ <main id="main">
9
+ <section class="page-hero section--tight">
10
+ <div class="container">
11
+ <nav class="crumbs" aria-label="Breadcrumb"><a href="/">Home</a> / Shipping &amp; Returns</nav>
12
+ <span class="eyebrow">Sample content</span>
13
+ <h1>Shipping &amp; Returns</h1>
14
+ </div>
15
+ </section>
16
+ <section class="section">
17
+ <div class="container prose">
18
+ <p><em>Sample Vapor Co. is a fictional demo store. The text below is neutral placeholder copy, not a real shipping policy.</em></p>
19
+ <h2>Shipping</h2>
20
+ <p>Age-restricted products require an adult signature (21+) on delivery. Describe your real shipping methods and timelines here.</p>
21
+ <h2>Returns</h2>
22
+ <p>Describe your return policy here. Consumable nicotine products are commonly non-returnable — set your own terms.</p>
23
+ </div>
24
+ </section>
25
+ </main>
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 48" width="240" height="48" role="img" aria-label="Sample Vapor Co.">
2
+ <rect width="240" height="48" rx="8" fill="#1b3b64"/>
3
+ <circle cx="26" cy="24" r="10" fill="none" stroke="#e0a83a" stroke-width="3"/>
4
+ <path d="M26 18v12M20 24h12" stroke="#e0a83a" stroke-width="3" stroke-linecap="round"/>
5
+ <text x="48" y="30" font-family="Montserrat, Inter, system-ui, sans-serif" font-size="18" font-weight="700" fill="#ffffff">Sample Vapor Co.</text>
6
+ </svg>
@@ -0,0 +1,120 @@
1
+ /* ============================================================================
2
+ Sample Vapor Co. — home page (fictional demo store, above-the-fold).
3
+ Section order + chrome mirror the live Shopify theme. Shared base in
4
+ themes/sample.css + pages/mkt.css.
5
+ ========================================================================== */
6
+
7
+ /* --- Hero: TWO-UP (banner + promo tile), no overlay text ------------------ */
8
+ .hero2 { padding-block: clamp(16px, 2vw, 24px); }
9
+ .hero2 .container { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
10
+ .hero2 a { display: block; border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-sm); }
11
+ .hero2 img { width: 100%; height: clamp(260px, 34vw, 420px); object-fit: cover; }
12
+ .hero2 .promo-tile { background: linear-gradient(135deg, #0b1020, var(--navy-ink)); }
13
+
14
+ /* --- 3 promo cards -------------------------------------------------------- */
15
+ .promos { padding-block: clamp(12px, 2vw, 20px); }
16
+ .promos .container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
17
+ .promo-card {
18
+ position: relative; min-height: 240px; border-radius: var(--radius-lg); overflow: hidden;
19
+ display: flex; flex-direction: column; justify-content: flex-start; padding: 28px; color: #fff;
20
+ }
21
+ .promo-card--blue { background: linear-gradient(160deg, #1466ff, var(--navy)); }
22
+ .promo-card--red { background: linear-gradient(160deg, #ff3b46, var(--sale)); }
23
+ .promo-card--img { background: #10131c; padding: 0; }
24
+ .promo-card--img img { width: 100%; height: 100%; object-fit: cover; }
25
+ .promo-card h3 { color: #fff; font-size: 1.7rem; }
26
+ .promo-card p { color: rgba(255,255,255,.88); margin-top: 4px; font-weight: 500; }
27
+ .promo-card .link { margin-top: 14px; font-family: var(--font-display); font-weight: 700; color: #fff; }
28
+ .promo-card .link::after { content: " ›"; }
29
+
30
+ /* --- Categories (icon cards, light-blue) ---------------------------------- */
31
+ .cats { padding-block: clamp(2rem, 4vw, 3.5rem); }
32
+ .cats h2 { text-align: center; margin-bottom: clamp(1.5rem, 3vw, 2.5rem); }
33
+ .cats .grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: clamp(12px, 1.6vw, 20px); }
34
+ .cat-icon-card {
35
+ display: flex; flex-direction: column; align-items: center; gap: 16px;
36
+ background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
37
+ padding: 26px 16px; text-align: center; transition: transform .14s, box-shadow .14s;
38
+ }
39
+ .cat-icon-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
40
+ .cat-icon-card .ico {
41
+ width: 92px; height: 92px; border-radius: 18px; background: var(--surface-2);
42
+ display: grid; place-items: center;
43
+ }
44
+ .cat-icon-card .ico svg { width: 52px; height: 52px; stroke: var(--navy); fill: none; stroke-width: 1.6; }
45
+ .cat-icon-card span { font-family: var(--font-display); font-weight: 700; color: var(--ink); }
46
+
47
+ /* --- Responsive ----------------------------------------------------------- */
48
+ @media (max-width: 900px) {
49
+ .hero2 .container { grid-template-columns: 1fr; }
50
+ .hero2 .promo-tile { display: none; }
51
+ .promos .container { grid-template-columns: 1fr; }
52
+ .cats .grid { grid-template-columns: repeat(3, 1fr); }
53
+ }
54
+ @media (max-width: 560px) {
55
+ .cats .grid { grid-template-columns: repeat(2, 1fr); }
56
+ }
57
+
58
+ /* --- Footer newsletter ---------------------------------------------------- */
59
+ .footer-news { display: flex; gap: 8px; margin-top: 12px; max-width: 320px; }
60
+ .footer-news input { flex: 1; border: 1px solid rgba(255,255,255,.3); background: transparent; color: #fff; border-radius: 999px; padding: 10px 16px; font-size: .9rem; }
61
+ .footer-news input::placeholder { color: #a9b8e6; }
62
+ .footer-news button { background: #fff; color: var(--navy-ink); border: 0; border-radius: 999px; width: 42px; font-size: 1.1rem; font-weight: 700; cursor: pointer; }
63
+ .footer-col > p { color: #a9b8e6; font-size: .88rem; margin-top: 4px; }
64
+
65
+ /* --- Payment badges ------------------------------------------------------- */
66
+ .pay-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
67
+ .pay-badges span { background: #fff; color: var(--navy-ink); font-size: .62rem; font-weight: 800; letter-spacing: .03em; padding: 6px 9px; border-radius: 5px; }
68
+
69
+ /* --- Product carousel (real catalog snapshot) ----------------------------- */
70
+ .shelf .sec-head { margin-bottom: 1.25rem; }
71
+ .pcarousel {
72
+ display: grid; grid-auto-flow: column; grid-auto-columns: minmax(200px, 1fr);
73
+ gap: clamp(12px, 1.6vw, 20px); overflow-x: auto; scroll-snap-type: x mandatory;
74
+ padding-bottom: 12px; scrollbar-width: thin;
75
+ }
76
+ .pcard { scroll-snap-align: start; }
77
+ .pcard a { display: block; background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; transition: transform .14s, box-shadow .14s, border-color .14s; }
78
+ .pcard a:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--surface-2); }
79
+ .pcard__media { aspect-ratio: 1/1; background: var(--surface); display: grid; place-items: center; }
80
+ .pcard__media img { width: 100%; height: 100%; object-fit: contain; padding: 10px; }
81
+ .pcard__vendor { font-family: var(--font-display); font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); padding: 12px 14px 0; }
82
+ .pcard__title { font-family: var(--font-body); font-size: .9rem; font-weight: 600; line-height: 1.3; color: var(--ink); padding: 2px 14px 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
83
+ .pcard__price { font-family: var(--font-display); font-weight: 700; color: var(--navy); padding: 6px 14px 14px; }
84
+ .pcard__was { color: var(--muted); font-weight: 500; text-decoration: line-through; margin-right: 4px; }
85
+
86
+ /* --- Staff's Pick spotlight ----------------------------------------------- */
87
+ .spotlight { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 4vw, 48px); align-items: center; background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
88
+ .spotlight:hover { box-shadow: var(--shadow-md); }
89
+ .spotlight__media { aspect-ratio: 4/3; background: var(--surface); display: grid; place-items: center; }
90
+ .spotlight__media img { width: 100%; height: 100%; object-fit: contain; padding: 24px; }
91
+ .spotlight__body { padding: clamp(20px, 4vw, 40px) clamp(20px, 4vw, 40px) clamp(20px, 4vw, 40px) 0; }
92
+ .spotlight__body h3 { font-size: clamp(1.3rem, 2.6vw, 1.9rem); margin: 4px 0 10px; }
93
+ .spotlight__price { font-family: var(--font-display); font-weight: 700; font-size: 1.3rem; color: var(--navy); margin-bottom: 1.2rem; }
94
+
95
+ /* --- Shop by Brands grid -------------------------------------------------- */
96
+ .brandgrid { display: grid; grid-template-columns: repeat(6, 1fr); gap: clamp(10px, 1.4vw, 16px); }
97
+ .brandtile { display: grid; place-items: center; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); min-height: 96px; padding: 14px; transition: transform .14s, box-shadow .14s, border-color .14s; }
98
+ .brandtile:hover { transform: translateY(-3px); box-shadow: var(--shadow-sm); border-color: var(--surface-2); }
99
+ .brandtile img { max-height: 56px; max-width: 100%; object-fit: contain; }
100
+ /* Text brand tile (used when a brand has no logo asset) — same tile chrome, name centered. */
101
+ .brandtile--text { font-family: var(--font-display); font-weight: 700; color: var(--navy); text-align: center; font-size: .95rem; letter-spacing: -.01em; }
102
+
103
+ /* --- Blog cards ----------------------------------------------------------- */
104
+ .bloggrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 2vw, 24px); }
105
+ .blogcard { display: flex; flex-direction: column; background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; transition: transform .14s, box-shadow .14s; }
106
+ .blogcard:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
107
+ .blogcard__media { aspect-ratio: 16/9; background: linear-gradient(135deg, var(--surface-2), var(--surface)); }
108
+ .blogcard h3 { font-size: 1.02rem; line-height: 1.35; padding: 16px 18px 0; color: var(--ink); }
109
+ .blogcard .link-more { padding: 10px 18px 18px; }
110
+
111
+ .center { text-align: center; }
112
+
113
+ @media (max-width: 900px) {
114
+ .spotlight { grid-template-columns: 1fr; }
115
+ .spotlight__media { aspect-ratio: 16/10; }
116
+ .spotlight__body { padding: 0 24px 28px; }
117
+ .brandgrid { grid-template-columns: repeat(4, 1fr); }
118
+ .bloggrid { grid-template-columns: 1fr; }
119
+ }
120
+ @media (max-width: 560px) { .brandgrid { grid-template-columns: repeat(3, 1fr); } }
@@ -0,0 +1,185 @@
1
+ /* ============================================================================
2
+ Sample Vapor Co. — shared marketing component layer (mkt.css)
3
+ Reused across home + all marketing pages: layout, buttons, header/nav,
4
+ footer, section headers, tile + category grids, compliance strip.
5
+ Depends on tokens from themes/sample.css.
6
+ ========================================================================== */
7
+
8
+ /* --- Layout --------------------------------------------------------------- */
9
+ .container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: clamp(16px, 4vw, 40px); }
10
+ .section { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
11
+ .section--tight { padding-block: clamp(1.75rem, 4vw, 3rem); }
12
+ .section--surface { background: var(--surface); }
13
+
14
+ /* --- Buttons -------------------------------------------------------------- */
15
+ .btn {
16
+ display: inline-flex; align-items: center; justify-content: center; gap: .5em;
17
+ font-family: var(--font-display); font-weight: 600; font-size: .95rem;
18
+ padding: .8em 1.6em; border-radius: 999px; border: 2px solid transparent;
19
+ transition: transform .12s ease, background .15s ease, color .15s ease, box-shadow .15s ease;
20
+ }
21
+ .btn:hover { transform: translateY(-1px); }
22
+ .btn-primary { background: var(--navy); color: #fff; }
23
+ .btn-primary:hover { background: var(--navy-dark); box-shadow: var(--shadow-md); }
24
+ .btn-line { border-color: #fff; color: #fff; }
25
+ .btn-line:hover { background: #fff; color: var(--navy); }
26
+ .btn-ghost { border-color: var(--border); color: var(--navy); background: #fff; }
27
+ .btn-ghost:hover { border-color: var(--navy); }
28
+
29
+ /* --- Announcement bar ----------------------------------------------------- */
30
+ .announce {
31
+ background: var(--navy); color: #fff; text-align: center;
32
+ font-size: .85rem; font-weight: 600; letter-spacing: .01em; padding: 9px 16px;
33
+ }
34
+
35
+ /* --- Top utility bar (shared chrome: FDA warning + marquee + utility nav) -- */
36
+ .topbar-warning {
37
+ background: #fff; border-bottom: 1px solid var(--border);
38
+ text-align: center; padding: 10px 16px;
39
+ }
40
+ .topbar-warning p { color: var(--ink); font-weight: 700; font-size: .82rem; letter-spacing: .01em; }
41
+
42
+ .announce-marquee { background: var(--sale); color: #fff; overflow: hidden; }
43
+ .announce-marquee__track {
44
+ display: flex; gap: 3rem; white-space: nowrap; width: max-content;
45
+ padding: 8px 0; font-size: .82rem; font-weight: 600;
46
+ animation: bdv-marquee 22s linear infinite;
47
+ }
48
+ .announce-marquee__track span { padding-inline: 1.5rem; }
49
+ @keyframes bdv-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
50
+ @media (prefers-reduced-motion: reduce) {
51
+ .announce-marquee__track { animation: none; justify-content: center; width: 100%; }
52
+ }
53
+
54
+ .utility-nav { border-bottom: 1px solid var(--border); background: #fff; }
55
+ .utility-nav .container { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-block: 8px; }
56
+ .utility-nav ul { display: flex; gap: 20px; }
57
+ .utility-nav a { font-size: .82rem; font-weight: 600; color: var(--ink); }
58
+ .utility-nav a:hover { color: var(--navy); }
59
+ .utility-nav__right { display: flex; align-items: center; gap: 16px; }
60
+ .utility-nav__right .sel { font-size: .8rem; color: var(--muted); }
61
+ .utility-nav__right .social { display: flex; gap: 12px; }
62
+ .utility-nav__right .social a svg { width: 16px; height: 16px; fill: var(--ink); }
63
+
64
+ /* --- Header / nav --------------------------------------------------------- */
65
+ .site-header { position: sticky; top: 0; z-index: 100; background: #fff; border-bottom: 1px solid var(--border); }
66
+ .header-row { display: flex; align-items: center; gap: clamp(12px, 2.5vw, 32px); padding-block: 14px; }
67
+ .brand img { height: 46px; width: auto; }
68
+ .primary-nav { flex: 1; }
69
+ .primary-nav > ul { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(10px, 1.6vw, 22px); }
70
+ .primary-nav a { font-weight: 600; font-size: .92rem; color: var(--ink); padding: 6px 2px; border-bottom: 2px solid transparent; transition: color .15s, border-color .15s; }
71
+ .primary-nav a:hover { color: var(--navy); border-color: var(--navy); }
72
+ .primary-nav a.is-flag { color: var(--sale); }
73
+ .header-actions { display: flex; align-items: center; gap: 16px; }
74
+ .header-actions a { display: inline-flex; color: var(--ink); }
75
+ .header-actions a:hover { color: var(--navy); }
76
+ .header-actions svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.8; }
77
+ .hamburger { display: none; flex-direction: column; gap: 5px; background: none; border: 0; padding: 6px; }
78
+ .hamburger span { width: 26px; height: 2px; background: var(--ink); border-radius: 2px; }
79
+
80
+ /* header search pill + account/cart badge (shared chrome) */
81
+ .site-header .header-row { gap: clamp(12px, 3vw, 40px); }
82
+ .header-search { flex: 1; max-width: 560px; position: relative; }
83
+ .header-search input {
84
+ width: 100%; border: 1px solid var(--border); border-radius: 999px;
85
+ padding: 11px 46px 11px 18px; font-size: .9rem; background: #fff; color: var(--ink);
86
+ }
87
+ .header-search input::placeholder { color: var(--muted); }
88
+ .header-search button { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); background: none; border: 0; padding: 6px; }
89
+ .header-search svg { width: 20px; height: 20px; stroke: var(--navy); fill: none; stroke-width: 2; }
90
+ .header-actions .cart { position: relative; }
91
+ .header-actions .cart .count {
92
+ position: absolute; top: -6px; right: -8px; background: var(--navy); color: #fff;
93
+ font-size: .62rem; font-weight: 700; min-width: 16px; height: 16px; border-radius: 999px;
94
+ display: grid; place-items: center; padding: 0 4px;
95
+ }
96
+
97
+ /* primary nav: NEW badge + dropdown carets + second-row nav bar (shared chrome) */
98
+ .primary-nav a .new-badge {
99
+ display: inline-block; background: var(--sale); color: #fff; font-size: .58rem; font-weight: 800;
100
+ letter-spacing: .06em; padding: 2px 5px; border-radius: 4px; margin-left: 6px; vertical-align: middle;
101
+ }
102
+ .primary-nav a .caret { font-size: .7em; margin-left: 4px; opacity: .7; }
103
+ .primary-nav-bar { border-top: 1px solid var(--border); background: #fff; }
104
+ .primary-nav-bar .container { padding-block: 4px; }
105
+ .primary-nav-bar .primary-nav > ul { gap: clamp(10px, 1.4vw, 20px); }
106
+ .primary-nav-bar .primary-nav a { padding-block: 10px; }
107
+
108
+ /* --- Section headers ------------------------------------------------------ */
109
+ .sec-head { text-align: center; max-width: 62ch; margin: 0 auto clamp(1.5rem, 3vw, 2.5rem); }
110
+ .sec-head .eyebrow { display: inline-block; font-family: var(--font-display); font-weight: 700; font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--navy); margin-bottom: .55rem; }
111
+ .sec-head p { margin-top: .7rem; font-size: 1.02rem; }
112
+ .sec-head--between { display: flex; align-items: baseline; justify-content: space-between; text-align: left; max-width: none; gap: 16px; }
113
+ .link-more { font-family: var(--font-display); font-weight: 600; color: var(--navy); white-space: nowrap; }
114
+ .link-more:hover { text-decoration: underline; }
115
+
116
+ /* --- Brand / category tile grid ------------------------------------------- */
117
+ .tile-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(12px, 1.6vw, 20px); }
118
+ .tile {
119
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
120
+ background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
121
+ padding: 26px 18px; min-height: 148px; text-align: center; transition: transform .14s, box-shadow .14s, border-color .14s;
122
+ }
123
+ .tile:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--surface-2); }
124
+ .tile img { max-height: 66px; width: auto; object-fit: contain; }
125
+ .tile span { font-family: var(--font-display); font-weight: 600; font-size: .95rem; color: var(--ink); }
126
+
127
+ /* --- Category cards (shop by category) ------------------------------------ */
128
+ .cat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 2vw, 24px); }
129
+ .cat-card {
130
+ position: relative; display: flex; align-items: flex-end; overflow: hidden;
131
+ min-height: 190px; border-radius: var(--radius-lg); color: #fff;
132
+ background: linear-gradient(135deg, var(--navy), var(--navy-ink));
133
+ padding: 22px; transition: transform .14s, box-shadow .14s;
134
+ }
135
+ .cat-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
136
+ .cat-card::after { content: "→"; position: absolute; top: 20px; right: 22px; font-size: 1.3rem; opacity: .85; }
137
+ .cat-card h3 { color: #fff; font-size: 1.35rem; }
138
+ .cat-card p { color: rgba(255, 255, 255, .82); margin-top: 4px; font-size: .9rem; }
139
+
140
+ /* --- Compliance strip (source-copied legal text; not fabricated) ---------- */
141
+ .compliance-strip { background: var(--surface-2); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
142
+ .compliance-strip .container { padding-block: 14px; }
143
+ .compliance-strip p { color: var(--navy-ink); font-size: .82rem; font-weight: 600; text-align: center; letter-spacing: .01em; }
144
+
145
+ /* --- Footer --------------------------------------------------------------- */
146
+ .site-footer { background: var(--navy-ink); color: #dbe4fb; padding-block: clamp(2.5rem, 5vw, 4rem) 1.5rem; }
147
+ .footer-grid { display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: clamp(24px, 4vw, 56px); }
148
+ .footer-brand img { height: 40px; filter: brightness(0) invert(1); opacity: .95; }
149
+ .footer-brand p { color: #a9b8e6; margin-top: 14px; font-size: .9rem; max-width: 34ch; }
150
+ .footer-col h4 { color: #fff; font-size: .82rem; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 14px; }
151
+ .footer-col ul { display: flex; flex-direction: column; gap: 9px; }
152
+ .footer-col a { color: #c3cff0; font-size: .92rem; }
153
+ .footer-col a:hover { color: #fff; text-decoration: underline; }
154
+ .footer-warning { margin-top: 34px; padding: 16px 18px; border: 1px solid rgba(255, 255, 255, .22); border-radius: var(--radius); }
155
+ .footer-warning strong { color: #fff; }
156
+ .footer-warning p { color: #c3cff0; font-size: .82rem; }
157
+ .footer-bottom { margin-top: 28px; padding-top: 18px; border-top: 1px solid rgba(255, 255, 255, .16); display: flex; flex-wrap: wrap; gap: 10px 24px; justify-content: space-between; align-items: center; }
158
+ .footer-bottom p, .footer-bottom a { color: #93a4d6; font-size: .82rem; }
159
+
160
+ /* --- Reveal-on-scroll ----------------------------------------------------- */
161
+ .reveal { opacity: 0; transform: translateY(18px); transition: opacity .5s ease, transform .5s ease; }
162
+ .reveal.in { opacity: 1; transform: none; }
163
+
164
+ /* --- Responsive ----------------------------------------------------------- */
165
+ @media (max-width: 900px) {
166
+ .header-search { order: 3; flex-basis: 100%; max-width: none; }
167
+ .utility-nav ul { gap: 12px; }
168
+ }
169
+ @media (max-width: 1000px) {
170
+ .tile-grid { grid-template-columns: repeat(3, 1fr); }
171
+ }
172
+ @media (max-width: 860px) {
173
+ .primary-nav { position: fixed; inset: 0 0 0 auto; width: min(80vw, 320px); background: #fff; box-shadow: var(--shadow-lg); transform: translateX(100%); transition: transform .25s ease; padding: 80px 24px 24px; overflow-y: auto; }
174
+ .primary-nav.open { transform: none; }
175
+ .primary-nav > ul { flex-direction: column; align-items: stretch; gap: 4px; }
176
+ .primary-nav a { display: block; padding: 12px 4px; border-bottom: 1px solid var(--border); }
177
+ .hamburger { display: flex; }
178
+ .cat-grid { grid-template-columns: 1fr 1fr; }
179
+ .footer-grid { grid-template-columns: 1fr 1fr; }
180
+ }
181
+ @media (max-width: 560px) {
182
+ .tile-grid { grid-template-columns: repeat(2, 1fr); }
183
+ .cat-grid { grid-template-columns: 1fr; }
184
+ .footer-grid { grid-template-columns: 1fr; }
185
+ }
@@ -0,0 +1,155 @@
1
+ /* ============================================================================
2
+ Sample Vapor Co. — shared marketing subpage styles (page.css)
3
+ Page hero + prose + contact cards. Used by pages-html/**. Depends on tokens
4
+ from themes/sample.css and components from pages/mkt.css.
5
+ ========================================================================== */
6
+
7
+ /* --- Page hero ------------------------------------------------------------ */
8
+ .page-hero { background: linear-gradient(135deg, var(--navy), var(--navy-ink)); color: #fff; }
9
+ .page-hero .container { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
10
+ .page-hero .eyebrow { display: inline-block; font-family: var(--font-display); font-weight: 700; font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; color: #afc2ff; margin-bottom: .7rem; }
11
+ .page-hero h1 { color: #fff; max-width: 22ch; }
12
+ .page-hero p { color: #dce5ff; font-size: 1.08rem; margin-top: .9rem; max-width: 60ch; }
13
+ .crumbs { font-size: .82rem; color: #a9b8e6; margin-bottom: 1rem; }
14
+ .crumbs a:hover { color: #fff; text-decoration: underline; }
15
+
16
+ /* --- Prose ---------------------------------------------------------------- */
17
+ .prose { max-width: 70ch; margin-inline: auto; }
18
+ .prose h2 { margin-top: 2.2rem; margin-bottom: .6rem; }
19
+ .prose h3 { margin-top: 1.6rem; margin-bottom: .4rem; }
20
+ .prose p { margin-bottom: 1rem; color: var(--ink); }
21
+ .prose p.lead { font-size: 1.15rem; color: var(--muted); }
22
+ .prose ul.bullets { margin: 0 0 1rem; padding-left: 1.1rem; list-style: disc; }
23
+ .prose ul.bullets li { margin-bottom: .4rem; color: var(--ink); }
24
+ .prose a { color: var(--navy); font-weight: 600; }
25
+ .prose a:hover { text-decoration: underline; }
26
+
27
+ /* --- Value cards (about-page "what we're about" grid) --------------------- */
28
+ .value-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 2vw, 22px); margin-top: 1.6rem; }
29
+ .value-card { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 24px; background: #fff; }
30
+ .value-card h3 { color: var(--navy); font-size: 1.05rem; margin-bottom: .55rem; }
31
+ .value-card p { color: var(--ink); font-size: .96rem; }
32
+ @media (max-width: 860px) { .value-grid { grid-template-columns: 1fr; } }
33
+
34
+ /* --- Contact cards -------------------------------------------------------- */
35
+ .contact-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 2vw, 22px); margin-top: 1.5rem; }
36
+ .contact-card { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 24px; background: #fff; }
37
+ .contact-card h3 { color: var(--navy); font-size: 1.05rem; margin-bottom: .5rem; }
38
+ .contact-card a { color: var(--ink); font-weight: 600; }
39
+ .contact-card a:hover { color: var(--navy); }
40
+ .contact-card p { font-size: .92rem; }
41
+
42
+ /* --- Brand chips ---------------------------------------------------------- */
43
+ .brand-chips { display: flex; flex-wrap: wrap; gap: 10px; margin: 1rem 0 1.5rem; }
44
+ .brand-chips a { border: 1px solid var(--border); border-radius: 999px; padding: 8px 16px; font-size: .88rem; font-weight: 600; color: var(--navy); background: var(--surface); }
45
+ .brand-chips a:hover { background: var(--surface-2); }
46
+
47
+ @media (max-width: 720px) {
48
+ .contact-grid { grid-template-columns: 1fr; }
49
+ }
50
+
51
+ /* ============================================================================
52
+ About page — hero overlay + alternating media/copy splits + pickers
53
+ (clones sample-store.example/pages/about-us section-for-section)
54
+ ========================================================================== */
55
+
56
+ /* --- Full-bleed hero with image + left-aligned overlay copy --------------- */
57
+ .hero-overlay { position: relative; overflow: hidden; background: var(--navy-ink); }
58
+ .hero-overlay > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
59
+ .hero-overlay::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0,10,40,.62) 0%, rgba(0,10,40,.34) 45%, rgba(0,10,40,.08) 100%); }
60
+ .hero-overlay__inner { position: relative; z-index: 1; padding-block: clamp(3rem, 9vw, 7rem); }
61
+ .hero-overlay .crumbs { color: #cdd8f5; }
62
+ .hero-overlay .crumbs a { color: #eaf0ff; }
63
+ .hero-overlay h1 { color: #fff; max-width: 16ch; text-shadow: 0 2px 18px rgba(0,10,40,.4); }
64
+ .hero-overlay p { color: #eaf0ff; font-size: clamp(1rem, 1.6vw, 1.2rem); margin-top: 1rem; max-width: 46ch; }
65
+
66
+ /* --- Media / copy split (Our Story, Our Mission, Service) ------------------ */
67
+ .split .container { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.75rem, 5vw, 4.5rem); align-items: center; }
68
+ .split--rev .container { direction: rtl; }
69
+ .split--rev .split__copy, .split--rev .split__media { direction: ltr; }
70
+ .split__copy h2 { margin-bottom: 1rem; }
71
+ .split__copy p { color: var(--ink); margin-bottom: 1rem; max-width: 52ch; }
72
+ .split__copy .btn { margin-top: .4rem; }
73
+ .split__media img { width: 100%; border-radius: var(--radius-lg); box-shadow: var(--shadow-md); object-fit: cover; }
74
+ .split--center .split__copy { text-align: center; }
75
+ .split--center .split__copy p { margin-inline: auto; }
76
+ .split--center .split__copy .btn { display: inline-flex; }
77
+
78
+ /* --- Trusted Brands band (centered) --------------------------------------- */
79
+ .brands-band .brand-chips { justify-content: center; max-width: 780px; margin-inline: auto; }
80
+
81
+ /* --- Centered CTA band (Service — decorative image absent from source) ----- */
82
+ .cta-band { text-align: center; }
83
+ .cta-band .container { max-width: 60ch; }
84
+ .cta-band p { color: var(--ink); margin-top: 1rem; }
85
+ .cta-band .btn { margin-top: 1.5rem; }
86
+
87
+ /* --- Category picker (Find Your Fav) -------------------------------------- */
88
+ .pick-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(12px, 1.6vw, 20px); }
89
+ .pick-card {
90
+ display: flex; flex-direction: column; justify-content: space-between; gap: 12px;
91
+ min-height: 132px; padding: 22px 20px; background: #fff;
92
+ border: 1px solid var(--border); border-radius: var(--radius-lg);
93
+ transition: transform .14s, box-shadow .14s, border-color .14s;
94
+ }
95
+ .pick-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--surface-2); }
96
+ .pick-card h3 { color: var(--navy); font-size: 1.15rem; }
97
+ .pick-card span { font-family: var(--font-display); font-weight: 600; font-size: .9rem; color: var(--navy); }
98
+ .pick-card span::after { content: " →"; }
99
+
100
+ /* ============================================================================
101
+ Contact page — icon info band + form (clones /pages/contact-us)
102
+ ========================================================================== */
103
+ .page-hero--center { text-align: center; background: none; color: inherit; }
104
+ .page-hero--center h1 { color: var(--ink); max-width: none; margin-inline: auto; }
105
+ .page-hero--center p { color: var(--muted); margin-inline: auto; }
106
+
107
+ .contact-band { background: var(--navy); border-radius: var(--radius-lg); color: #fff; }
108
+ .contact-band .contact-band__inner { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 3vw, 32px); padding: clamp(28px, 5vw, 48px); text-align: center; }
109
+ .contact-band__item svg { width: 40px; height: 40px; margin: 0 auto 16px; stroke: #fff; fill: none; stroke-width: 1.6; }
110
+ .contact-band__item a, .contact-band__item span { color: #fff; font-size: 1.02rem; font-weight: 500; }
111
+ .contact-band__item a:hover { text-decoration: underline; }
112
+
113
+ .contact-form { margin-top: clamp(1.75rem, 4vw, 2.75rem); display: grid; gap: 16px; }
114
+ .contact-form .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
115
+ .contact-form input, .contact-form textarea {
116
+ width: 100%; font: inherit; color: var(--ink); background: var(--surface);
117
+ border: 1px solid var(--border); border-radius: var(--radius); padding: 15px 18px;
118
+ }
119
+ .contact-form textarea { min-height: 180px; resize: vertical; }
120
+ .contact-form input::placeholder, .contact-form textarea::placeholder { color: var(--muted); }
121
+ .contact-form input:focus, .contact-form textarea:focus { outline: none; border-color: var(--navy); box-shadow: 0 0 0 3px rgba(0,48,160,.14); }
122
+ .contact-form .btn-primary { width: 100%; padding-block: 1em; font-size: 1rem; }
123
+
124
+ /* ============================================================================
125
+ Blog index — post-card grid (clones /blogs/news "Latest News")
126
+ ========================================================================== */
127
+ .blog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2.4vw, 28px); }
128
+ .blog-card {
129
+ display: flex; flex-direction: column; gap: 10px; padding: 24px;
130
+ background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
131
+ transition: transform .14s, box-shadow .14s, border-color .14s;
132
+ }
133
+ .blog-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--surface-2); }
134
+ .blog-card__date { font-size: .8rem; color: var(--muted); font-weight: 600; letter-spacing: .02em; }
135
+ .blog-card h2 { font-size: 1.18rem; line-height: 1.25; color: var(--ink); }
136
+ .blog-card h2 a:hover { color: var(--navy); }
137
+ .blog-card__more { margin-top: auto; font-family: var(--font-display); font-weight: 600; color: var(--navy); font-size: .9rem; }
138
+ .blog-card__more:hover { text-decoration: underline; }
139
+ .blog-card__more::after { content: " →"; }
140
+
141
+ /* --- Responsive ----------------------------------------------------------- */
142
+ @media (max-width: 900px) {
143
+ .pick-grid { grid-template-columns: repeat(2, 1fr); }
144
+ .blog-grid { grid-template-columns: repeat(2, 1fr); }
145
+ }
146
+ @media (max-width: 760px) {
147
+ .split .container, .split--rev .container { grid-template-columns: 1fr; direction: ltr; }
148
+ .split--rev .split__media { order: -1; }
149
+ .contact-band .contact-band__inner { grid-template-columns: 1fr; }
150
+ .contact-form .row2 { grid-template-columns: 1fr; }
151
+ }
152
+ @media (max-width: 560px) {
153
+ .pick-grid { grid-template-columns: 1fr 1fr; }
154
+ .blog-grid { grid-template-columns: 1fr; }
155
+ }