Skip to content

Commit eff95f0

Browse files
committed
Add playground link
1 parent 3a445e7 commit eff95f0

3 files changed

Lines changed: 310 additions & 2 deletions

File tree

index.html

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ <h2>Beyond Static Checklists</h2>
4040
<i class="fab fa-github" aria-hidden="true"></i>
4141
<span>GitHub repo</span>
4242
</a>
43-
<a href="#" class="btn btn-secondary is-disabled" aria-disabled="true" tabindex="-1" title="Coming soon">
43+
<button type="button" class="btn btn-playground" id="playgroundGateTrigger" aria-haspopup="dialog" aria-controls="playgroundGateModal">
4444
<i class="fa-solid fa-laptop-code" aria-hidden="true"></i>
4545
<span>Playground</span>
46-
</a>
46+
</button>
4747
</div>
4848
</div>
4949

@@ -447,6 +447,37 @@ <h3>Checklist Reviewer Toolkit</h3>
447447
</div>
448448
</div>
449449

450+
<!-- Playground gate: explain external VPS-hosted demo before opening a new tab -->
451+
<div id="playgroundGateModal" class="modal playground-gate-modal" aria-hidden="true">
452+
<div class="playground-gate-panel" role="dialog" aria-modal="true" aria-labelledby="playgroundGateTitle" aria-describedby="playgroundGateDesc">
453+
<button type="button" class="close playground-gate-close" data-playground-gate-close aria-label="Close dialog">
454+
<i class="fa-solid fa-xmark" aria-hidden="true"></i>
455+
</button>
456+
457+
<div class="playground-gate-glow" aria-hidden="true"></div>
458+
<div class="playground-gate-header">
459+
<div class="playground-gate-icon-ring" aria-hidden="true">
460+
<span class="playground-gate-icon">
461+
<i class="fa-solid fa-laptop-code"></i>
462+
</span>
463+
</div>
464+
<p class="playground-gate-kicker">Interactive demo</p>
465+
<h2 id="playgroundGateTitle" class="playground-gate-title">Open the live playground?</h2>
466+
<p id="playgroundGateDesc" class="playground-gate-lead">
467+
Choosing Continue opens our read-only toolkit demo in a new browser tab. The playground currently runs on a small dedicated server (personal VPS)—there is nothing to install, and your visit is an ordinary website visit like any other external link.
468+
</p>
469+
</div>
470+
471+
<div class="playground-gate-actions">
472+
<button type="button" class="btn btn-tertiary playground-gate-cancel" data-playground-gate-close>Stay on this page</button>
473+
<button type="button" class="btn btn-primary playground-gate-confirm" id="playgroundGateConfirm">
474+
<i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i>
475+
<span>Continue to playground</span>
476+
</button>
477+
</div>
478+
</div>
479+
</div>
480+
450481
<!-- Info Modal -->
451482
<div id="infoModal" class="modal info-modal">
452483
<div class="modal-content info-content">

script.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,54 @@ document.addEventListener('DOMContentLoaded', () => {
5555

5656
initHeroDemo();
5757
initPosterLightbox();
58+
initPlaygroundGate();
5859
});
5960

