Files
Physilinks/index.html
2025-12-12 09:34:49 +01:00

346 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Physilinks</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;700&display=swap"
rel="stylesheet"
/>
<style>
:root {
--bg: #0f172a;
--panel: #111827;
--accent: #22d3ee;
--text: #e2e8f0;
--muted: #94a3b8;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family:
"Manrope",
system-ui,
-apple-system,
sans-serif;
background:
radial-gradient(
circle at 25% 20%,
rgba(56, 189, 248, 0.12),
transparent 25%
),
radial-gradient(
circle at 80% 10%,
rgba(167, 139, 250, 0.16),
transparent 30%
),
radial-gradient(
circle at 40% 80%,
rgba(52, 211, 153, 0.12),
transparent 25%
),
var(--bg);
color: var(--text);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.shell {
width: min(1100px, 100%);
background: rgba(17, 24, 39, 0.72);
border: 1px solid rgba(148, 163, 184, 0.1);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
border-radius: 18px;
overflow: hidden;
position: relative;
backdrop-filter: blur(10px);
}
header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
background: rgba(255, 255, 255, 0.02);
border-bottom: 1px solid rgba(148, 163, 184, 0.08);
}
header h1 {
font-size: 20px;
margin: 0;
letter-spacing: 0.5px;
}
header .meta {
display: flex;
gap: 14px;
align-items: center;
font-size: 13px;
color: var(--muted);
}
.pause-btn {
background: rgba(34, 211, 238, 0.14);
color: #67e8f9;
border: 1px solid rgba(34, 211, 238, 0.4);
border-radius: 10px;
padding: 8px 12px;
font-weight: 700;
cursor: pointer;
transition:
transform 120ms ease,
filter 120ms ease;
}
.pause-btn:hover {
filter: brightness(1.05);
transform: translateY(-1px);
}
.pause-btn:active {
transform: translateY(0);
}
header .pill {
padding: 6px 10px;
border-radius: 8px;
background: rgba(34, 211, 238, 0.12);
color: #67e8f9;
border: 1px solid rgba(34, 211, 238, 0.35);
font-weight: 700;
font-size: 12px;
letter-spacing: 0.6px;
text-transform: uppercase;
}
#scene-wrapper {
position: relative;
width: 100%;
height: 680px;
background:
radial-gradient(
circle at 30% 30%,
rgba(255, 255, 255, 0.02),
transparent 40%
),
radial-gradient(
circle at 75% 60%,
rgba(255, 255, 255, 0.02),
transparent 45%
),
#0b1222;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.hud-bar {
display: flex;
gap: 10px;
flex-wrap: wrap;
padding: 12px 16px;
background: rgba(255, 255, 255, 0.03);
border-top: 1px solid rgba(148, 163, 184, 0.08);
}
.hud-bar .card {
background: rgba(255, 255, 255, 0.04);
padding: 10px 12px;
border: 1px solid rgba(148, 163, 184, 0.14);
border-radius: 10px;
font-size: 13px;
color: var(--muted);
display: flex;
align-items: center;
gap: 6px;
}
.hud-bar .card strong {
color: var(--text);
font-size: 13px;
}
.legend {
position: absolute;
top: 16px;
right: 16px;
background: rgba(255, 255, 255, 0.04);
padding: 10px 12px;
border: 1px solid rgba(148, 163, 184, 0.14);
border-radius: 10px;
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
pointer-events: none;
}
.legend span {
width: 18px;
height: 18px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.12);
display: inline-block;
}
.pause-overlay {
position: absolute;
top: 12px;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(148, 163, 184, 0.3);
color: #e2e8f0;
padding: 8px 14px;
border-radius: 12px;
font-weight: 800;
letter-spacing: 0.5px;
opacity: 0;
pointer-events: none;
transition: opacity 160ms ease;
}
.pause-overlay.visible {
opacity: 1;
}
.game-over {
position: absolute;
inset: 0;
background: rgba(10, 13, 25, 0.72);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
opacity: 0;
transition: opacity 200ms ease;
}
.game-over.visible {
pointer-events: auto;
opacity: 1;
}
.game-over__card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: 14px;
padding: 20px 24px;
text-align: center;
min-width: 240px;
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.35);
}
.game-over__card .title {
font-size: 22px;
font-weight: 700;
margin-bottom: 10px;
}
.game-over__card .score-line {
font-size: 14px;
color: var(--muted);
margin-bottom: 14px;
}
.game-over__card button {
background: linear-gradient(135deg, #22d3ee, #0ea5e9);
border: none;
color: #0b1222;
font-weight: 700;
padding: 10px 16px;
border-radius: 10px;
cursor: pointer;
font-size: 14px;
}
.game-over__card button:hover {
filter: brightness(1.08);
}
.floating-score {
position: absolute;
color: #e0f2fe;
font-weight: 800;
font-size: 18px;
pointer-events: none;
text-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
animation: floatUp 900ms ease-out forwards;
}
@keyframes floatUp {
0% {
opacity: 1;
transform: translate(-50%, 0);
}
60% {
opacity: 0.9;
transform: translate(-50%, -22px);
}
100% {
opacity: 0;
transform: translate(-50%, -38px);
}
}
.instructions {
padding: 14px 18px;
font-size: 14px;
color: var(--muted);
background: rgba(255, 255, 255, 0.03);
border-top: 1px solid rgba(148, 163, 184, 0.08);
line-height: 1.6;
}
@media (max-width: 800px) {
#scene-wrapper {
height: 520px;
}
header h1 {
font-size: 18px;
}
}
</style>
</head>
<body>
<div class="shell">
<header>
<h1>Physilinks</h1>
<div class="meta">
<span class="pill">Physics</span>
<span>Link same-colored balls to clear them.</span>
</div>
<button class="pause-btn" id="pause-btn">Pause</button>
</header>
<div id="scene-wrapper">
<div class="legend" id="palette-legend"></div>
<div class="pause-overlay" id="pause-overlay">Paused</div>
<div class="game-over" id="game-over">
<div class="game-over__card">
<div class="title">Game Over</div>
<div class="score-line">
Final score: <span id="final-score">0</span>
</div>
<button id="restart-btn">Restart</button>
</div>
</div>
</div>
<div class="hud-bar">
<div class="card">
<strong>Spawn</strong> <span id="spawn-rate"></span>
</div>
<div class="card">
<strong>Min link</strong> <span id="min-link"></span>
</div>
<div class="card">
<strong>Active color</strong>
<span id="active-color"></span>
</div>
<div class="card">
<strong>Chain length</strong>
<span id="chain-length">0</span>
</div>
<div class="card">
<strong>Score</strong> <span id="score">0</span>
</div>
</div>
<div class="instructions">
Click or touch a ball to start a chain. Drag through balls of
the same color to link them together. Drag back to the previous
ball to undo the last link. Release to finish: chains of fewer
than the minimum vanish; chains with enough balls clear all
linked balls (score: 10 × length²). If the entry gets blocked
and a new ball cannot drop in, the run ends—restart to try
again. Pause/resume with the Pause button or Escape key.
</div>
</div>
<script src="https://unpkg.com/matter-js@0.19.0/build/matter.min.js"></script>
<script src="main.js"></script>
</body>
</html>