61+
const PLAYGROUND_URL = 'https://hemati.xyz/checklist_reviewer_playground';
62+
63+
let playgroundGateLastFocus = null;
64+
65+
function openPlaygroundGateModal() {
66+
const modal = document.getElementById('playgroundGateModal');
67+
const confirmBtn = document.getElementById('playgroundGateConfirm');
68+
if (!modal) return;
69+
playgroundGateLastFocus = document.activeElement;
70+
modal.style.display = 'flex';
71+
modal.setAttribute('aria-hidden', 'false');
72+
document.body.style.overflow = 'hidden';
73+
confirmBtn?.focus();
74+
}
75+
76+
function closePlaygroundGateModal() {
77+
const modal = document.getElementById('playgroundGateModal');
78+
if (!modal) return;
79+
modal.style.display = 'none';
80+
modal.setAttribute('aria-hidden', 'true');
81+
document.body.style.overflow = 'auto';
82+
if (playgroundGateLastFocus && typeof playgroundGateLastFocus.focus === 'function') {
83+
playgroundGateLastFocus.focus();
84+
}
85+
playgroundGateLastFocus = null;
86+
}
87+
88+
function confirmPlaygroundOpen() {
89+
window.open(PLAYGROUND_URL, '_blank', 'noopener,noreferrer');
90+
closePlaygroundGateModal();
91+
}
92+
93+
function initPlaygroundGate() {
94+
const modal = document.getElementById('playgroundGateModal');
95+
const trigger = document.getElementById('playgroundGateTrigger');
96+
const confirmBtn = document.getElementById('playgroundGateConfirm');
97+
if (!modal || !trigger) return;
98+
99+
trigger.addEventListener('click', openPlaygroundGateModal);
100+
confirmBtn?.addEventListener('click', confirmPlaygroundOpen);
101+
modal.querySelectorAll('[data-playground-gate-close]').forEach((el) => {
102+
el.addEventListener('click', closePlaygroundGateModal);
103+
});
104+
}
105+
60106
/**
61107
* Hero mock card: ring cursor on setup only — upload → dropdowns → Run Review → checklist → analysis (no cursor) → loop.
62108
* CTA still skips setup and jumps to review.
@@ -761,18 +807,23 @@ function closeModal() {
761807
window.onclick = function(event) {
762808
const imageModal = document.getElementById("imageModal");
763809
const infoModal = document.getElementById("infoModal");
810+
const playgroundGateModal = document.getElementById("playgroundGateModal");
764811
if (event.target == imageModal) {
765812
closeModal();
766813
}
767814
if (event.target == infoModal) {
768815
closeInfoModal();
769816
}
817+
if (event.target == playgroundGateModal) {
818+
closePlaygroundGateModal();
819+
}
770820
}
771821

772822
// Escape key to close modals
773823
document.addEventListener('keydown', function(event) {
774824
if (event.key === "Escape") {
775825
closeModal();
776826
closeInfoModal();
827+
closePlaygroundGateModal();
777828
}
778829
});

style.css

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ h1, h2, h3, h4 {
9595
display: inline-block;
9696
padding: 0.8rem 2rem;
9797
border-radius: 8px;
98+
font-family: inherit;
99+
font-size: inherit;
98100
font-weight: 600;
99101
transition: all 0.3s ease;
100102
cursor: pointer;
@@ -129,6 +131,34 @@ h1, h2, h3, h4 {
129131
box-shadow: 0 6px 20px rgba(58, 134, 255, 0.6);
130132
}
131133

134+
/* Hero playground CTA — same visual weight as .btn-primary, distinct inviting palette */
135+
.btn-playground {
136+
background: linear-gradient(145deg, #14b8a6 0%, #0d9488 45%, #0f766e 100%);
137+
color: #f0fdfa;
138+
box-shadow:
139+
0 4px 18px rgba(13, 148, 136, 0.42),
140+
inset 0 1px 0 rgba(255, 255, 255, 0.22);
141+
}
142+
143+
.btn-playground:hover {
144+
background: linear-gradient(145deg, #2dd4bf 0%, #14b8a6 50%, #0d9488 100%);
145+
color: #fff;
146+
transform: translateY(-2px);
147+
box-shadow:
148+
0 6px 24px rgba(13, 148, 136, 0.5),
149+
inset 0 1px 0 rgba(255, 255, 255, 0.28);
150+
}
151+
152+
.btn-playground:active {
153+
transform: translateY(0);
154+
box-shadow: 0 2px 12px rgba(13, 148, 136, 0.35);
155+
}
156+
157+
.btn-playground:focus-visible {
158+
outline: 3px solid rgba(45, 212, 191, 0.65);
159+
outline-offset: 3px;
160+
}
161+
132162
.btn-secondary {
133163
background-color: transparent;
134164
color: var(--text-dark);
@@ -346,6 +376,15 @@ h1, h2, h3, h4 {
346376
padding: 0.75rem 1.35rem;
347377
}
348378

379+
/* Same visual width as GitHub: equal flex share (content-only sizing was shrinking “Playground”) */
380+
@media (min-width: 481px) {
381+
.hero .cta-group .btn-primary,
382+
.hero .cta-group .btn-playground {
383+
flex: 1 1 0;
384+
min-width: 12rem;
385+
}
386+
}
387+
349388
/* Hero Visual Graphic (Mock Checklist) */
350389
.hero-visual {
351390
flex: 1;
@@ -2285,6 +2324,193 @@ h1, h2, h3, h4 {
22852324
font-weight: 600;
22862325
}
22872326

2327+
/* Playground gate — external demo disclosure */
2328+
.playground-gate-modal {
2329+
padding: 1.25rem;
2330+
align-items: center;
2331+
justify-content: center;
2332+
background-color: rgba(15, 23, 42, 0.35);
2333+
backdrop-filter: blur(14px);
2334+
-webkit-backdrop-filter: blur(14px);
2335+
}
2336+
2337+
.playground-gate-panel {
2338+
position: relative;
2339+
width: 100%;
2340+
max-width: 26.5rem;
2341+
margin: 0 auto;
2342+
padding: 2.35rem 1.85rem 1.85rem;
2343+
border-radius: 1.35rem;
2344+
background:
2345+
linear-gradient(155deg, rgba(255, 255, 255, 0.97) 0%, rgba(248, 250, 252, 0.98) 45%, rgba(241, 245, 249, 0.99) 100%);
2346+
border: 1px solid rgba(255, 255, 255, 0.9);
2347+
box-shadow:
2348+
0 0 0 1px rgba(58, 134, 255, 0.06),
2349+
0 24px 48px rgba(27, 59, 111, 0.14),
2350+
0 8px 16px rgba(15, 23, 42, 0.06);
2351+
overflow: hidden;
2352+
animation: zoom 0.35s cubic-bezier(0.22, 1, 0.36, 1);
2353+
}
2354+
2355+
.playground-gate-glow {
2356+
position: absolute;
2357+
inset: -45% -30% auto;
2358+
height: 75%;
2359+
background:
2360+
radial-gradient(ellipse 70% 60% at 30% 20%, rgba(58, 134, 255, 0.22) 0%, transparent 55%),
2361+
radial-gradient(ellipse 55% 50% at 85% 35%, rgba(99, 102, 241, 0.14) 0%, transparent 50%);
2362+
pointer-events: none;
2363+
z-index: 0;
2364+
animation: playgroundGateGlow 14s ease-in-out infinite;
2365+
}
2366+
2367+
.playground-gate-modal .playground-gate-close {
2368+
position: absolute;
2369+
top: 0.75rem;
2370+
right: 0.75rem;
2371+
z-index: 3;
2372+
width: 2.45rem;
2373+
height: 2.45rem;
2374+
margin: 0;
2375+
padding: 0;
2376+
font-size: 1rem;
2377+
font-weight: 600;
2378+
color: var(--text-muted);
2379+
background: rgba(241, 245, 249, 0.95);
2380+
border: 1px solid rgba(15, 23, 42, 0.06);
2381+
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
2382+
border-radius: 0.65rem;
2383+
display: inline-flex;
2384+
align-items: center;
2385+
justify-content: center;
2386+
cursor: pointer;
2387+
transition: color 0.2s ease, background 0.2s ease, transform 0.2s ease;
2388+
}
2389+
2390+
.playground-gate-modal .playground-gate-close:hover {
2391+
color: var(--text-dark);
2392+
background: #fff;
2393+
transform: scale(1.04);
2394+
}
2395+
2396+
.playground-gate-header {
2397+
position: relative;
2398+
z-index: 1;
2399+
text-align: center;
2400+
padding: 0 0.25rem;
2401+
}
2402+
2403+
.playground-gate-icon-ring {
2404+
width: 4.25rem;
2405+
height: 4.25rem;
2406+
margin: 0 auto 1.15rem;
2407+
border-radius: 50%;
2408+
padding: 3px;
2409+
background: linear-gradient(135deg, var(--accent) 0%, #6366f1 50%, #22d3ee 100%);
2410+
box-shadow:
2411+
0 12px 28px rgba(58, 134, 255, 0.35),
2412+
inset 0 1px 0 rgba(255, 255, 255, 0.35);
2413+
}
2414+
2415+
.playground-gate-icon {
2416+
display: flex;
2417+
align-items: center;
2418+
justify-content: center;
2419+
width: 100%;
2420+
height: 100%;
2421+
border-radius: 50%;
2422+
background: linear-gradient(180deg, #ffffff 0%, #f1f5f9 100%);
2423+
font-size: 1.65rem;
2424+
color: var(--accent);
2425+
}
2426+
2427+
.playground-gate-kicker {
2428+
margin: 0 0 0.4rem;
2429+
font-size: 0.7rem;
2430+
font-weight: 700;
2431+
letter-spacing: 0.14em;
2432+
text-transform: uppercase;
2433+
color: var(--accent);
2434+
}
2435+
2436+
.playground-gate-title {
2437+
margin: 0 0 0.85rem;
2438+
font-family: var(--font-heading);
2439+
font-size: 1.45rem;
2440+
font-weight: 700;
2441+
letter-spacing: -0.02em;
2442+
line-height: 1.2;
2443+
color: var(--primary);
2444+
}
2445+
2446+
.playground-gate-lead {
2447+
margin: 0;
2448+
font-size: 0.95rem;
2449+
line-height: 1.65;
2450+
color: var(--text-muted);
2451+
text-align: left;
2452+
}
2453+
2454+
.playground-gate-actions {
2455+
position: relative;
2456+
z-index: 1;
2457+
display: flex;
2458+
flex-wrap: wrap;
2459+
gap: 0.65rem;
2460+
margin-top: 1.5rem;
2461+
justify-content: stretch;
2462+
}
2463+
2464+
.playground-gate-actions .btn {
2465+
flex: 1 1 calc(50% - 0.35rem);
2466+
min-width: min(100%, 10rem);
2467+
min-height: 2.85rem;
2468+
display: inline-flex;
2469+
align-items: center;
2470+
justify-content: center;
2471+
gap: 0.45rem;
2472+
padding: 0.75rem 1rem;
2473+
font-size: 0.92rem;
2474+
}
2475+
2476+
.playground-gate-actions .playground-gate-confirm {
2477+
flex: 1 1 100%;
2478+
order: -1;
2479+
box-shadow: 0 6px 20px rgba(58, 134, 255, 0.38);
2480+
}
2481+
2482+
@media (min-width: 420px) {
2483+
.playground-gate-actions .playground-gate-confirm {
2484+
flex: 1 1 auto;
2485+
order: 0;
2486+
}
2487+
2488+
.playground-gate-actions .playground-gate-cancel {
2489+
flex: 1 1 auto;
2490+
}
2491+
}
2492+
2493+
@keyframes playgroundGateGlow {
2494+
0%, 100% {
2495+
transform: translate(0, 0) scale(1);
2496+
opacity: 1;
2497+
}
2498+
50% {
2499+
transform: translate(4%, -3%) scale(1.05);
2500+
opacity: 0.92;
2501+
}
2502+
}
2503+
2504+
@media (prefers-reduced-motion: reduce) {
2505+
.playground-gate-panel {
2506+
animation-duration: 0.01ms;
2507+
}
2508+
2509+
.playground-gate-glow {
2510+
animation: none;
2511+
}
2512+
}
2513+
22882514
@keyframes zoom { from {transform:scale(0.95); opacity:0} to {transform:scale(1); opacity:1} }
22892515

22902516
/* Footer */

0 commit comments

Comments
 (0)