progress
This commit is contained in:
parent
c99af0e299
commit
4be8fcb158
|
|
@ -0,0 +1,854 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Organic Nature Search Hub</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Georgia', serif;
|
||||
background: linear-gradient(135deg, #f5f5dc 0%, #e8e3d3 100%);
|
||||
color: #2d3a24;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Animated background vines */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image:
|
||||
url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10,50 Q30,20 50,50 T90,50' stroke='%23a8b89c' stroke-width='0.5' fill='none' opacity='0.3'/%3E%3C/svg%3E");
|
||||
background-size: 200px 200px;
|
||||
animation: floatVines 30s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@keyframes floatVines {
|
||||
0%, 100% { transform: translateX(0) translateY(0); }
|
||||
50% { transform: translateX(-20px) translateY(-10px); }
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #4a5c3a;
|
||||
margin-bottom: 40px;
|
||||
font-size: 2.5em;
|
||||
font-weight: 300;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h1::after {
|
||||
content: '🌿';
|
||||
position: absolute;
|
||||
right: -40px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(-20deg);
|
||||
font-size: 0.6em;
|
||||
animation: sway 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes sway {
|
||||
0%, 100% { transform: translateY(-50%) rotate(-20deg); }
|
||||
50% { transform: translateY(-50%) rotate(20deg); }
|
||||
}
|
||||
|
||||
.hybrid-component {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 30px;
|
||||
padding: 40px;
|
||||
box-shadow:
|
||||
0 10px 40px rgba(74, 92, 58, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Decorative leaf corners */
|
||||
.hybrid-component::before,
|
||||
.hybrid-component::after {
|
||||
content: '🍃';
|
||||
position: absolute;
|
||||
font-size: 2em;
|
||||
opacity: 0.3;
|
||||
animation: leafFloat 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hybrid-component::before {
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.hybrid-component::after {
|
||||
bottom: -10px;
|
||||
right: -10px;
|
||||
transform: rotate(135deg);
|
||||
animation-delay: 3s;
|
||||
}
|
||||
|
||||
@keyframes leafFloat {
|
||||
0%, 100% { transform: translateY(0) rotate(inherit); }
|
||||
50% { transform: translateY(-5px) rotate(calc(inherit + 10deg)); }
|
||||
}
|
||||
|
||||
/* Search Container */
|
||||
.search-container {
|
||||
position: relative;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #f0f4e8 0%, #e8eede 100%);
|
||||
border-radius: 25px;
|
||||
border: 2px solid #c5d4b5;
|
||||
overflow: hidden;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.search-wrapper:focus-within {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 8px 30px rgba(139, 168, 112, 0.2);
|
||||
border-color: #8ba870;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 20px 60px 20px 25px;
|
||||
font-size: 18px;
|
||||
font-family: inherit;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #2d3a24;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: #7a8b6a;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.search-input:focus::placeholder {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
.search-icon:hover {
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
.search-icon svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
stroke: #7a8b6a;
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
transition: stroke 0.3s;
|
||||
}
|
||||
|
||||
.search-wrapper:focus-within .search-icon svg {
|
||||
stroke: #4a5c3a;
|
||||
}
|
||||
|
||||
/* Growing vine animation on search */
|
||||
.search-vine {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #8ba870 0%, #a4b896 50%, #8ba870 100%);
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.search-wrapper:focus-within .search-vine {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
/* Autocomplete Dropdown */
|
||||
.autocomplete {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 10px;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 40px rgba(74, 92, 58, 0.15);
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.95);
|
||||
pointer-events: none;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.autocomplete.active {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.autocomplete-item {
|
||||
padding: 15px 25px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.autocomplete-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
background: #8ba870;
|
||||
transform: scaleY(0);
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
.autocomplete-item:hover {
|
||||
background: #f0f4e8;
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.autocomplete-item:hover::before {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
.autocomplete-icon {
|
||||
font-size: 1.2em;
|
||||
opacity: 0;
|
||||
transform: scale(0) rotate(-180deg);
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.autocomplete-item:hover .autocomplete-icon {
|
||||
opacity: 1;
|
||||
transform: scale(1) rotate(0);
|
||||
}
|
||||
|
||||
/* Recent Items (Seed Pods) */
|
||||
.recent-items {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.recent-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
color: #4a5c3a;
|
||||
}
|
||||
|
||||
.recent-header h3 {
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.seed-pods {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.seed-pod {
|
||||
background: linear-gradient(135deg, #e8d5b7 0%, #d4c4a0 100%);
|
||||
border: 1px solid #c9b897;
|
||||
border-radius: 20px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.seed-pod::before {
|
||||
content: '🌰';
|
||||
position: absolute;
|
||||
left: -20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
opacity: 0;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.seed-pod:hover {
|
||||
transform: translateY(-2px) scale(1.05);
|
||||
box-shadow: 0 4px 15px rgba(180, 160, 120, 0.3);
|
||||
padding-left: 30px;
|
||||
background: linear-gradient(135deg, #f0e2c4 0%, #dcc8a5 100%);
|
||||
}
|
||||
|
||||
.seed-pod:hover::before {
|
||||
left: 8px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Filters (Unfolding Leaves) */
|
||||
.filters {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.filters-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.filters-header h3 {
|
||||
color: #4a5c3a;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.filter-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.2em;
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
.filter-toggle.active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.filter-leaves {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 12px;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.filter-leaves.open {
|
||||
max-height: 200px;
|
||||
opacity: 1;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.filter-leaf {
|
||||
background: linear-gradient(135deg, #d4e4c5 0%, #c5d4b5 100%);
|
||||
border: 1px solid #b5c4a5;
|
||||
border-radius: 15px 5px 15px 5px;
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filter-leaf::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, #a4b896 0%, #8ba870 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-out;
|
||||
}
|
||||
|
||||
.filter-leaf.active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.filter-leaf span {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #4a5c3a;
|
||||
font-size: 14px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.filter-leaf.active span {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.filter-leaf:hover {
|
||||
transform: scale(1.05) rotate(-2deg);
|
||||
box-shadow: 0 4px 15px rgba(139, 168, 112, 0.2);
|
||||
}
|
||||
|
||||
/* Results Preview */
|
||||
.results-preview {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.results-preview.active {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.results-header h3 {
|
||||
color: #4a5c3a;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.results-count {
|
||||
color: #7a8b6a;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
background: linear-gradient(135deg, #fafaf5 0%, #f5f5f0 100%);
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.result-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -100%;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(139, 168, 112, 0.1) 50%, transparent 100%);
|
||||
transition: left 0.5s ease-out;
|
||||
}
|
||||
|
||||
.result-item:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.result-item:hover {
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 4px 20px rgba(139, 168, 112, 0.1);
|
||||
border-color: #c5d4b5;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2d3a24;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.result-description {
|
||||
font-size: 14px;
|
||||
color: #7a8b6a;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.result-meta {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
color: #a0a0a0;
|
||||
}
|
||||
|
||||
/* Loading animation */
|
||||
.loading-roots {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.loading-roots.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.root {
|
||||
width: 4px;
|
||||
height: 40px;
|
||||
background: linear-gradient(to bottom, #8ba870, #4a5c3a);
|
||||
border-radius: 2px;
|
||||
animation: growRoot 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.root:nth-child(2) { animation-delay: 0.1s; }
|
||||
.root:nth-child(3) { animation-delay: 0.2s; }
|
||||
.root:nth-child(4) { animation-delay: 0.3s; }
|
||||
.root:nth-child(5) { animation-delay: 0.4s; }
|
||||
|
||||
@keyframes growRoot {
|
||||
0%, 100% {
|
||||
transform: scaleY(0.4);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Breathing idle animation */
|
||||
.breathing {
|
||||
animation: breathe 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes breathe {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.02); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Search Hub - Organic Nature Theme</h1>
|
||||
|
||||
<div class="hybrid-component breathing">
|
||||
<!-- Search Bar with Growing Vine -->
|
||||
<div class="search-container">
|
||||
<div class="search-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
class="search-input"
|
||||
placeholder="Plant your search seeds..."
|
||||
id="searchInput"
|
||||
>
|
||||
<div class="search-icon" id="searchIcon">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="search-vine"></div>
|
||||
</div>
|
||||
|
||||
<!-- Autocomplete Dropdown (Blooming Flowers) -->
|
||||
<div class="autocomplete" id="autocomplete">
|
||||
<div class="autocomplete-item">
|
||||
<span class="autocomplete-icon">🌸</span>
|
||||
<span>Organic vegetables delivery</span>
|
||||
</div>
|
||||
<div class="autocomplete-item">
|
||||
<span class="autocomplete-icon">🌺</span>
|
||||
<span>Organic farming techniques</span>
|
||||
</div>
|
||||
<div class="autocomplete-item">
|
||||
<span class="autocomplete-icon">🌼</span>
|
||||
<span>Organic garden design ideas</span>
|
||||
</div>
|
||||
<div class="autocomplete-item">
|
||||
<span class="autocomplete-icon">🌻</span>
|
||||
<span>Organic skincare products</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Items (Seed Pods) -->
|
||||
<div class="recent-items">
|
||||
<div class="recent-header">
|
||||
<span>🌱</span>
|
||||
<h3>Recently Planted</h3>
|
||||
</div>
|
||||
<div class="seed-pods">
|
||||
<div class="seed-pod">Garden tools</div>
|
||||
<div class="seed-pod">Composting guide</div>
|
||||
<div class="seed-pod">Native plants</div>
|
||||
<div class="seed-pod">Herb garden</div>
|
||||
<div class="seed-pod">Soil health</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters (Unfolding Leaves) -->
|
||||
<div class="filters">
|
||||
<div class="filters-header">
|
||||
<h3>🍀 Filter by Category</h3>
|
||||
<button class="filter-toggle" id="filterToggle">🍃</button>
|
||||
</div>
|
||||
<div class="filter-leaves" id="filterLeaves">
|
||||
<div class="filter-leaf" data-filter="plants">
|
||||
<span>🌿 Plants</span>
|
||||
</div>
|
||||
<div class="filter-leaf" data-filter="tools">
|
||||
<span>🛠️ Tools</span>
|
||||
</div>
|
||||
<div class="filter-leaf" data-filter="guides">
|
||||
<span>📖 Guides</span>
|
||||
</div>
|
||||
<div class="filter-leaf" data-filter="seeds">
|
||||
<span>🌰 Seeds</span>
|
||||
</div>
|
||||
<div class="filter-leaf" data-filter="organic">
|
||||
<span>🌾 Organic</span>
|
||||
</div>
|
||||
<div class="filter-leaf" data-filter="indoor">
|
||||
<span>🪴 Indoor</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Animation -->
|
||||
<div class="loading-roots" id="loadingRoots">
|
||||
<div class="root"></div>
|
||||
<div class="root"></div>
|
||||
<div class="root"></div>
|
||||
<div class="root"></div>
|
||||
<div class="root"></div>
|
||||
</div>
|
||||
|
||||
<!-- Results Preview (Natural Growth) -->
|
||||
<div class="results-preview" id="resultsPreview">
|
||||
<div class="results-header">
|
||||
<h3>🌳 Growing Results</h3>
|
||||
<span class="results-count">5 branches found</span>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-title">Organic Vegetable Garden Starter Kit</div>
|
||||
<div class="result-description">
|
||||
Everything you need to start your own organic vegetable garden, including heirloom seeds,
|
||||
natural fertilizers, and comprehensive growing guides.
|
||||
</div>
|
||||
<div class="result-meta">
|
||||
<span>🌟 4.8 rating</span>
|
||||
<span>💚 Eco-friendly</span>
|
||||
<span>📦 Ships in 2 days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-title">Complete Guide to Companion Planting</div>
|
||||
<div class="result-description">
|
||||
Learn which plants grow better together and create a thriving ecosystem in your garden
|
||||
using natural symbiotic relationships.
|
||||
</div>
|
||||
<div class="result-meta">
|
||||
<span>📚 Digital guide</span>
|
||||
<span>🌱 Beginner friendly</span>
|
||||
<span>⏱️ 45 min read</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-title">Handcrafted Cedar Raised Garden Beds</div>
|
||||
<div class="result-description">
|
||||
Beautiful, sustainably sourced cedar garden beds that naturally resist pests and weather
|
||||
while providing the perfect growing environment.
|
||||
</div>
|
||||
<div class="result-meta">
|
||||
<span>🪵 Sustainable wood</span>
|
||||
<span>🔨 Easy assembly</span>
|
||||
<span>🎯 Custom sizes</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Nature-inspired search hub functionality
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const autocomplete = document.getElementById('autocomplete');
|
||||
const filterToggle = document.getElementById('filterToggle');
|
||||
const filterLeaves = document.getElementById('filterLeaves');
|
||||
const resultsPreview = document.getElementById('resultsPreview');
|
||||
const loadingRoots = document.getElementById('loadingRoots');
|
||||
const seedPods = document.querySelectorAll('.seed-pod');
|
||||
const filterItems = document.querySelectorAll('.filter-leaf');
|
||||
|
||||
let searchTimeout;
|
||||
let activeFilters = new Set();
|
||||
|
||||
// Search input handling with vine growth
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
if (value.length > 0) {
|
||||
// Show autocomplete with blooming animation
|
||||
autocomplete.classList.add('active');
|
||||
|
||||
// Simulate search delay with root growth
|
||||
searchTimeout = setTimeout(() => {
|
||||
showSearchResults();
|
||||
}, 800);
|
||||
} else {
|
||||
autocomplete.classList.remove('active');
|
||||
resultsPreview.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Click outside to close autocomplete
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.search-container')) {
|
||||
autocomplete.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Autocomplete item selection
|
||||
document.querySelectorAll('.autocomplete-item').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const text = item.querySelector('span:last-child').textContent;
|
||||
searchInput.value = text;
|
||||
autocomplete.classList.remove('active');
|
||||
showSearchResults();
|
||||
|
||||
// Add to recent items
|
||||
addToRecentItems(text);
|
||||
});
|
||||
});
|
||||
|
||||
// Filter toggle animation
|
||||
filterToggle.addEventListener('click', () => {
|
||||
filterToggle.classList.toggle('active');
|
||||
filterLeaves.classList.toggle('open');
|
||||
});
|
||||
|
||||
// Filter leaf selection
|
||||
filterItems.forEach(leaf => {
|
||||
leaf.addEventListener('click', () => {
|
||||
leaf.classList.toggle('active');
|
||||
const filter = leaf.dataset.filter;
|
||||
|
||||
if (activeFilters.has(filter)) {
|
||||
activeFilters.delete(filter);
|
||||
} else {
|
||||
activeFilters.add(filter);
|
||||
}
|
||||
|
||||
// Trigger filtered search if there's a search term
|
||||
if (searchInput.value) {
|
||||
showSearchResults();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Seed pod (recent item) clicks
|
||||
seedPods.forEach(pod => {
|
||||
pod.addEventListener('click', () => {
|
||||
searchInput.value = pod.textContent;
|
||||
showSearchResults();
|
||||
});
|
||||
});
|
||||
|
||||
// Show search results with growth animation
|
||||
function showSearchResults() {
|
||||
// Show loading roots
|
||||
resultsPreview.classList.remove('active');
|
||||
loadingRoots.classList.add('active');
|
||||
|
||||
// Simulate loading time
|
||||
setTimeout(() => {
|
||||
loadingRoots.classList.remove('active');
|
||||
resultsPreview.classList.add('active');
|
||||
|
||||
// Update results count based on filters
|
||||
const count = 5 - activeFilters.size;
|
||||
document.querySelector('.results-count').textContent =
|
||||
`${count} branch${count !== 1 ? 'es' : ''} found`;
|
||||
}, 1200);
|
||||
}
|
||||
|
||||
// Add to recent items
|
||||
function addToRecentItems(text) {
|
||||
const seedPodsContainer = document.querySelector('.seed-pods');
|
||||
const existingPods = Array.from(seedPods);
|
||||
|
||||
// Check if already exists
|
||||
const exists = existingPods.some(pod => pod.textContent === text);
|
||||
if (!exists) {
|
||||
// Create new seed pod
|
||||
const newPod = document.createElement('div');
|
||||
newPod.className = 'seed-pod';
|
||||
newPod.textContent = text.substring(0, 20) + (text.length > 20 ? '...' : '');
|
||||
|
||||
// Add click handler
|
||||
newPod.addEventListener('click', () => {
|
||||
searchInput.value = text;
|
||||
showSearchResults();
|
||||
});
|
||||
|
||||
// Add with growth animation
|
||||
newPod.style.transform = 'scale(0)';
|
||||
seedPodsContainer.insertBefore(newPod, seedPodsContainer.firstChild);
|
||||
|
||||
// Animate growth
|
||||
setTimeout(() => {
|
||||
newPod.style.transform = 'scale(1)';
|
||||
newPod.style.transition = 'transform 0.3s ease-out';
|
||||
}, 10);
|
||||
|
||||
// Remove oldest if too many
|
||||
if (seedPodsContainer.children.length > 8) {
|
||||
const oldest = seedPodsContainer.lastChild;
|
||||
oldest.style.transform = 'scale(0)';
|
||||
setTimeout(() => oldest.remove(), 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Result item hover sound effect (optional - using CSS only for now)
|
||||
document.querySelectorAll('.result-item').forEach(item => {
|
||||
item.addEventListener('mouseenter', () => {
|
||||
// Could add subtle nature sound here
|
||||
item.style.transform = 'translateX(5px)';
|
||||
});
|
||||
});
|
||||
|
||||
// Initial breathing animation
|
||||
const component = document.querySelector('.hybrid-component');
|
||||
setInterval(() => {
|
||||
component.classList.add('breathing');
|
||||
}, 100);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,829 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Architectural Brutalism Dashboard Widget</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
background: #1a1a1a;
|
||||
color: #e0e0e0;
|
||||
line-height: 1.6;
|
||||
padding: 40px;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.02em;
|
||||
color: #fff;
|
||||
margin-bottom: 40px;
|
||||
text-transform: uppercase;
|
||||
text-shadow: 4px 4px 0 #000;
|
||||
}
|
||||
|
||||
/* Brutalist Dashboard Container */
|
||||
.hybrid-component {
|
||||
background: #2a2a2a;
|
||||
padding: 0;
|
||||
border: 8px solid #333;
|
||||
box-shadow:
|
||||
0 10px 0 #1a1a1a,
|
||||
0 20px 0 #0a0a0a,
|
||||
0 20px 40px rgba(0,0,0,0.8);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Control Panel Header */
|
||||
.control-panel {
|
||||
background: linear-gradient(to bottom, #404040, #303030);
|
||||
padding: 20px 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 4px solid #1a1a1a;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.control-panel::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 8px;
|
||||
background: repeating-linear-gradient(
|
||||
90deg,
|
||||
#ff6b6b 0,
|
||||
#ff6b6b 20px,
|
||||
#ffcc00 20px,
|
||||
#ffcc00 40px
|
||||
);
|
||||
}
|
||||
|
||||
/* Industrial Switches (Filters) */
|
||||
.filter-bank {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.industrial-switch {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
background: #1a1a1a;
|
||||
border: 3px solid #333;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
|
||||
.industrial-switch:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 0 #000;
|
||||
}
|
||||
|
||||
.switch-label {
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 700;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.switch-handle {
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 3px;
|
||||
width: 34px;
|
||||
height: 28px;
|
||||
background: linear-gradient(to bottom, #666, #444);
|
||||
border: 2px solid #222;
|
||||
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
|
||||
.industrial-switch.active .switch-handle {
|
||||
left: 39px;
|
||||
background: linear-gradient(to bottom, #ff6b6b, #e55a5a);
|
||||
box-shadow: 0 0 10px #ff6b6b;
|
||||
}
|
||||
|
||||
/* Control Buttons */
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.brutalist-button {
|
||||
background: #333;
|
||||
border: 3px solid #444;
|
||||
color: #fff;
|
||||
padding: 10px 20px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.brutalist-button:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 2px 2px 0 #ff6b6b;
|
||||
}
|
||||
|
||||
.brutalist-button:active {
|
||||
transform: translate(0, 0);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.brutalist-button.refresh {
|
||||
background: #444;
|
||||
border-color: #555;
|
||||
}
|
||||
|
||||
.brutalist-button.export {
|
||||
background: #3a3a3a;
|
||||
border-color: #4a4a4a;
|
||||
}
|
||||
|
||||
.brutalist-button.settings {
|
||||
background: #383838;
|
||||
border-color: #484848;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
/* Main Dashboard Content */
|
||||
.dashboard-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 300px;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
/* Chart Area - Concrete Blocks */
|
||||
.chart-container {
|
||||
background: #1a1a1a;
|
||||
padding: 40px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.concrete-chart {
|
||||
height: 400px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-around;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chart-block {
|
||||
flex: 1;
|
||||
background: linear-gradient(to bottom, #4a4a4a, #3a3a3a);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
border: 4px solid #2a2a2a;
|
||||
box-shadow:
|
||||
inset 0 -4px 0 #1a1a1a,
|
||||
0 4px 0 #0a0a0a;
|
||||
}
|
||||
|
||||
.chart-block:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow:
|
||||
inset 0 -4px 0 #1a1a1a,
|
||||
0 14px 0 #0a0a0a,
|
||||
0 14px 20px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.chart-block::before {
|
||||
content: attr(data-value);
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-weight: 900;
|
||||
font-size: 18px;
|
||||
color: #ff6b6b;
|
||||
text-shadow: 2px 2px 0 #000;
|
||||
}
|
||||
|
||||
.chart-block::after {
|
||||
content: attr(data-label);
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-weight: 700;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Grid Overlay */
|
||||
.grid-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
repeating-linear-gradient(0deg, transparent, transparent 39px, #2a2a2a 39px, #2a2a2a 40px),
|
||||
repeating-linear-gradient(90deg, transparent, transparent 19.5%, #2a2a2a 19.5%, #2a2a2a 20%);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Side Panel - Settings & Alerts */
|
||||
.side-panel {
|
||||
background: #262626;
|
||||
border-left: 4px solid #1a1a1a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Settings Panel */
|
||||
.settings-panel {
|
||||
padding: 30px 20px;
|
||||
border-bottom: 4px solid #1a1a1a;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-weight: 900;
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 20px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-label {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.brutalist-slider {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #1a1a1a;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.brutalist-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: linear-gradient(to bottom, #666, #444);
|
||||
border: 3px solid #222;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.brutalist-slider::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: linear-gradient(to bottom, #666, #444);
|
||||
border: 3px solid #222;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.brutalist-select {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
background: #1a1a1a;
|
||||
border: 3px solid #333;
|
||||
color: #999;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Alert System */
|
||||
.alert-panel {
|
||||
flex: 1;
|
||||
padding: 30px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.alert-lights {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.alert-indicator {
|
||||
aspect-ratio: 1;
|
||||
background: #1a1a1a;
|
||||
border: 4px solid #2a2a2a;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.alert-indicator::before {
|
||||
content: attr(data-alert);
|
||||
position: absolute;
|
||||
bottom: -20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: #555;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.alert-indicator.active {
|
||||
animation: alertPulse 1s infinite;
|
||||
background: #ff6b6b;
|
||||
border-color: #ff4444;
|
||||
box-shadow:
|
||||
0 0 20px #ff6b6b,
|
||||
inset 0 0 20px rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.alert-indicator.warning {
|
||||
background: #ffcc00;
|
||||
border-color: #ffaa00;
|
||||
box-shadow:
|
||||
0 0 20px #ffcc00,
|
||||
inset 0 0 20px rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
@keyframes alertPulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.8; transform: scale(0.95); }
|
||||
}
|
||||
|
||||
/* Export Blueprint Overlay */
|
||||
.blueprint-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.95);
|
||||
display: none;
|
||||
z-index: 1000;
|
||||
padding: 40px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.blueprint-overlay.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.blueprint-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: #001a33;
|
||||
border: 4px solid #003366;
|
||||
padding: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.blueprint-grid {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image:
|
||||
repeating-linear-gradient(0deg, transparent, transparent 19px, #003366 19px, #003366 20px),
|
||||
repeating-linear-gradient(90deg, transparent, transparent 19px, #003366 19px, #003366 20px);
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blueprint-title {
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3px;
|
||||
color: #66ccff;
|
||||
margin-bottom: 30px;
|
||||
text-shadow: 2px 2px 0 #003366;
|
||||
}
|
||||
|
||||
.blueprint-data {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #66ccff;
|
||||
line-height: 1.8;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.blueprint-close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #003366;
|
||||
border: 3px solid #66ccff;
|
||||
color: #66ccff;
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.blueprint-close:hover {
|
||||
background: #66ccff;
|
||||
color: #001a33;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Rotating Refresh Animation */
|
||||
@keyframes concreteRotate {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.brutalist-button.refresh.rotating {
|
||||
animation: concreteRotate 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1024px) {
|
||||
.dashboard-content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.side-panel {
|
||||
border-left: none;
|
||||
border-top: 4px solid #1a1a1a;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
border-right: 4px solid #1a1a1a;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-bank {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.concrete-chart {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Dashboard Widget - Architectural Brutalism Theme</h1>
|
||||
|
||||
<div class="hybrid-component">
|
||||
<!-- Control Panel with Filters and Actions -->
|
||||
<div class="control-panel">
|
||||
<div class="filter-bank">
|
||||
<div class="industrial-switch" data-filter="revenue">
|
||||
<span class="switch-label">Revenue</span>
|
||||
<div class="switch-handle"></div>
|
||||
</div>
|
||||
<div class="industrial-switch" data-filter="users">
|
||||
<span class="switch-label">Users</span>
|
||||
<div class="switch-handle"></div>
|
||||
</div>
|
||||
<div class="industrial-switch" data-filter="traffic">
|
||||
<span class="switch-label">Traffic</span>
|
||||
<div class="switch-handle"></div>
|
||||
</div>
|
||||
<div class="industrial-switch" data-filter="sales">
|
||||
<span class="switch-label">Sales</span>
|
||||
<div class="switch-handle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-buttons">
|
||||
<button class="brutalist-button refresh">↻ Refresh</button>
|
||||
<button class="brutalist-button export">⬇ Export</button>
|
||||
<button class="brutalist-button settings">⚙</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Dashboard Content -->
|
||||
<div class="dashboard-content">
|
||||
<!-- Chart Container with Concrete Blocks -->
|
||||
<div class="chart-container">
|
||||
<div class="concrete-chart" id="chart">
|
||||
<div class="chart-block" data-value="87%" data-label="Jan" style="height: 87%"></div>
|
||||
<div class="chart-block" data-value="92%" data-label="Feb" style="height: 92%"></div>
|
||||
<div class="chart-block" data-value="76%" data-label="Mar" style="height: 76%"></div>
|
||||
<div class="chart-block" data-value="84%" data-label="Apr" style="height: 84%"></div>
|
||||
<div class="chart-block" data-value="95%" data-label="May" style="height: 95%"></div>
|
||||
<div class="chart-block" data-value="79%" data-label="Jun" style="height: 79%"></div>
|
||||
</div>
|
||||
<div class="grid-overlay"></div>
|
||||
</div>
|
||||
|
||||
<!-- Side Panel with Settings and Alerts -->
|
||||
<div class="side-panel">
|
||||
<!-- Settings Panel -->
|
||||
<div class="settings-panel">
|
||||
<h3 class="panel-title">Control Infrastructure</h3>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Update Frequency</label>
|
||||
<input type="range" class="brutalist-slider" min="1" max="60" value="30">
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Data Granularity</label>
|
||||
<select class="brutalist-select">
|
||||
<option>Hourly</option>
|
||||
<option>Daily</option>
|
||||
<option selected>Monthly</option>
|
||||
<option>Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Alert Threshold</label>
|
||||
<input type="range" class="brutalist-slider" min="0" max="100" value="75">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alert Panel -->
|
||||
<div class="alert-panel">
|
||||
<h3 class="panel-title">Warning System</h3>
|
||||
|
||||
<div class="alert-lights">
|
||||
<div class="alert-indicator" data-alert="CPU Load"></div>
|
||||
<div class="alert-indicator active" data-alert="Memory"></div>
|
||||
<div class="alert-indicator" data-alert="Network"></div>
|
||||
<div class="alert-indicator warning" data-alert="Storage"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Blueprint Export Overlay -->
|
||||
<div class="blueprint-overlay" id="blueprintOverlay">
|
||||
<div class="blueprint-content">
|
||||
<div class="blueprint-grid"></div>
|
||||
<button class="blueprint-close" onclick="closeBlueprint()">×</button>
|
||||
<h2 class="blueprint-title">System Architecture Blueprint</h2>
|
||||
<pre class="blueprint-data" id="blueprintData"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Industrial Switch Functionality
|
||||
const switches = document.querySelectorAll('.industrial-switch');
|
||||
const chartBlocks = document.querySelectorAll('.chart-block');
|
||||
|
||||
switches.forEach(switchEl => {
|
||||
switchEl.addEventListener('click', function() {
|
||||
this.classList.toggle('active');
|
||||
updateChartData();
|
||||
});
|
||||
});
|
||||
|
||||
// Chart Block Interactions
|
||||
chartBlocks.forEach(block => {
|
||||
block.addEventListener('click', function() {
|
||||
// Animate block
|
||||
this.style.transition = 'all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55)';
|
||||
this.style.transform = 'translateY(-20px) scale(1.05)';
|
||||
|
||||
setTimeout(() => {
|
||||
this.style.transform = 'translateY(0) scale(1)';
|
||||
}, 600);
|
||||
|
||||
// Flash value
|
||||
const value = this.getAttribute('data-value');
|
||||
this.setAttribute('data-value', '▓▓▓');
|
||||
setTimeout(() => {
|
||||
this.setAttribute('data-value', value);
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
// Refresh Functionality
|
||||
const refreshBtn = document.querySelector('.brutalist-button.refresh');
|
||||
refreshBtn.addEventListener('click', function() {
|
||||
this.classList.add('rotating');
|
||||
|
||||
// Simulate data refresh
|
||||
setTimeout(() => {
|
||||
updateChartData();
|
||||
this.classList.remove('rotating');
|
||||
|
||||
// Flash all blocks
|
||||
chartBlocks.forEach((block, index) => {
|
||||
setTimeout(() => {
|
||||
const newHeight = Math.floor(Math.random() * 30) + 70;
|
||||
block.style.height = newHeight + '%';
|
||||
block.setAttribute('data-value', newHeight + '%');
|
||||
}, index * 100);
|
||||
});
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
// Export Blueprint Functionality
|
||||
const exportBtn = document.querySelector('.brutalist-button.export');
|
||||
exportBtn.addEventListener('click', function() {
|
||||
generateBlueprint();
|
||||
document.getElementById('blueprintOverlay').classList.add('active');
|
||||
});
|
||||
|
||||
function generateBlueprint() {
|
||||
const activeFilters = Array.from(switches)
|
||||
.filter(s => s.classList.contains('active'))
|
||||
.map(s => s.getAttribute('data-filter'));
|
||||
|
||||
const chartData = Array.from(chartBlocks).map(block => ({
|
||||
label: block.getAttribute('data-label'),
|
||||
value: block.getAttribute('data-value')
|
||||
}));
|
||||
|
||||
const blueprint = `DASHBOARD CONFIGURATION BLUEPRINT
|
||||
===============================================
|
||||
Generated: ${new Date().toISOString()}
|
||||
Version: 1.0.0
|
||||
|
||||
ACTIVE FILTERS:
|
||||
${activeFilters.length ? activeFilters.map(f => ` - ${f.toUpperCase()}`).join('\n') : ' - NONE'}
|
||||
|
||||
CHART DATA STRUCTURE:
|
||||
${chartData.map(d => ` ${d.label}: ${d.value}`).join('\n')}
|
||||
|
||||
SYSTEM ALERTS:
|
||||
- CPU Load: NORMAL
|
||||
- Memory: CRITICAL
|
||||
- Network: NORMAL
|
||||
- Storage: WARNING
|
||||
|
||||
SETTINGS:
|
||||
- Update Frequency: 30s
|
||||
- Data Granularity: MONTHLY
|
||||
- Alert Threshold: 75%
|
||||
|
||||
BUILD INSTRUCTIONS:
|
||||
1. Initialize concrete block rendering engine
|
||||
2. Configure industrial switch matrix
|
||||
3. Establish alert monitoring pipeline
|
||||
4. Activate data refresh mechanism
|
||||
5. Enable blueprint export system
|
||||
|
||||
END BLUEPRINT
|
||||
===============================================`;
|
||||
|
||||
document.getElementById('blueprintData').textContent = blueprint;
|
||||
}
|
||||
|
||||
function closeBlueprint() {
|
||||
document.getElementById('blueprintOverlay').classList.remove('active');
|
||||
}
|
||||
|
||||
// Settings Panel Interactions
|
||||
const sliders = document.querySelectorAll('.brutalist-slider');
|
||||
sliders.forEach(slider => {
|
||||
slider.addEventListener('input', function() {
|
||||
// Visual feedback
|
||||
const percent = (this.value - this.min) / (this.max - this.min);
|
||||
this.style.background = `linear-gradient(to right, #ff6b6b 0%, #ff6b6b ${percent * 100}%, #1a1a1a ${percent * 100}%, #1a1a1a 100%)`;
|
||||
});
|
||||
});
|
||||
|
||||
// Alert System
|
||||
const alertIndicators = document.querySelectorAll('.alert-indicator');
|
||||
|
||||
// Simulate random alerts
|
||||
setInterval(() => {
|
||||
const randomAlert = alertIndicators[Math.floor(Math.random() * alertIndicators.length)];
|
||||
const alertType = Math.random() > 0.5 ? 'active' : 'warning';
|
||||
|
||||
// Clear other alerts occasionally
|
||||
if (Math.random() > 0.7) {
|
||||
alertIndicators.forEach(alert => {
|
||||
alert.classList.remove('active', 'warning');
|
||||
});
|
||||
}
|
||||
|
||||
// Set new alert
|
||||
randomAlert.classList.add(alertType);
|
||||
}, 5000);
|
||||
|
||||
// Update chart based on active filters
|
||||
function updateChartData() {
|
||||
const activeFilters = Array.from(switches)
|
||||
.filter(s => s.classList.contains('active'))
|
||||
.map(s => s.getAttribute('data-filter'));
|
||||
|
||||
// Simulate different data based on filters
|
||||
chartBlocks.forEach((block, index) => {
|
||||
let baseValue = parseInt(block.style.height);
|
||||
|
||||
activeFilters.forEach(filter => {
|
||||
switch(filter) {
|
||||
case 'revenue':
|
||||
baseValue += Math.random() * 10 - 5;
|
||||
break;
|
||||
case 'users':
|
||||
baseValue += Math.random() * 15 - 7.5;
|
||||
break;
|
||||
case 'traffic':
|
||||
baseValue += Math.random() * 20 - 10;
|
||||
break;
|
||||
case 'sales':
|
||||
baseValue += Math.random() * 12 - 6;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
baseValue = Math.max(20, Math.min(100, baseValue));
|
||||
block.style.height = Math.floor(baseValue) + '%';
|
||||
block.setAttribute('data-value', Math.floor(baseValue) + '%');
|
||||
});
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'r' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
refreshBtn.click();
|
||||
} else if (e.key === 'e' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
exportBtn.click();
|
||||
} else if (e.key === 'Escape') {
|
||||
closeBlueprint();
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize sliders with gradient
|
||||
sliders.forEach(slider => {
|
||||
slider.dispatchEvent(new Event('input'));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,855 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ocean Depths Advanced Search - Themed Hybrid UI #11</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background: linear-gradient(180deg, #001f3f 0%, #003366 20%, #004080 50%, #000033 100%);
|
||||
min-height: 100vh;
|
||||
color: #e0f7fa;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Animated bubbles background */
|
||||
.bubble {
|
||||
position: absolute;
|
||||
background: radial-gradient(circle, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.1) 100%);
|
||||
border-radius: 50%;
|
||||
opacity: 0.6;
|
||||
animation: rise 10s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes rise {
|
||||
from {
|
||||
transform: translateY(100vh) translateX(0) scale(1);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-100px) translateX(100px) scale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Water ripple effect */
|
||||
.water-ripple {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(transparent 30%, rgba(0,100,200,0.1) 50%, transparent 70%);
|
||||
animation: ripple 8s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(20px); }
|
||||
}
|
||||
|
||||
/* Main container */
|
||||
.ocean-depths-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Title with bioluminescence */
|
||||
.title {
|
||||
text-align: center;
|
||||
font-size: 3em;
|
||||
margin-bottom: 40px;
|
||||
text-shadow: 0 0 20px #00ffff, 0 0 40px #00ffff;
|
||||
animation: glow 3s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from { text-shadow: 0 0 20px #00ffff, 0 0 40px #00ffff; }
|
||||
to { text-shadow: 0 0 30px #00ffff, 0 0 50px #00ffff, 0 0 70px #00ffff; }
|
||||
}
|
||||
|
||||
/* Periscope Search Bar */
|
||||
.periscope-search {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto 40px;
|
||||
}
|
||||
|
||||
.periscope-lens {
|
||||
position: absolute;
|
||||
left: -60px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: radial-gradient(circle, #1a5490 0%, #0d47a1 50%, #001f3f 100%);
|
||||
border-radius: 50%;
|
||||
border: 3px solid #00acc1;
|
||||
box-shadow: 0 0 20px rgba(0,172,193,0.5);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 20px 60px 20px 20px;
|
||||
font-size: 1.2em;
|
||||
background: rgba(0,50,100,0.8);
|
||||
border: 2px solid #00acc1;
|
||||
border-radius: 50px;
|
||||
color: #e0f7fa;
|
||||
box-shadow: inset 0 2px 10px rgba(0,0,0,0.5), 0 0 20px rgba(0,172,193,0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
box-shadow: inset 0 2px 10px rgba(0,0,0,0.5), 0 0 30px rgba(0,255,255,0.5);
|
||||
}
|
||||
|
||||
.search-button {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 15px 25px;
|
||||
background: linear-gradient(135deg, #00acc1 0%, #0277bd 100%);
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-button:hover {
|
||||
background: linear-gradient(135deg, #00bcd4 0%, #0288d1 100%);
|
||||
box-shadow: 0 0 20px rgba(0,188,212,0.5);
|
||||
}
|
||||
|
||||
/* Control Panel with Valves */
|
||||
.control-panel {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Filter Valves */
|
||||
.filter-valve {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.valve-wheel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(circle, #1565c0 0%, #0d47a1 50%, #001f3f 100%);
|
||||
border-radius: 50%;
|
||||
border: 4px solid #00acc1;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.valve-wheel::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80%;
|
||||
height: 4px;
|
||||
background: #00acc1;
|
||||
box-shadow: 0 0 10px rgba(0,172,193,0.5);
|
||||
}
|
||||
|
||||
.valve-wheel::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(90deg);
|
||||
width: 80%;
|
||||
height: 4px;
|
||||
background: #00acc1;
|
||||
box-shadow: 0 0 10px rgba(0,172,193,0.5);
|
||||
}
|
||||
|
||||
.valve-wheel.active {
|
||||
transform: rotate(45deg);
|
||||
border-color: #00ffff;
|
||||
box-shadow: 0 5px 30px rgba(0,255,255,0.5);
|
||||
}
|
||||
|
||||
.valve-label {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
font-size: 0.9em;
|
||||
color: #b3e5fc;
|
||||
}
|
||||
|
||||
/* Sonar Controls (Advanced Operators) */
|
||||
.sonar-controls {
|
||||
background: rgba(0,50,100,0.6);
|
||||
border: 2px solid #00acc1;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
margin-bottom: 40px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.sonar-title {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.3em;
|
||||
color: #00ffff;
|
||||
text-shadow: 0 0 10px rgba(0,255,255,0.5);
|
||||
}
|
||||
|
||||
.operator-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.operator-btn {
|
||||
padding: 10px 20px;
|
||||
background: linear-gradient(135deg, #004080 0%, #002050 100%);
|
||||
border: 1px solid #00acc1;
|
||||
border-radius: 25px;
|
||||
color: #b3e5fc;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.operator-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(0,255,255,0.3);
|
||||
transition: width 0.3s, height 0.3s;
|
||||
}
|
||||
|
||||
.operator-btn:hover::before {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.operator-btn:hover {
|
||||
border-color: #00ffff;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 0 15px rgba(0,255,255,0.5);
|
||||
}
|
||||
|
||||
/* Autocomplete Bubbles */
|
||||
.autocomplete-bubbles {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.autocomplete-bubbles.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bubble-suggestion {
|
||||
background: rgba(0,100,200,0.8);
|
||||
border: 1px solid #00acc1;
|
||||
border-radius: 30px;
|
||||
padding: 10px 20px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
animation: bubbleIn 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes bubbleIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px) scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.bubble-suggestion:hover {
|
||||
background: rgba(0,150,255,0.9);
|
||||
transform: translateX(10px);
|
||||
box-shadow: 0 0 20px rgba(0,255,255,0.3);
|
||||
}
|
||||
|
||||
/* Ship's Log (History) */
|
||||
.ships-log {
|
||||
background: rgba(0,50,100,0.6);
|
||||
border: 2px solid #00acc1;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
margin-bottom: 40px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.log-title {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 20px;
|
||||
color: #00ffff;
|
||||
text-shadow: 0 0 10px rgba(0,255,255,0.5);
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
background: rgba(0,30,60,0.5);
|
||||
border-left: 3px solid #00acc1;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.log-entry:hover {
|
||||
background: rgba(0,50,100,0.7);
|
||||
border-left-color: #00ffff;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.log-timestamp {
|
||||
font-size: 0.8em;
|
||||
color: #80deea;
|
||||
}
|
||||
|
||||
/* Treasure Chest (Saved Searches) */
|
||||
.treasure-chest {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 80px;
|
||||
height: 60px;
|
||||
cursor: pointer;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.chest-body {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg, #8b6914 0%, #cdaa3d 50%, #8b6914 100%);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.chest-lid {
|
||||
position: absolute;
|
||||
bottom: 35px;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
background: linear-gradient(135deg, #cdaa3d 0%, #eedc82 50%, #cdaa3d 100%);
|
||||
border-radius: 5px 5px 0 0;
|
||||
transform-origin: bottom;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.treasure-chest:hover .chest-lid {
|
||||
transform: rotateX(-45deg);
|
||||
}
|
||||
|
||||
.saved-searches {
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
right: 20px;
|
||||
background: rgba(0,50,100,0.9);
|
||||
border: 2px solid #cdaa3d;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
display: none;
|
||||
max-width: 300px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.saved-searches.open {
|
||||
display: block;
|
||||
animation: treasureOpen 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes treasureOpen {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8) translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fish School Results */
|
||||
.results-ocean {
|
||||
min-height: 400px;
|
||||
position: relative;
|
||||
background: rgba(0,30,60,0.3);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fish-result {
|
||||
background: linear-gradient(135deg, #004080 0%, #0066cc 100%);
|
||||
border: 1px solid #00acc1;
|
||||
border-radius: 50px 20px 20px 50px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
animation: swim 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes swim {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(10px) rotate(1deg); }
|
||||
50% { transform: translateX(-10px) rotate(-1deg); }
|
||||
75% { transform: translateX(5px) rotate(0.5deg); }
|
||||
}
|
||||
|
||||
.fish-result::before {
|
||||
content: '>';
|
||||
position: absolute;
|
||||
right: -15px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 2em;
|
||||
color: #004080;
|
||||
}
|
||||
|
||||
.fish-result:hover {
|
||||
background: linear-gradient(135deg, #0066cc 0%, #0099ff 100%);
|
||||
transform: translateX(20px);
|
||||
box-shadow: 0 0 30px rgba(0,153,255,0.3);
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 10px;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.result-description {
|
||||
color: #b3e5fc;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Pressure Gauge */
|
||||
.pressure-gauge {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: radial-gradient(circle, #001f3f 0%, #000033 100%);
|
||||
border-radius: 50%;
|
||||
border: 3px solid #00acc1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 20px rgba(0,172,193,0.5);
|
||||
}
|
||||
|
||||
.gauge-needle {
|
||||
position: absolute;
|
||||
width: 3px;
|
||||
height: 40px;
|
||||
background: #ff0000;
|
||||
bottom: 50%;
|
||||
left: 50%;
|
||||
transform-origin: bottom;
|
||||
transform: translateX(-50%) rotate(0deg);
|
||||
transition: transform 0.5s ease;
|
||||
box-shadow: 0 0 10px rgba(255,0,0,0.5);
|
||||
}
|
||||
|
||||
.gauge-center {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #00acc1;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.title {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-valve {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.periscope-lens {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
padding: 15px 50px 15px 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="water-ripple"></div>
|
||||
|
||||
<!-- Animated bubbles -->
|
||||
<div id="bubbleContainer"></div>
|
||||
|
||||
<div class="ocean-depths-container">
|
||||
<h1 class="title">Ocean Depths Search</h1>
|
||||
|
||||
<!-- Periscope Search Bar -->
|
||||
<div class="periscope-search">
|
||||
<div class="periscope-lens"></div>
|
||||
<input type="text" class="search-input" id="searchInput" placeholder="Explore the depths...">
|
||||
<button class="search-button" onclick="performSearch()">Dive</button>
|
||||
|
||||
<!-- Autocomplete Bubbles -->
|
||||
<div class="autocomplete-bubbles" id="autocompleteBubbles"></div>
|
||||
</div>
|
||||
|
||||
<!-- Control Panel with Filter Valves -->
|
||||
<div class="control-panel">
|
||||
<div class="filter-valve">
|
||||
<div class="valve-wheel" onclick="toggleFilter(this, 'recent')" data-filter="recent"></div>
|
||||
<div class="valve-label">Recent</div>
|
||||
</div>
|
||||
<div class="filter-valve">
|
||||
<div class="valve-wheel" onclick="toggleFilter(this, 'deep')" data-filter="deep"></div>
|
||||
<div class="valve-label">Deep Search</div>
|
||||
</div>
|
||||
<div class="filter-valve">
|
||||
<div class="valve-wheel" onclick="toggleFilter(this, 'images')" data-filter="images"></div>
|
||||
<div class="valve-label">Images</div>
|
||||
</div>
|
||||
<div class="filter-valve">
|
||||
<div class="valve-wheel" onclick="toggleFilter(this, 'videos')" data-filter="videos"></div>
|
||||
<div class="valve-label">Videos</div>
|
||||
</div>
|
||||
<div class="filter-valve">
|
||||
<div class="valve-wheel" onclick="toggleFilter(this, 'archived')" data-filter="archived"></div>
|
||||
<div class="valve-label">Archived</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sonar Controls (Advanced Operators) -->
|
||||
<div class="sonar-controls">
|
||||
<div class="sonar-title">Sonar Operations</div>
|
||||
<div class="operator-buttons">
|
||||
<button class="operator-btn" onclick="addOperator('AND')">AND</button>
|
||||
<button class="operator-btn" onclick="addOperator('OR')">OR</button>
|
||||
<button class="operator-btn" onclick="addOperator('NOT')">NOT</button>
|
||||
<button class="operator-btn" onclick="addOperator('*')">Wildcard</button>
|
||||
<button class="operator-btn" onclick="addOperator('\"\"')">Exact</button>
|
||||
<button class="operator-btn" onclick="addOperator('~')">Fuzzy</button>
|
||||
<button class="operator-btn" onclick="addOperator('^')">Boost</button>
|
||||
<button class="operator-btn" onclick="addOperator('[]')">Range</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ship's Log (History) -->
|
||||
<div class="ships-log">
|
||||
<div class="log-title">Ship's Log</div>
|
||||
<div id="searchHistory"></div>
|
||||
</div>
|
||||
|
||||
<!-- Results Ocean -->
|
||||
<div class="results-ocean" id="resultsOcean">
|
||||
<div class="fish-result">
|
||||
<div class="result-title">Deep Sea Discovery</div>
|
||||
<div class="result-description">Begin your exploration of the ocean depths...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Treasure Chest (Saved Searches) -->
|
||||
<div class="treasure-chest" onclick="toggleSavedSearches()">
|
||||
<div class="chest-lid"></div>
|
||||
<div class="chest-body"></div>
|
||||
</div>
|
||||
|
||||
<div class="saved-searches" id="savedSearches">
|
||||
<h3 style="color: #cdaa3d; margin-bottom: 15px;">Treasured Searches</h3>
|
||||
<div id="savedSearchesList"></div>
|
||||
<button class="operator-btn" style="margin-top: 10px;" onclick="saveCurrentSearch()">Save Current</button>
|
||||
</div>
|
||||
|
||||
<!-- Pressure Gauge -->
|
||||
<div class="pressure-gauge">
|
||||
<div class="gauge-needle" id="pressureNeedle"></div>
|
||||
<div class="gauge-center"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Initialize bubbles
|
||||
function createBubbles() {
|
||||
const container = document.getElementById('bubbleContainer');
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = 'bubble';
|
||||
bubble.style.width = Math.random() * 40 + 10 + 'px';
|
||||
bubble.style.height = bubble.style.width;
|
||||
bubble.style.left = Math.random() * 100 + '%';
|
||||
bubble.style.animationDelay = Math.random() * 10 + 's';
|
||||
bubble.style.animationDuration = Math.random() * 10 + 10 + 's';
|
||||
container.appendChild(bubble);
|
||||
}
|
||||
}
|
||||
|
||||
// Active filters
|
||||
let activeFilters = new Set();
|
||||
|
||||
// Search history
|
||||
let searchHistory = [];
|
||||
|
||||
// Saved searches
|
||||
let savedSearches = JSON.parse(localStorage.getItem('oceanSearches') || '[]');
|
||||
|
||||
// Toggle filter
|
||||
function toggleFilter(element, filter) {
|
||||
element.classList.toggle('active');
|
||||
if (activeFilters.has(filter)) {
|
||||
activeFilters.delete(filter);
|
||||
} else {
|
||||
activeFilters.add(filter);
|
||||
}
|
||||
updatePressure();
|
||||
}
|
||||
|
||||
// Add operator to search
|
||||
function addOperator(operator) {
|
||||
const input = document.getElementById('searchInput');
|
||||
if (operator === '\"\"') {
|
||||
const start = input.selectionStart;
|
||||
const end = input.selectionEnd;
|
||||
const text = input.value;
|
||||
input.value = text.substring(0, start) + '\"' + text.substring(start, end) + '\"' + text.substring(end);
|
||||
input.focus();
|
||||
input.setSelectionRange(end + 2, end + 2);
|
||||
} else if (operator === '[]') {
|
||||
input.value += ' [TO ]';
|
||||
input.focus();
|
||||
input.setSelectionRange(input.value.length - 4, input.value.length - 4);
|
||||
} else {
|
||||
input.value += ' ' + operator + ' ';
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Autocomplete functionality
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const autocompleteBubbles = document.getElementById('autocompleteBubbles');
|
||||
const suggestions = [
|
||||
'coral reefs ecosystem',
|
||||
'deep sea creatures',
|
||||
'ocean currents patterns',
|
||||
'marine biodiversity',
|
||||
'underwater volcanoes',
|
||||
'bioluminescent organisms',
|
||||
'ocean floor mapping',
|
||||
'submarine canyons',
|
||||
'hydrothermal vents',
|
||||
'abyssal plains'
|
||||
];
|
||||
|
||||
searchInput.addEventListener('input', function() {
|
||||
const value = this.value.toLowerCase();
|
||||
if (value.length > 2) {
|
||||
const matches = suggestions.filter(s => s.toLowerCase().includes(value));
|
||||
showAutocomplete(matches);
|
||||
} else {
|
||||
hideAutocomplete();
|
||||
}
|
||||
});
|
||||
|
||||
function showAutocomplete(matches) {
|
||||
autocompleteBubbles.innerHTML = '';
|
||||
if (matches.length > 0) {
|
||||
autocompleteBubbles.classList.add('active');
|
||||
matches.slice(0, 5).forEach((match, index) => {
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = 'bubble-suggestion';
|
||||
bubble.textContent = match;
|
||||
bubble.style.animationDelay = index * 0.1 + 's';
|
||||
bubble.onclick = () => {
|
||||
searchInput.value = match;
|
||||
hideAutocomplete();
|
||||
};
|
||||
autocompleteBubbles.appendChild(bubble);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hideAutocomplete() {
|
||||
autocompleteBubbles.classList.remove('active');
|
||||
}
|
||||
|
||||
// Perform search
|
||||
function performSearch() {
|
||||
const query = searchInput.value;
|
||||
if (query.trim()) {
|
||||
// Add to history
|
||||
addToHistory(query);
|
||||
|
||||
// Generate results
|
||||
generateResults(query);
|
||||
|
||||
// Update pressure
|
||||
updatePressure();
|
||||
|
||||
// Hide autocomplete
|
||||
hideAutocomplete();
|
||||
}
|
||||
}
|
||||
|
||||
// Add to history
|
||||
function addToHistory(query) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
searchHistory.unshift({ query, timestamp });
|
||||
if (searchHistory.length > 10) searchHistory.pop();
|
||||
updateHistoryDisplay();
|
||||
}
|
||||
|
||||
// Update history display
|
||||
function updateHistoryDisplay() {
|
||||
const historyContainer = document.getElementById('searchHistory');
|
||||
historyContainer.innerHTML = '';
|
||||
searchHistory.forEach(entry => {
|
||||
const logEntry = document.createElement('div');
|
||||
logEntry.className = 'log-entry';
|
||||
logEntry.innerHTML = `
|
||||
<div>${entry.query}</div>
|
||||
<div class="log-timestamp">${entry.timestamp}</div>
|
||||
`;
|
||||
logEntry.onclick = () => {
|
||||
searchInput.value = entry.query;
|
||||
performSearch();
|
||||
};
|
||||
historyContainer.appendChild(logEntry);
|
||||
});
|
||||
}
|
||||
|
||||
// Generate results
|
||||
function generateResults(query) {
|
||||
const resultsOcean = document.getElementById('resultsOcean');
|
||||
resultsOcean.innerHTML = '';
|
||||
|
||||
const sampleResults = [
|
||||
{ title: 'Deep Ocean Mysteries', description: 'Exploring the unknown depths where light cannot reach...' },
|
||||
{ title: 'Bioluminescent Wonders', description: 'Creatures that create their own light in the darkness...' },
|
||||
{ title: 'Underwater Canyons', description: 'Massive geological formations carved by ancient currents...' },
|
||||
{ title: 'Marine Life Adaptations', description: 'How organisms survive extreme pressure and darkness...' },
|
||||
{ title: 'Ocean Current Patterns', description: 'The invisible rivers that flow through our seas...' }
|
||||
];
|
||||
|
||||
sampleResults.forEach((result, index) => {
|
||||
const fishResult = document.createElement('div');
|
||||
fishResult.className = 'fish-result';
|
||||
fishResult.style.animationDelay = index * 2 + 's';
|
||||
fishResult.innerHTML = `
|
||||
<div class="result-title">${result.title}</div>
|
||||
<div class="result-description">${result.description}</div>
|
||||
`;
|
||||
resultsOcean.appendChild(fishResult);
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle saved searches
|
||||
function toggleSavedSearches() {
|
||||
const savedSearchesPanel = document.getElementById('savedSearches');
|
||||
savedSearchesPanel.classList.toggle('open');
|
||||
if (savedSearchesPanel.classList.contains('open')) {
|
||||
updateSavedSearchesList();
|
||||
}
|
||||
}
|
||||
|
||||
// Save current search
|
||||
function saveCurrentSearch() {
|
||||
const query = searchInput.value;
|
||||
if (query.trim() && !savedSearches.includes(query)) {
|
||||
savedSearches.push(query);
|
||||
localStorage.setItem('oceanSearches', JSON.stringify(savedSearches));
|
||||
updateSavedSearchesList();
|
||||
}
|
||||
}
|
||||
|
||||
// Update saved searches list
|
||||
function updateSavedSearchesList() {
|
||||
const list = document.getElementById('savedSearchesList');
|
||||
list.innerHTML = '';
|
||||
savedSearches.forEach(search => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'log-entry';
|
||||
item.textContent = search;
|
||||
item.onclick = () => {
|
||||
searchInput.value = search;
|
||||
performSearch();
|
||||
toggleSavedSearches();
|
||||
};
|
||||
list.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
// Update pressure gauge
|
||||
function updatePressure() {
|
||||
const needle = document.getElementById('pressureNeedle');
|
||||
const pressure = (activeFilters.size * 15) + (searchHistory.length * 5);
|
||||
needle.style.transform = `translateX(-50%) rotate(${Math.min(pressure, 180)}deg)`;
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
searchInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
performSearch();
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
createBubbles();
|
||||
updateSavedSearchesList();
|
||||
|
||||
// Click outside to close autocomplete
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchInput.contains(e.target) && !autocompleteBubbles.contains(e.target)) {
|
||||
hideAutocomplete();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,810 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Art Deco Profile Dashboard - Iteration 12</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Bebas+Neue&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Playfair Display', serif;
|
||||
background: #0a0a0a;
|
||||
color: #f4e7d1;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Art Deco Background Pattern */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
rgba(212, 175, 55, 0.03) 10px,
|
||||
rgba(212, 175, 55, 0.03) 20px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
rgba(212, 175, 55, 0.03) 10px,
|
||||
rgba(212, 175, 55, 0.03) 20px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
/* Header with Art Deco Frame */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
font-size: 4rem;
|
||||
letter-spacing: 0.2em;
|
||||
color: #d4af37;
|
||||
text-shadow:
|
||||
0 0 20px rgba(212, 175, 55, 0.5),
|
||||
2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-ornament {
|
||||
width: 300px;
|
||||
height: 40px;
|
||||
margin: 0 auto;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
#d4af37 20%,
|
||||
#d4af37 80%,
|
||||
transparent 100%
|
||||
);
|
||||
position: relative;
|
||||
clip-path: polygon(
|
||||
0 50%, 10% 0, 90% 0, 100% 50%,
|
||||
90% 100%, 10% 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* Main Grid Layout */
|
||||
.main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 350px 1fr;
|
||||
gap: 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
/* Profile Section */
|
||||
.profile-section {
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
|
||||
border: 2px solid #d4af37;
|
||||
border-radius: 0;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.profile-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: repeating-conic-gradient(
|
||||
from 0deg at 50% 50%,
|
||||
transparent 0deg,
|
||||
rgba(212, 175, 55, 0.1) 10deg,
|
||||
transparent 20deg
|
||||
);
|
||||
animation: rotate 60s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Avatar Container */
|
||||
.avatar-container {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin: 0 auto 30px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.avatar-frame {
|
||||
position: absolute;
|
||||
inset: -20px;
|
||||
background: conic-gradient(
|
||||
from 45deg,
|
||||
#d4af37 0deg,
|
||||
#f4e7d1 45deg,
|
||||
#d4af37 90deg,
|
||||
#8b6914 135deg,
|
||||
#d4af37 180deg,
|
||||
#f4e7d1 225deg,
|
||||
#d4af37 270deg,
|
||||
#8b6914 315deg,
|
||||
#d4af37 360deg
|
||||
);
|
||||
clip-path: polygon(
|
||||
50% 0%, 100% 25%, 100% 75%, 50% 100%,
|
||||
0% 75%, 0% 25%
|
||||
);
|
||||
animation: shimmer 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
|
||||
clip-path: polygon(
|
||||
50% 0%, 100% 25%, 100% 75%, 50% 100%,
|
||||
0% 75%, 0% 25%
|
||||
);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
font-size: 4rem;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
font-size: 1.8rem;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
color: #f4e7d1;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.profile-title {
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: #d4af37;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Stats as Golden Ratio Meters */
|
||||
.stats-container {
|
||||
margin-top: 40px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.stat-meter {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 8px;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.meter-track {
|
||||
height: 20px;
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #d4af37;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.meter-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
#8b6914 0%,
|
||||
#d4af37 50%,
|
||||
#f4e7d1 100%
|
||||
);
|
||||
transition: width 1s ease-out;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.meter-fill::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.3) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
animation: slide 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes slide {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
.meter-value {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
color: #0a0a0a;
|
||||
font-weight: bold;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Main Content Area */
|
||||
.main-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* Activity Feed as Newspaper */
|
||||
.activity-feed {
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
|
||||
border: 2px solid #d4af37;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.feed-header {
|
||||
border-bottom: 3px double #d4af37;
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feed-title {
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
font-size: 2.5rem;
|
||||
letter-spacing: 0.3em;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.feed-date {
|
||||
font-size: 0.9rem;
|
||||
color: #8b6914;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.feed-columns {
|
||||
column-count: 2;
|
||||
column-gap: 30px;
|
||||
column-rule: 1px solid #d4af37;
|
||||
}
|
||||
|
||||
.feed-item {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: rgba(212, 175, 55, 0.05);
|
||||
border-left: 3px solid #d4af37;
|
||||
}
|
||||
|
||||
.feed-item-time {
|
||||
font-size: 0.8rem;
|
||||
color: #8b6914;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.feed-item-text {
|
||||
margin-top: 5px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Settings Panel */
|
||||
.settings-panel {
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
|
||||
border: 2px solid #d4af37;
|
||||
padding: 30px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
padding: 20px;
|
||||
background: rgba(212, 175, 55, 0.05);
|
||||
border: 1px solid #d4af37;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.control-label {
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
color: #d4af37;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.luxury-switch {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
background: #1a1a1a;
|
||||
border: 2px solid #d4af37;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.luxury-switch::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
background: #d4af37;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.luxury-switch.active::before {
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
.luxury-slider {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: #1a1a1a;
|
||||
border: 2px solid #d4af37;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.slider-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #8b6914, #d4af37);
|
||||
width: 50%;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
/* Achievements as Medallions */
|
||||
.achievements-section {
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
|
||||
border: 2px solid #d4af37;
|
||||
padding: 30px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.achievements-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.achievements-title {
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
font-size: 2rem;
|
||||
letter-spacing: 0.2em;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.medallions-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.medallion {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.medallion:hover {
|
||||
transform: scale(1.1) rotate(5deg);
|
||||
}
|
||||
|
||||
.medallion-outer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: conic-gradient(
|
||||
from 0deg,
|
||||
#d4af37 0deg,
|
||||
#f4e7d1 60deg,
|
||||
#d4af37 120deg,
|
||||
#8b6914 180deg,
|
||||
#d4af37 240deg,
|
||||
#f4e7d1 300deg,
|
||||
#d4af37 360deg
|
||||
);
|
||||
border-radius: 50%;
|
||||
animation: rotate 20s linear infinite;
|
||||
}
|
||||
|
||||
.medallion-inner {
|
||||
position: absolute;
|
||||
inset: 10px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
font-size: 2rem;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.medallion-name {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
/* Connections Constellation */
|
||||
.connections-section {
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
|
||||
border: 2px solid #d4af37;
|
||||
padding: 30px;
|
||||
margin-top: 30px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.constellation-canvas {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.connection-node {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: #1a1a1a;
|
||||
border: 2px solid #d4af37;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Bebas Neue', cursive;
|
||||
color: #d4af37;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.connection-node:hover {
|
||||
transform: scale(1.2);
|
||||
background: #d4af37;
|
||||
color: #0a0a0a;
|
||||
box-shadow: 0 0 30px rgba(212, 175, 55, 0.8);
|
||||
}
|
||||
|
||||
.connection-line {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
#d4af37 50%,
|
||||
transparent 100%
|
||||
);
|
||||
transform-origin: left center;
|
||||
z-index: 1;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.main-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.feed-columns {
|
||||
column-count: 1;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard">
|
||||
<!-- Header -->
|
||||
<header class="header">
|
||||
<h1>PROFILE LUXE</h1>
|
||||
<div class="header-ornament"></div>
|
||||
</header>
|
||||
|
||||
<!-- Main Grid -->
|
||||
<div class="main-grid">
|
||||
<!-- Profile Section -->
|
||||
<aside class="profile-section">
|
||||
<!-- Avatar -->
|
||||
<div class="avatar-container">
|
||||
<div class="avatar-frame"></div>
|
||||
<div class="avatar">VL</div>
|
||||
</div>
|
||||
<h2 class="profile-name">Victoria Luxmore</h2>
|
||||
<p class="profile-title">Elite Member Since 1925</p>
|
||||
|
||||
<!-- Stats Meters -->
|
||||
<div class="stats-container">
|
||||
<div class="stat-meter">
|
||||
<div class="stat-label">Prestige Level</div>
|
||||
<div class="meter-track">
|
||||
<div class="meter-fill" style="width: 85%">
|
||||
<span class="meter-value">85</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-meter">
|
||||
<div class="stat-label">Social Influence</div>
|
||||
<div class="meter-track">
|
||||
<div class="meter-fill" style="width: 72%">
|
||||
<span class="meter-value">72</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-meter">
|
||||
<div class="stat-label">Activity Score</div>
|
||||
<div class="meter-track">
|
||||
<div class="meter-fill" style="width: 90%">
|
||||
<span class="meter-value">90</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-meter">
|
||||
<div class="stat-label">Elite Status</div>
|
||||
<div class="meter-track">
|
||||
<div class="meter-fill" style="width: 95%">
|
||||
<span class="meter-value">95</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<!-- Activity Feed -->
|
||||
<section class="activity-feed">
|
||||
<div class="feed-header">
|
||||
<h2 class="feed-title">SOCIETY CHRONICLE</h2>
|
||||
<p class="feed-date">December 6, 1925</p>
|
||||
</div>
|
||||
<div class="feed-columns">
|
||||
<article class="feed-item">
|
||||
<time class="feed-item-time">10:45 PM</time>
|
||||
<p class="feed-item-text">Attended the exclusive Gatsby soirée at the Plaza. Spectacular evening of jazz and champagne.</p>
|
||||
</article>
|
||||
<article class="feed-item">
|
||||
<time class="feed-item-time">8:30 PM</time>
|
||||
<p class="feed-item-text">Achieved Diamond Elite status. Privileges now include access to the Penthouse Lounge.</p>
|
||||
</article>
|
||||
<article class="feed-item">
|
||||
<time class="feed-item-time">6:15 PM</time>
|
||||
<p class="feed-item-text">New connection established with Charleston M. Distinguished member of the Arts Society.</p>
|
||||
</article>
|
||||
<article class="feed-item">
|
||||
<time class="feed-item-time">4:00 PM</time>
|
||||
<p class="feed-item-text">Portfolio update: Art collection valued at new heights. Monet acquisition confirmed.</p>
|
||||
</article>
|
||||
<article class="feed-item">
|
||||
<time class="feed-item-time">2:30 PM</time>
|
||||
<p class="feed-item-text">Received invitation to the Annual Metropolis Gala. Black tie mandatory.</p>
|
||||
</article>
|
||||
<article class="feed-item">
|
||||
<time class="feed-item-time">12:00 PM</time>
|
||||
<p class="feed-item-text">Luncheon at the Ritz with fellow Elite members. Discussed upcoming charity auction.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Settings Panel -->
|
||||
<section class="settings-panel">
|
||||
<div class="control-group">
|
||||
<h3 class="control-label">PRIVACY MODE</h3>
|
||||
<div class="luxury-switch" onclick="toggleSwitch(this)"></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<h3 class="control-label">NOTIFICATIONS</h3>
|
||||
<div class="luxury-switch active" onclick="toggleSwitch(this)"></div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<h3 class="control-label">VISIBILITY</h3>
|
||||
<div class="luxury-slider" onclick="adjustSlider(event, this)">
|
||||
<div class="slider-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<h3 class="control-label">EXCLUSIVITY</h3>
|
||||
<div class="luxury-slider" onclick="adjustSlider(event, this)">
|
||||
<div class="slider-fill" style="width: 80%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Achievements Section -->
|
||||
<section class="achievements-section">
|
||||
<div class="achievements-header">
|
||||
<h2 class="achievements-title">MEDALLIONS OF DISTINCTION</h2>
|
||||
</div>
|
||||
<div class="medallions-grid">
|
||||
<div class="medallion">
|
||||
<div class="medallion-outer"></div>
|
||||
<div class="medallion-inner">★</div>
|
||||
<p class="medallion-name">ELITE STATUS</p>
|
||||
</div>
|
||||
<div class="medallion">
|
||||
<div class="medallion-outer"></div>
|
||||
<div class="medallion-inner">♦</div>
|
||||
<p class="medallion-name">DIAMOND TIER</p>
|
||||
</div>
|
||||
<div class="medallion">
|
||||
<div class="medallion-outer"></div>
|
||||
<div class="medallion-inner">♠</div>
|
||||
<p class="medallion-name">HIGH SOCIETY</p>
|
||||
</div>
|
||||
<div class="medallion">
|
||||
<div class="medallion-outer"></div>
|
||||
<div class="medallion-inner">♣</div>
|
||||
<p class="medallion-name">CLUB MEMBER</p>
|
||||
</div>
|
||||
<div class="medallion">
|
||||
<div class="medallion-outer"></div>
|
||||
<div class="medallion-inner">♥</div>
|
||||
<p class="medallion-name">PHILANTHROPIST</p>
|
||||
</div>
|
||||
<div class="medallion">
|
||||
<div class="medallion-outer"></div>
|
||||
<div class="medallion-inner">⚜</div>
|
||||
<p class="medallion-name">ARISTOCRAT</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Connections Constellation -->
|
||||
<section class="connections-section">
|
||||
<h2 class="achievements-title" style="text-align: center; margin-bottom: 30px;">SOCIAL CONSTELLATION</h2>
|
||||
<div class="constellation-canvas" id="constellation">
|
||||
<!-- Connection nodes will be dynamically positioned -->
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Toggle luxury switches
|
||||
function toggleSwitch(element) {
|
||||
element.classList.toggle('active');
|
||||
}
|
||||
|
||||
// Adjust luxury sliders
|
||||
function adjustSlider(event, slider) {
|
||||
const rect = slider.getBoundingClientRect();
|
||||
const percentage = ((event.clientX - rect.left) / rect.width) * 100;
|
||||
const fill = slider.querySelector('.slider-fill');
|
||||
fill.style.width = Math.max(0, Math.min(100, percentage)) + '%';
|
||||
}
|
||||
|
||||
// Animate stat meters on load
|
||||
window.addEventListener('load', () => {
|
||||
const meters = document.querySelectorAll('.meter-fill');
|
||||
meters.forEach((meter, index) => {
|
||||
const targetWidth = meter.style.width;
|
||||
meter.style.width = '0%';
|
||||
setTimeout(() => {
|
||||
meter.style.width = targetWidth;
|
||||
}, 100 + index * 200);
|
||||
});
|
||||
});
|
||||
|
||||
// Create connections constellation
|
||||
function createConstellation() {
|
||||
const canvas = document.getElementById('constellation');
|
||||
const connections = [
|
||||
{ id: 'CM', x: 20, y: 30 },
|
||||
{ id: 'JG', x: 80, y: 20 },
|
||||
{ id: 'DF', x: 50, y: 50 },
|
||||
{ id: 'NK', x: 30, y: 70 },
|
||||
{ id: 'TW', x: 70, y: 80 },
|
||||
{ id: 'ES', x: 90, y: 60 },
|
||||
{ id: 'RL', x: 10, y: 50 }
|
||||
];
|
||||
|
||||
// Create nodes
|
||||
connections.forEach(conn => {
|
||||
const node = document.createElement('div');
|
||||
node.className = 'connection-node';
|
||||
node.textContent = conn.id;
|
||||
node.style.left = conn.x + '%';
|
||||
node.style.top = conn.y + '%';
|
||||
node.style.transform = 'translate(-50%, -50%)';
|
||||
canvas.appendChild(node);
|
||||
});
|
||||
|
||||
// Create connection lines
|
||||
for (let i = 0; i < connections.length; i++) {
|
||||
for (let j = i + 1; j < connections.length; j++) {
|
||||
if (Math.random() > 0.5) { // Random connections
|
||||
const line = document.createElement('div');
|
||||
line.className = 'connection-line';
|
||||
|
||||
const x1 = connections[i].x;
|
||||
const y1 = connections[i].y;
|
||||
const x2 = connections[j].x;
|
||||
const y2 = connections[j].y;
|
||||
|
||||
const distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
||||
const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
|
||||
|
||||
line.style.width = distance + '%';
|
||||
line.style.left = x1 + '%';
|
||||
line.style.top = y1 + '%';
|
||||
line.style.transform = `rotate(${angle}deg)`;
|
||||
|
||||
canvas.appendChild(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createConstellation();
|
||||
|
||||
// Add hover effects to medallions
|
||||
document.querySelectorAll('.medallion').forEach(medallion => {
|
||||
medallion.addEventListener('click', function() {
|
||||
this.style.animation = 'none';
|
||||
setTimeout(() => {
|
||||
this.style.animation = '';
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
|
||||
// Parallax effect for background pattern
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
const x = e.clientX / window.innerWidth;
|
||||
const y = e.clientY / window.innerHeight;
|
||||
|
||||
document.body.style.backgroundPosition = `${x * 20}px ${y * 20}px`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,969 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Steampunk Machinery Communication Hub</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Libre Baskerville', serif;
|
||||
background: #1a1511;
|
||||
color: #d4af37;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 50%, rgba(212, 175, 55, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 50%, rgba(184, 134, 11, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 50% 20%, rgba(218, 165, 32, 0.05) 0%, transparent 50%);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 2.5rem;
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
color: #daa520;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8),
|
||||
0 0 20px rgba(212, 175, 55, 0.3);
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.communication-hub {
|
||||
width: 100%;
|
||||
max-width: 1400px;
|
||||
background: linear-gradient(145deg, #2a2217, #1f1813);
|
||||
border: 3px solid #8b6914;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
box-shadow:
|
||||
inset 0 0 50px rgba(212, 175, 55, 0.1),
|
||||
0 0 30px rgba(212, 175, 55, 0.2),
|
||||
0 10px 20px rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Decorative gears */
|
||||
.gear {
|
||||
position: absolute;
|
||||
opacity: 0.1;
|
||||
animation: rotate 20s linear infinite;
|
||||
}
|
||||
|
||||
.gear-1 {
|
||||
top: -50px;
|
||||
right: -50px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
animation-duration: 30s;
|
||||
}
|
||||
|
||||
.gear-2 {
|
||||
bottom: -30px;
|
||||
left: -30px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Main layout grid */
|
||||
.hub-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr 250px;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Participant Manifest (Left Panel) */
|
||||
.passenger-manifest {
|
||||
background: #1f1611;
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.manifest-header {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
color: #daa520;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.participant {
|
||||
background: linear-gradient(145deg, #2a2217, #1f1813);
|
||||
border: 1px solid #8b6914;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.participant:hover {
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 0 15px rgba(212, 175, 55, 0.3);
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #8b6914;
|
||||
background: #2a2217;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
color: #daa520;
|
||||
}
|
||||
|
||||
.participant-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-weight: 600;
|
||||
color: #daa520;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.participant-status {
|
||||
font-size: 0.8rem;
|
||||
color: #b8860b;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.steam-indicator {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #4ade80;
|
||||
box-shadow: 0 0 10px #4ade80;
|
||||
animation: steam-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes steam-pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.5; transform: scale(1.2); }
|
||||
}
|
||||
|
||||
/* Center Panel */
|
||||
.center-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Projection Apparatus (Screen Share) */
|
||||
.projection-apparatus {
|
||||
background: #0a0908;
|
||||
border: 3px solid #8b6914;
|
||||
border-radius: 10px;
|
||||
height: 400px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
inset 0 0 50px rgba(0, 0, 0, 0.8),
|
||||
0 0 20px rgba(212, 175, 55, 0.2);
|
||||
}
|
||||
|
||||
.projection-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(ellipse at center, #1a1511 0%, #0a0908 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.projection-content {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.projection-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 20px;
|
||||
animation: projection-flicker 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes projection-flicker {
|
||||
0%, 100% { opacity: 0.3; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
.projection-controls {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* Brass Valves (Video Controls) */
|
||||
.brass-valves {
|
||||
background: linear-gradient(145deg, #2a2217, #1f1813);
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.valve-control {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.valve-control:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.valve-wheel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 30% 30%, #daa520 0%, #8b6914 50%, #654321 100%);
|
||||
border: 3px solid #654321;
|
||||
position: relative;
|
||||
box-shadow:
|
||||
inset 0 0 20px rgba(0, 0, 0, 0.5),
|
||||
0 5px 10px rgba(0, 0, 0, 0.5);
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.valve-control.active .valve-wheel {
|
||||
transform: rotate(180deg);
|
||||
box-shadow:
|
||||
inset 0 0 20px rgba(212, 175, 55, 0.5),
|
||||
0 5px 10px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
.valve-spokes {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 60%;
|
||||
height: 60%;
|
||||
}
|
||||
|
||||
.valve-spoke {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 50%;
|
||||
background: #654321;
|
||||
left: 50%;
|
||||
transform-origin: bottom;
|
||||
}
|
||||
|
||||
.valve-spoke:nth-child(1) { transform: translateX(-50%) rotate(0deg); }
|
||||
.valve-spoke:nth-child(2) { transform: translateX(-50%) rotate(45deg); }
|
||||
.valve-spoke:nth-child(3) { transform: translateX(-50%) rotate(90deg); }
|
||||
.valve-spoke:nth-child(4) { transform: translateX(-50%) rotate(135deg); }
|
||||
|
||||
.valve-center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 30% 30%, #daa520 0%, #8b6914 100%);
|
||||
border: 2px solid #654321;
|
||||
}
|
||||
|
||||
.valve-label {
|
||||
position: absolute;
|
||||
bottom: -25px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0.8rem;
|
||||
color: #b8860b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Right Panel */
|
||||
.right-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Phonograph Cylinder (Recording) */
|
||||
.phonograph-cylinder {
|
||||
background: linear-gradient(145deg, #2a2217, #1f1813);
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cylinder-mechanism {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
background: linear-gradient(90deg, #8b6914 0%, #daa520 25%, #8b6914 50%, #daa520 75%, #8b6914 100%);
|
||||
border-radius: 20px;
|
||||
margin: 0 auto 15px;
|
||||
position: relative;
|
||||
box-shadow:
|
||||
inset 0 0 10px rgba(0, 0, 0, 0.5),
|
||||
0 5px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.cylinder-mechanism.recording {
|
||||
animation: cylinder-spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes cylinder-spin {
|
||||
from { background-position: 0 0; }
|
||||
to { background-position: 100px 0; }
|
||||
}
|
||||
|
||||
.record-button {
|
||||
background: radial-gradient(circle at 30% 30%, #dc2626 0%, #991b1b 100%);
|
||||
border: 3px solid #7f1d1d;
|
||||
border-radius: 50%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 0 auto;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow:
|
||||
inset 0 0 20px rgba(0, 0, 0, 0.5),
|
||||
0 5px 10px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.record-button:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow:
|
||||
inset 0 0 20px rgba(220, 38, 38, 0.5),
|
||||
0 5px 10px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(220, 38, 38, 0.5);
|
||||
}
|
||||
|
||||
.record-button.recording {
|
||||
animation: record-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes record-pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
/* Steam Puffs (Reactions) */
|
||||
.steam-reactions {
|
||||
background: linear-gradient(145deg, #2a2217, #1f1813);
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.reactions-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.steam-button {
|
||||
background: radial-gradient(circle at 30% 30%, #654321 0%, #3e2723 100%);
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.steam-button:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 0 15px rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
.steam-puff {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.8) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.steam-button:active .steam-puff {
|
||||
animation: steam-rise 1s ease-out;
|
||||
}
|
||||
|
||||
@keyframes steam-rise {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(0) scale(0.5);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(-50px) scale(1.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pneumatic Tube Messages (Chat) */
|
||||
.pneumatic-chat {
|
||||
grid-column: 1 / -1;
|
||||
background: linear-gradient(145deg, #1f1611, #141210);
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
height: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tube-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 15px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.tube-messages::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.tube-messages::-webkit-scrollbar-track {
|
||||
background: #1f1611;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.tube-messages::-webkit-scrollbar-thumb {
|
||||
background: #8b6914;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.message-capsule {
|
||||
background: linear-gradient(145deg, #2a2217, #1f1813);
|
||||
border: 1px solid #8b6914;
|
||||
border-radius: 20px;
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
animation: tube-arrive 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes tube-arrive {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px) scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.message-sender {
|
||||
font-weight: 600;
|
||||
color: #daa520;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
color: #d4af37;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 15px;
|
||||
font-size: 0.7rem;
|
||||
color: #8b6914;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tube-input-container {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tube-input {
|
||||
flex: 1;
|
||||
background: #1f1611;
|
||||
border: 2px solid #8b6914;
|
||||
border-radius: 20px;
|
||||
padding: 10px 20px;
|
||||
color: #d4af37;
|
||||
font-family: 'Libre Baskerville', serif;
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tube-input:focus {
|
||||
box-shadow: 0 0 15px rgba(212, 175, 55, 0.3);
|
||||
border-color: #daa520;
|
||||
}
|
||||
|
||||
.tube-send {
|
||||
background: radial-gradient(circle at 30% 30%, #8b6914 0%, #654321 100%);
|
||||
border: 2px solid #654321;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #daa520;
|
||||
font-size: 1.2rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tube-send:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 15px rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
/* Decorative elements */
|
||||
.brass-plate {
|
||||
position: absolute;
|
||||
background: linear-gradient(145deg, #daa520 0%, #8b6914 100%);
|
||||
border: 1px solid #654321;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
font-size: 0.7rem;
|
||||
color: #1f1611;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.hub-label {
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
/* Gauge decorations */
|
||||
.pressure-gauge {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: radial-gradient(circle at 30% 30%, #2a2217 0%, #1f1813 100%);
|
||||
border: 3px solid #8b6914;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.gauge-needle {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 20px;
|
||||
background: #dc2626;
|
||||
transform-origin: bottom;
|
||||
animation: gauge-wobble 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes gauge-wobble {
|
||||
0%, 100% { transform: rotate(-30deg); }
|
||||
50% { transform: rotate(30deg); }
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 1200px) {
|
||||
.hub-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.passenger-manifest,
|
||||
.right-panel {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Communication Hub - Steampunk Machinery Theme</h1>
|
||||
|
||||
<div class="communication-hub">
|
||||
<!-- Decorative gears -->
|
||||
<svg class="gear gear-1" viewBox="0 0 100 100">
|
||||
<path d="M50 15 L55 5 L45 5 Z M50 85 L55 95 L45 95 Z M85 50 L95 55 L95 45 Z M15 50 L5 55 L5 45 Z" fill="#8b6914"/>
|
||||
<circle cx="50" cy="50" r="35" fill="none" stroke="#8b6914" stroke-width="3"/>
|
||||
<circle cx="50" cy="50" r="20" fill="none" stroke="#8b6914" stroke-width="2"/>
|
||||
</svg>
|
||||
|
||||
<svg class="gear gear-2" viewBox="0 0 100 100">
|
||||
<path d="M50 15 L55 5 L45 5 Z M50 85 L55 95 L45 95 Z M85 50 L95 55 L95 45 Z M15 50 L5 55 L5 45 Z" fill="#8b6914"/>
|
||||
<circle cx="50" cy="50" r="35" fill="none" stroke="#8b6914" stroke-width="3"/>
|
||||
</svg>
|
||||
|
||||
<!-- Brass plate label -->
|
||||
<div class="brass-plate hub-label">Victorian Comm Station</div>
|
||||
|
||||
<!-- Pressure gauge decoration -->
|
||||
<div class="pressure-gauge">
|
||||
<div class="gauge-needle"></div>
|
||||
</div>
|
||||
|
||||
<div class="hub-grid">
|
||||
<!-- Passenger Manifest (Participant List) -->
|
||||
<div class="passenger-manifest">
|
||||
<h2 class="manifest-header">Passenger Manifest</h2>
|
||||
<div class="participant">
|
||||
<div class="participant-avatar">VW</div>
|
||||
<div class="participant-info">
|
||||
<div class="participant-name">Victoria Whitmore</div>
|
||||
<div class="participant-status">Chief Engineer</div>
|
||||
</div>
|
||||
<div class="steam-indicator"></div>
|
||||
</div>
|
||||
<div class="participant">
|
||||
<div class="participant-avatar">AT</div>
|
||||
<div class="participant-info">
|
||||
<div class="participant-name">Augustus Thornbury</div>
|
||||
<div class="participant-status">Navigator</div>
|
||||
</div>
|
||||
<div class="steam-indicator"></div>
|
||||
</div>
|
||||
<div class="participant">
|
||||
<div class="participant-avatar">ES</div>
|
||||
<div class="participant-info">
|
||||
<div class="participant-name">Eleanor Sterling</div>
|
||||
<div class="participant-status">Communications</div>
|
||||
</div>
|
||||
<div class="steam-indicator"></div>
|
||||
</div>
|
||||
<div class="participant">
|
||||
<div class="participant-avatar">RB</div>
|
||||
<div class="participant-info">
|
||||
<div class="participant-name">Reginald Blackwood</div>
|
||||
<div class="participant-status">Guest Observer</div>
|
||||
</div>
|
||||
<div class="steam-indicator" style="background: #fbbf24;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Center Panel -->
|
||||
<div class="center-panel">
|
||||
<!-- Projection Apparatus (Screen Share) -->
|
||||
<div class="projection-apparatus">
|
||||
<div class="projection-screen">
|
||||
<div class="projection-content">
|
||||
<div class="projection-icon">🎭</div>
|
||||
<h3 style="color: #daa520; margin-bottom: 10px;">Projection Chamber Active</h3>
|
||||
<p style="color: #b8860b; font-style: italic;">Awaiting visual transmission...</p>
|
||||
</div>
|
||||
<div class="projection-controls">
|
||||
<button class="tube-send" style="width: 50px; height: 50px;">
|
||||
<span>📽️</span>
|
||||
</button>
|
||||
<button class="tube-send" style="width: 50px; height: 50px;">
|
||||
<span>🔍</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Brass Valves (Video Controls) -->
|
||||
<div class="brass-valves">
|
||||
<div class="valve-control" id="camera-valve">
|
||||
<div class="valve-wheel">
|
||||
<div class="valve-spokes">
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
</div>
|
||||
<div class="valve-center"></div>
|
||||
</div>
|
||||
<div class="valve-label">Camera</div>
|
||||
</div>
|
||||
|
||||
<div class="valve-control active" id="audio-valve">
|
||||
<div class="valve-wheel">
|
||||
<div class="valve-spokes">
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
</div>
|
||||
<div class="valve-center"></div>
|
||||
</div>
|
||||
<div class="valve-label">Audio</div>
|
||||
</div>
|
||||
|
||||
<div class="valve-control" id="screen-valve">
|
||||
<div class="valve-wheel">
|
||||
<div class="valve-spokes">
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
<div class="valve-spoke"></div>
|
||||
</div>
|
||||
<div class="valve-center"></div>
|
||||
</div>
|
||||
<div class="valve-label">Screen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Panel -->
|
||||
<div class="right-panel">
|
||||
<!-- Phonograph Cylinder (Recording) -->
|
||||
<div class="phonograph-cylinder">
|
||||
<h3 style="color: #daa520; margin-bottom: 15px; font-size: 1rem;">Recording Chamber</h3>
|
||||
<div class="cylinder-mechanism"></div>
|
||||
<button class="record-button" id="record-btn"></button>
|
||||
<p style="color: #b8860b; margin-top: 10px; font-size: 0.8rem;">00:00:00</p>
|
||||
</div>
|
||||
|
||||
<!-- Steam Puffs (Reactions) -->
|
||||
<div class="steam-reactions">
|
||||
<h3 style="color: #daa520; margin-bottom: 15px; font-size: 1rem; text-align: center;">Steam Signals</h3>
|
||||
<div class="reactions-grid">
|
||||
<button class="steam-button">
|
||||
👍
|
||||
<div class="steam-puff"></div>
|
||||
</button>
|
||||
<button class="steam-button">
|
||||
❤️
|
||||
<div class="steam-puff"></div>
|
||||
</button>
|
||||
<button class="steam-button">
|
||||
😂
|
||||
<div class="steam-puff"></div>
|
||||
</button>
|
||||
<button class="steam-button">
|
||||
👏
|
||||
<div class="steam-puff"></div>
|
||||
</button>
|
||||
<button class="steam-button">
|
||||
🎩
|
||||
<div class="steam-puff"></div>
|
||||
</button>
|
||||
<button class="steam-button">
|
||||
⚙️
|
||||
<div class="steam-puff"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pneumatic Tube Messages (Chat) -->
|
||||
<div class="pneumatic-chat">
|
||||
<div class="tube-messages" id="chat-messages">
|
||||
<div class="message-capsule">
|
||||
<div class="message-time">14:32</div>
|
||||
<div class="message-sender">Victoria Whitmore</div>
|
||||
<div class="message-text">Greetings, fellow inventors! The atmospheric pressure readings are optimal for today's demonstration.</div>
|
||||
</div>
|
||||
<div class="message-capsule">
|
||||
<div class="message-time">14:33</div>
|
||||
<div class="message-sender">Augustus Thornbury</div>
|
||||
<div class="message-text">Splendid! I've calibrated the projection apparatus to maximum clarity. The aetheric interference should be minimal.</div>
|
||||
</div>
|
||||
<div class="message-capsule">
|
||||
<div class="message-time">14:35</div>
|
||||
<div class="message-sender">Eleanor Sterling</div>
|
||||
<div class="message-text">All pneumatic tubes are functioning at peak efficiency. Message velocity has increased by 23% since last fortnight.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tube-input-container">
|
||||
<input type="text" class="tube-input" id="chat-input" placeholder="Compose your pneumatic message...">
|
||||
<button class="tube-send" id="send-btn">➤</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Valve controls
|
||||
const valves = document.querySelectorAll('.valve-control');
|
||||
valves.forEach(valve => {
|
||||
valve.addEventListener('click', function() {
|
||||
this.classList.toggle('active');
|
||||
|
||||
// Add steam effect
|
||||
const steamEffect = document.createElement('div');
|
||||
steamEffect.style.cssText = `
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: radial-gradient(circle, rgba(255,255,255,0.3) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
animation: steam-expand 1s ease-out forwards;
|
||||
pointer-events: none;
|
||||
`;
|
||||
this.appendChild(steamEffect);
|
||||
|
||||
setTimeout(() => steamEffect.remove(), 1000);
|
||||
});
|
||||
});
|
||||
|
||||
// Add CSS for steam expansion
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes steam-expand {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%) scale(2);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Recording functionality
|
||||
const recordBtn = document.getElementById('record-btn');
|
||||
const cylinderMech = document.querySelector('.cylinder-mechanism');
|
||||
let isRecording = false;
|
||||
let recordingTime = 0;
|
||||
let recordingInterval;
|
||||
|
||||
recordBtn.addEventListener('click', function() {
|
||||
isRecording = !isRecording;
|
||||
this.classList.toggle('recording');
|
||||
cylinderMech.classList.toggle('recording');
|
||||
|
||||
if (isRecording) {
|
||||
recordingInterval = setInterval(() => {
|
||||
recordingTime++;
|
||||
const hours = Math.floor(recordingTime / 3600).toString().padStart(2, '0');
|
||||
const minutes = Math.floor((recordingTime % 3600) / 60).toString().padStart(2, '0');
|
||||
const seconds = (recordingTime % 60).toString().padStart(2, '0');
|
||||
this.nextElementSibling.textContent = `${hours}:${minutes}:${seconds}`;
|
||||
}, 1000);
|
||||
} else {
|
||||
clearInterval(recordingInterval);
|
||||
}
|
||||
});
|
||||
|
||||
// Chat functionality
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const sendBtn = document.getElementById('send-btn');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
|
||||
function sendMessage() {
|
||||
const text = chatInput.value.trim();
|
||||
if (text) {
|
||||
const time = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
|
||||
const messageHtml = `
|
||||
<div class="message-capsule">
|
||||
<div class="message-time">${time}</div>
|
||||
<div class="message-sender">You</div>
|
||||
<div class="message-text">${text}</div>
|
||||
</div>
|
||||
`;
|
||||
chatMessages.insertAdjacentHTML('beforeend', messageHtml);
|
||||
chatInput.value = '';
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
|
||||
// Add pneumatic tube sound effect simulation
|
||||
sendBtn.style.transform = 'scale(0.9)';
|
||||
setTimeout(() => sendBtn.style.transform = 'scale(1)', 200);
|
||||
}
|
||||
}
|
||||
|
||||
sendBtn.addEventListener('click', sendMessage);
|
||||
chatInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') sendMessage();
|
||||
});
|
||||
|
||||
// Steam reactions
|
||||
const steamButtons = document.querySelectorAll('.steam-button');
|
||||
steamButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Create multiple steam puffs
|
||||
for (let i = 0; i < 3; i++) {
|
||||
setTimeout(() => {
|
||||
const puff = document.createElement('div');
|
||||
puff.className = 'steam-puff';
|
||||
puff.style.left = `${40 + (Math.random() * 20)}%`;
|
||||
puff.style.animation = 'steam-rise 1s ease-out forwards';
|
||||
this.appendChild(puff);
|
||||
setTimeout(() => puff.remove(), 1000);
|
||||
}, i * 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Participant status updates
|
||||
setInterval(() => {
|
||||
const indicators = document.querySelectorAll('.steam-indicator');
|
||||
indicators.forEach(indicator => {
|
||||
if (Math.random() > 0.7) {
|
||||
indicator.style.animationDuration = Math.random() * 2 + 1 + 's';
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
// Add some ambient gauge movements
|
||||
const gaugeNeedle = document.querySelector('.gauge-needle');
|
||||
setInterval(() => {
|
||||
const rotation = -30 + Math.random() * 60;
|
||||
gaugeNeedle.style.transform = `rotate(${rotation}deg)`;
|
||||
}, 2000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,805 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Glass Morphism Input Intelligence</title>
|
||||
<style>
|
||||
:root {
|
||||
--glass-bg: rgba(255, 255, 255, 0.1);
|
||||
--glass-border: rgba(255, 255, 255, 0.2);
|
||||
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
--glass-blur: blur(10px);
|
||||
--text-primary: #1a1a1a;
|
||||
--text-secondary: #666;
|
||||
--accent-valid: #22c55e;
|
||||
--accent-invalid: #ef4444;
|
||||
--accent-info: #3b82f6;
|
||||
--gradient-edge: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.3) 0%,
|
||||
rgba(255, 255, 255, 0.1) 100%);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--glass-bg: rgba(255, 255, 255, 0.05);
|
||||
--glass-border: rgba(255, 255, 255, 0.1);
|
||||
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
--text-primary: #f5f5f5;
|
||||
--text-secondary: #a8a8a8;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
color: white;
|
||||
font-weight: 300;
|
||||
font-size: 2.5em;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.input-intelligence {
|
||||
position: relative;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.glass-container {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--glass-border);
|
||||
box-shadow: var(--glass-shadow);
|
||||
padding: 24px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glass-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: var(--gradient-edge);
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.glass-input {
|
||||
width: 100%;
|
||||
padding: 16px 48px 16px 16px;
|
||||
font-size: 16px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
color: var(--text-primary);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
transition: all 0.3s ease;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.glass-input:focus {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.input-status {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.input-status.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.status-icon.valid {
|
||||
stroke: var(--accent-valid);
|
||||
}
|
||||
|
||||
.status-icon.invalid {
|
||||
stroke: var(--accent-invalid);
|
||||
}
|
||||
|
||||
.status-icon.loading {
|
||||
stroke: var(--accent-info);
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.help-tooltip {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
box-shadow: var(--glass-shadow);
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.3s ease;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.help-tooltip.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.help-tooltip::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 24px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.help-text {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.autocomplete-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--glass-shadow);
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.98);
|
||||
transition: all 0.3s ease;
|
||||
pointer-events: none;
|
||||
z-index: 20;
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.autocomplete-dropdown.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.autocomplete-item {
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.autocomplete-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.autocomplete-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transition: left 0.3s ease;
|
||||
}
|
||||
|
||||
.autocomplete-item:hover::before,
|
||||
.autocomplete-item.selected::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.autocomplete-item.selected {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.autocomplete-primary {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.autocomplete-secondary {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.format-hint {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 48px;
|
||||
transform: translateY(-50%);
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.format-hint.show {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
color: white;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.feature-list li {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.glass-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.glass-input {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Input Intelligence - Glass Morphism Theme</h1>
|
||||
|
||||
<div class="demo-section">
|
||||
<h2 class="demo-title">Email Input with Validation</h2>
|
||||
<div class="input-intelligence">
|
||||
<div class="glass-container">
|
||||
<div class="input-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
class="glass-input"
|
||||
id="emailInput"
|
||||
placeholder="Enter your email address"
|
||||
autocomplete="off"
|
||||
>
|
||||
<span class="format-hint" id="emailFormatHint"></span>
|
||||
<div class="input-status" id="emailStatus">
|
||||
<svg class="status-icon" viewBox="0 0 24 24" fill="none">
|
||||
<!-- Dynamic SVG content inserted by JS -->
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-tooltip" id="emailHelp">
|
||||
<p class="help-text"></p>
|
||||
</div>
|
||||
<div class="autocomplete-dropdown" id="emailAutocomplete"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h2 class="demo-title">Phone Number with Smart Formatting</h2>
|
||||
<div class="input-intelligence">
|
||||
<div class="glass-container">
|
||||
<div class="input-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
class="glass-input"
|
||||
id="phoneInput"
|
||||
placeholder="Enter phone number"
|
||||
autocomplete="off"
|
||||
>
|
||||
<span class="format-hint" id="phoneFormatHint"></span>
|
||||
<div class="input-status" id="phoneStatus">
|
||||
<svg class="status-icon" viewBox="0 0 24 24" fill="none">
|
||||
<!-- Dynamic SVG content inserted by JS -->
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-tooltip" id="phoneHelp">
|
||||
<p class="help-text"></p>
|
||||
</div>
|
||||
<div class="autocomplete-dropdown" id="phoneAutocomplete"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h2 class="demo-title">Credit Card with Security Features</h2>
|
||||
<div class="input-intelligence">
|
||||
<div class="glass-container">
|
||||
<div class="input-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
class="glass-input"
|
||||
id="cardInput"
|
||||
placeholder="Enter card number"
|
||||
autocomplete="off"
|
||||
>
|
||||
<span class="format-hint" id="cardFormatHint"></span>
|
||||
<div class="input-status" id="cardStatus">
|
||||
<svg class="status-icon" viewBox="0 0 24 24" fill="none">
|
||||
<!-- Dynamic SVG content inserted by JS -->
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-tooltip" id="cardHelp">
|
||||
<p class="help-text"></p>
|
||||
</div>
|
||||
<div class="autocomplete-dropdown" id="cardAutocomplete"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-list">
|
||||
<ul>
|
||||
<li>Real-time validation with visual feedback</li>
|
||||
<li>Contextual help that adapts to input</li>
|
||||
<li>Smart formatting as you type</li>
|
||||
<li>Intelligent autocomplete suggestions</li>
|
||||
<li>Glass morphism design with depth hierarchy</li>
|
||||
<li>Light/dark mode adaptive styling</li>
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// SVG Icons
|
||||
const icons = {
|
||||
valid: '<circle cx="12" cy="12" r="10"></circle><path d="M9 12l2 2 4-4"></path>',
|
||||
invalid: '<circle cx="12" cy="12" r="10"></circle><path d="M15 9l-6 6"></path><path d="M9 9l6 6"></path>',
|
||||
loading: '<circle cx="12" cy="12" r="10" stroke-dasharray="50" stroke-dashoffset="10"></circle>'
|
||||
};
|
||||
|
||||
// Input Intelligence Class
|
||||
class InputIntelligence {
|
||||
constructor(inputId, config) {
|
||||
this.input = document.getElementById(inputId);
|
||||
this.status = document.getElementById(inputId.replace('Input', 'Status'));
|
||||
this.help = document.getElementById(inputId.replace('Input', 'Help'));
|
||||
this.autocomplete = document.getElementById(inputId.replace('Input', 'Autocomplete'));
|
||||
this.formatHint = document.getElementById(inputId.replace('Input', 'FormatHint'));
|
||||
this.config = config;
|
||||
this.selectedIndex = -1;
|
||||
this.suggestions = [];
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.input.addEventListener('input', this.handleInput.bind(this));
|
||||
this.input.addEventListener('focus', this.handleFocus.bind(this));
|
||||
this.input.addEventListener('blur', this.handleBlur.bind(this));
|
||||
this.input.addEventListener('keydown', this.handleKeydown.bind(this));
|
||||
|
||||
this.autocomplete.addEventListener('click', this.handleAutocompleteClick.bind(this));
|
||||
}
|
||||
|
||||
handleInput(e) {
|
||||
const value = e.target.value;
|
||||
|
||||
// Clear previous timeout
|
||||
clearTimeout(this.validationTimeout);
|
||||
clearTimeout(this.autocompleteTimeout);
|
||||
|
||||
// Show loading state
|
||||
this.showStatus('loading');
|
||||
|
||||
// Format input
|
||||
if (this.config.formatter) {
|
||||
const formatted = this.config.formatter(value);
|
||||
if (formatted !== value) {
|
||||
e.target.value = formatted;
|
||||
this.showFormatHint(this.config.formatHint);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate after delay
|
||||
this.validationTimeout = setTimeout(() => {
|
||||
this.validate(e.target.value);
|
||||
}, 500);
|
||||
|
||||
// Show autocomplete after delay
|
||||
if (value.length >= this.config.minLength) {
|
||||
this.autocompleteTimeout = setTimeout(() => {
|
||||
this.showAutocomplete(value);
|
||||
}, 300);
|
||||
} else {
|
||||
this.hideAutocomplete();
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus() {
|
||||
if (this.input.value.length >= this.config.minLength) {
|
||||
this.showAutocomplete(this.input.value);
|
||||
}
|
||||
this.showHelp(this.config.initialHelp);
|
||||
}
|
||||
|
||||
handleBlur(e) {
|
||||
// Delay to allow autocomplete clicks
|
||||
setTimeout(() => {
|
||||
if (!this.autocomplete.contains(e.relatedTarget)) {
|
||||
this.hideAutocomplete();
|
||||
this.hideHelp();
|
||||
this.hideFormatHint();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
if (!this.autocomplete.classList.contains('show')) return;
|
||||
|
||||
const items = this.autocomplete.querySelectorAll('.autocomplete-item');
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
this.selectedIndex = Math.min(this.selectedIndex + 1, items.length - 1);
|
||||
this.updateSelection(items);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
this.selectedIndex = Math.max(this.selectedIndex - 1, -1);
|
||||
this.updateSelection(items);
|
||||
break;
|
||||
case 'Enter':
|
||||
if (this.selectedIndex >= 0 && this.selectedIndex < items.length) {
|
||||
e.preventDefault();
|
||||
this.selectSuggestion(this.suggestions[this.selectedIndex]);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
this.hideAutocomplete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleAutocompleteClick(e) {
|
||||
const item = e.target.closest('.autocomplete-item');
|
||||
if (item) {
|
||||
const index = Array.from(item.parentNode.children).indexOf(item);
|
||||
this.selectSuggestion(this.suggestions[index]);
|
||||
}
|
||||
}
|
||||
|
||||
validate(value) {
|
||||
if (!value) {
|
||||
this.hideStatus();
|
||||
this.showHelp(this.config.initialHelp);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = this.config.validator(value);
|
||||
|
||||
if (result.valid) {
|
||||
this.showStatus('valid');
|
||||
this.showHelp(result.help || this.config.validHelp);
|
||||
} else {
|
||||
this.showStatus('invalid');
|
||||
this.showHelp(result.help || this.config.invalidHelp);
|
||||
}
|
||||
}
|
||||
|
||||
showAutocomplete(value) {
|
||||
this.suggestions = this.config.getSuggestions(value);
|
||||
this.selectedIndex = -1;
|
||||
|
||||
if (this.suggestions.length === 0) {
|
||||
this.hideAutocomplete();
|
||||
return;
|
||||
}
|
||||
|
||||
this.autocomplete.innerHTML = this.suggestions.map((suggestion, index) => `
|
||||
<div class="autocomplete-item">
|
||||
<div class="autocomplete-primary">${suggestion.primary}</div>
|
||||
${suggestion.secondary ? `<div class="autocomplete-secondary">${suggestion.secondary}</div>` : ''}
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
this.autocomplete.classList.add('show');
|
||||
}
|
||||
|
||||
hideAutocomplete() {
|
||||
this.autocomplete.classList.remove('show');
|
||||
this.selectedIndex = -1;
|
||||
}
|
||||
|
||||
updateSelection(items) {
|
||||
items.forEach((item, index) => {
|
||||
item.classList.toggle('selected', index === this.selectedIndex);
|
||||
});
|
||||
}
|
||||
|
||||
selectSuggestion(suggestion) {
|
||||
this.input.value = suggestion.value;
|
||||
this.hideAutocomplete();
|
||||
this.validate(suggestion.value);
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
showStatus(type) {
|
||||
const icon = this.status.querySelector('.status-icon');
|
||||
icon.innerHTML = icons[type];
|
||||
icon.className = `status-icon ${type}`;
|
||||
this.status.classList.add('show');
|
||||
}
|
||||
|
||||
hideStatus() {
|
||||
this.status.classList.remove('show');
|
||||
}
|
||||
|
||||
showHelp(text) {
|
||||
this.help.querySelector('.help-text').textContent = text;
|
||||
this.help.classList.add('show');
|
||||
}
|
||||
|
||||
hideHelp() {
|
||||
this.help.classList.remove('show');
|
||||
}
|
||||
|
||||
showFormatHint(text) {
|
||||
this.formatHint.textContent = text;
|
||||
this.formatHint.classList.add('show');
|
||||
|
||||
clearTimeout(this.formatHintTimeout);
|
||||
this.formatHintTimeout = setTimeout(() => {
|
||||
this.hideFormatHint();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
hideFormatHint() {
|
||||
this.formatHint.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// Email configuration
|
||||
const emailConfig = {
|
||||
minLength: 3,
|
||||
initialHelp: 'Enter a valid email address',
|
||||
validHelp: 'Email address looks good!',
|
||||
invalidHelp: 'Please enter a valid email format',
|
||||
formatHint: 'Auto-formatted',
|
||||
validator: (value) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return {
|
||||
valid: emailRegex.test(value),
|
||||
help: value.includes('@') && !value.includes('.') ?
|
||||
'Don\'t forget the domain extension (e.g., .com)' : null
|
||||
};
|
||||
},
|
||||
formatter: (value) => {
|
||||
return value.toLowerCase().replace(/\s/g, '');
|
||||
},
|
||||
getSuggestions: (value) => {
|
||||
const domains = [
|
||||
{ domain: 'gmail.com', provider: 'Google' },
|
||||
{ domain: 'outlook.com', provider: 'Microsoft' },
|
||||
{ domain: 'yahoo.com', provider: 'Yahoo' },
|
||||
{ domain: 'icloud.com', provider: 'Apple' },
|
||||
{ domain: 'protonmail.com', provider: 'ProtonMail' }
|
||||
];
|
||||
|
||||
if (value.includes('@') && !value.split('@')[1]) {
|
||||
const [username] = value.split('@');
|
||||
return domains.map(d => ({
|
||||
primary: `${username}@${d.domain}`,
|
||||
secondary: `${d.provider} email`,
|
||||
value: `${username}@${d.domain}`
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// Phone configuration
|
||||
const phoneConfig = {
|
||||
minLength: 3,
|
||||
initialHelp: 'Enter a phone number',
|
||||
validHelp: 'Phone number is valid',
|
||||
invalidHelp: 'Please enter a valid phone number',
|
||||
formatHint: 'Auto-formatted',
|
||||
validator: (value) => {
|
||||
const digits = value.replace(/\D/g, '');
|
||||
return {
|
||||
valid: digits.length >= 10 && digits.length <= 15,
|
||||
help: digits.length < 10 ? 'Phone number too short' :
|
||||
digits.length > 15 ? 'Phone number too long' : null
|
||||
};
|
||||
},
|
||||
formatter: (value) => {
|
||||
const digits = value.replace(/\D/g, '');
|
||||
|
||||
if (digits.length <= 3) return digits;
|
||||
if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
|
||||
if (digits.length <= 10) return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
|
||||
|
||||
return `+${digits.slice(0, digits.length - 10)} (${digits.slice(-10, -7)}) ${digits.slice(-7, -4)}-${digits.slice(-4)}`;
|
||||
},
|
||||
getSuggestions: (value) => {
|
||||
const countryCodes = [
|
||||
{ code: '+1', country: 'United States / Canada' },
|
||||
{ code: '+44', country: 'United Kingdom' },
|
||||
{ code: '+33', country: 'France' },
|
||||
{ code: '+49', country: 'Germany' },
|
||||
{ code: '+81', country: 'Japan' }
|
||||
];
|
||||
|
||||
if (value === '+' || value.startsWith('+')) {
|
||||
return countryCodes
|
||||
.filter(c => c.code.startsWith(value))
|
||||
.map(c => ({
|
||||
primary: c.code,
|
||||
secondary: c.country,
|
||||
value: c.code + ' '
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// Credit card configuration
|
||||
const cardConfig = {
|
||||
minLength: 4,
|
||||
initialHelp: 'Enter your credit card number',
|
||||
validHelp: 'Card number is valid',
|
||||
invalidHelp: 'Please enter a valid card number',
|
||||
formatHint: 'Formatting applied',
|
||||
validator: (value) => {
|
||||
const digits = value.replace(/\s/g, '');
|
||||
|
||||
// Basic Luhn algorithm check
|
||||
if (digits.length < 13 || digits.length > 19) {
|
||||
return { valid: false };
|
||||
}
|
||||
|
||||
let sum = 0;
|
||||
let isEven = false;
|
||||
|
||||
for (let i = digits.length - 1; i >= 0; i--) {
|
||||
let digit = parseInt(digits[i]);
|
||||
|
||||
if (isEven) {
|
||||
digit *= 2;
|
||||
if (digit > 9) digit -= 9;
|
||||
}
|
||||
|
||||
sum += digit;
|
||||
isEven = !isEven;
|
||||
}
|
||||
|
||||
return {
|
||||
valid: sum % 10 === 0,
|
||||
help: digits.length < 16 ? 'Card number incomplete' : null
|
||||
};
|
||||
},
|
||||
formatter: (value) => {
|
||||
const digits = value.replace(/\s/g, '');
|
||||
const groups = [];
|
||||
|
||||
// Detect card type and format accordingly
|
||||
if (digits.startsWith('34') || digits.startsWith('37')) {
|
||||
// American Express: 4-6-5
|
||||
groups.push(digits.slice(0, 4), digits.slice(4, 10), digits.slice(10, 15));
|
||||
} else {
|
||||
// Others: 4-4-4-4
|
||||
for (let i = 0; i < digits.length; i += 4) {
|
||||
groups.push(digits.slice(i, i + 4));
|
||||
}
|
||||
}
|
||||
|
||||
return groups.filter(g => g).join(' ');
|
||||
},
|
||||
getSuggestions: (value) => {
|
||||
const cardTypes = [
|
||||
{ prefix: '4', type: 'Visa', icon: '💳' },
|
||||
{ prefix: '5', type: 'Mastercard', icon: '💳' },
|
||||
{ prefix: '34', type: 'American Express', icon: '💳' },
|
||||
{ prefix: '37', type: 'American Express', icon: '💳' },
|
||||
{ prefix: '6', type: 'Discover', icon: '💳' }
|
||||
];
|
||||
|
||||
const digits = value.replace(/\s/g, '');
|
||||
|
||||
if (digits.length <= 2) {
|
||||
return cardTypes
|
||||
.filter(c => c.prefix.startsWith(digits))
|
||||
.map(c => ({
|
||||
primary: `${c.icon} ${c.type}`,
|
||||
secondary: `Starts with ${c.prefix}`,
|
||||
value: c.prefix
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize all inputs
|
||||
new InputIntelligence('emailInput', emailConfig);
|
||||
new InputIntelligence('phoneInput', phoneConfig);
|
||||
new InputIntelligence('cardInput', cardConfig);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,718 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Retro Computing Action Controller</title>
|
||||
<style>
|
||||
/* Retro Terminal Theme */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: #000000;
|
||||
color: #00ff00;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* CRT Monitor Effect */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
rgba(18, 16, 16, 0) 50%,
|
||||
rgba(0, 0, 0, 0.25) 50%
|
||||
);
|
||||
background-size: 100% 2px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
animation: scanlines 8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes scanlines {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 0 10px; }
|
||||
}
|
||||
|
||||
main {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #00ff00;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 30px;
|
||||
text-shadow: 0 0 10px #00ff00;
|
||||
}
|
||||
|
||||
/* Terminal Container */
|
||||
.terminal-controller {
|
||||
background: #0a0a0a;
|
||||
border: 2px solid #00ff00;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow:
|
||||
0 0 20px rgba(0, 255, 0, 0.5),
|
||||
inset 0 0 20px rgba(0, 255, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Terminal Header */
|
||||
.terminal-header {
|
||||
border-bottom: 1px solid #00ff00;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
color: #00ff00;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.terminal-status {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #00ff00;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 5px #00ff00;
|
||||
animation: blink 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 50% { opacity: 1; }
|
||||
51%, 100% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
/* Command History */
|
||||
.command-history {
|
||||
height: 200px;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid #00ff00;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.command-history::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.command-history::-webkit-scrollbar-track {
|
||||
background: #0a0a0a;
|
||||
}
|
||||
|
||||
.command-history::-webkit-scrollbar-thumb {
|
||||
background: #00ff00;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.history-entry {
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.history-entry.command {
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.history-entry.output {
|
||||
color: #00cc00;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.history-entry.error {
|
||||
color: #ff3333;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.history-entry.success {
|
||||
color: #00ff00;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
/* Command Input Area */
|
||||
.command-input-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid #00ff00;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.prompt {
|
||||
color: #00ff00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.command-display {
|
||||
flex: 1;
|
||||
color: #00ff00;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cursor {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 14px;
|
||||
background: #00ff00;
|
||||
animation: cursor-blink 1s infinite;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
@keyframes cursor-blink {
|
||||
0%, 49% { opacity: 1; }
|
||||
50%, 100% { opacity: 0; }
|
||||
}
|
||||
|
||||
/* Action Buttons */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.terminal-button {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: 1px solid #00ff00;
|
||||
color: #00ff00;
|
||||
padding: 12px 20px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.terminal-button:hover {
|
||||
background: rgba(0, 255, 0, 0.1);
|
||||
box-shadow: 0 0 10px rgba(0, 255, 0, 0.5);
|
||||
text-shadow: 0 0 5px #00ff00;
|
||||
}
|
||||
|
||||
.terminal-button:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.terminal-button.executing {
|
||||
pointer-events: none;
|
||||
animation: pulse 0.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
/* Loading Animation */
|
||||
.loading-animation {
|
||||
display: none;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.loading-animation.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
border: 1px solid #00ff00;
|
||||
margin: 10px 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loading-progress {
|
||||
height: 100%;
|
||||
background: #00ff00;
|
||||
width: 0%;
|
||||
transition: width 0.3s;
|
||||
box-shadow: 0 0 10px #00ff00;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Confirmation Dialog */
|
||||
.confirmation-dialog {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #000000;
|
||||
border: 2px solid #00ff00;
|
||||
padding: 20px;
|
||||
z-index: 10;
|
||||
box-shadow: 0 0 30px rgba(0, 255, 0, 0.8);
|
||||
}
|
||||
|
||||
.confirmation-dialog.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.confirmation-message {
|
||||
margin-bottom: 20px;
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.confirmation-prompt {
|
||||
color: #ffff00;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.confirmation-options {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.confirmation-option {
|
||||
color: #00ff00;
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.confirmation-option:hover {
|
||||
border-color: #00ff00;
|
||||
background: rgba(0, 255, 0, 0.1);
|
||||
}
|
||||
|
||||
/* ASCII Art Elements */
|
||||
.ascii-decoration {
|
||||
color: #00ff00;
|
||||
opacity: 0.3;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
white-space: pre;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* Sound indicator */
|
||||
.sound-indicator {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
color: #00ff00;
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.sound-indicator.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Glitch effect for errors */
|
||||
@keyframes glitch {
|
||||
0%, 100% {
|
||||
text-shadow:
|
||||
-2px 0 #ff0000,
|
||||
2px 0 #00ffff;
|
||||
}
|
||||
25% {
|
||||
text-shadow:
|
||||
2px 0 #ff0000,
|
||||
-2px 0 #00ffff;
|
||||
}
|
||||
50% {
|
||||
text-shadow:
|
||||
-2px 0 #ff0000,
|
||||
2px 0 #00ffff;
|
||||
}
|
||||
}
|
||||
|
||||
.glitch {
|
||||
animation: glitch 0.5s infinite;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Action Controller - Retro Computing Theme</h1>
|
||||
|
||||
<div class="terminal-controller">
|
||||
<div class="terminal-header">
|
||||
<div class="terminal-title">SYSTEM TERMINAL v3.1</div>
|
||||
<div class="terminal-status">
|
||||
<div class="status-indicator"></div>
|
||||
<div class="status-indicator"></div>
|
||||
<div class="status-indicator"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ascii-decoration">
|
||||
╔═══════════════════════════════════════════════════════════════╗
|
||||
║ ┌─┐┌─┐┌┬┐┬┌─┐┌┐┌ ┌─┐┌─┐┌┐┌┌┬┐┬─┐┌─┐┬ ┬ ┌─┐┬─┐ ║
|
||||
║ ├─┤│ │ ││ ││││ │ │ ││││ │ ├┬┘│ ││ │ ├┤ ├┬┘ ║
|
||||
║ ┴ ┴└─┘ ┴ ┴└─┘┘└┘ └─┘└─┘┘└┘ ┴ ┴└─└─┘┴─┘┴─┘└─┘┴└─ ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
</div>
|
||||
|
||||
<div class="command-history" id="commandHistory">
|
||||
<div class="history-entry command">> SYSTEM INITIALIZED</div>
|
||||
<div class="history-entry output">Ready for commands...</div>
|
||||
<div class="history-entry output">Type 'HELP' for available commands</div>
|
||||
</div>
|
||||
|
||||
<div class="command-input-area">
|
||||
<span class="prompt">></span>
|
||||
<div class="command-display" id="commandDisplay">
|
||||
<span id="currentCommand"></span><span class="cursor"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="loading-animation" id="loadingAnimation">
|
||||
<div>EXECUTING COMMAND...</div>
|
||||
<div class="loading-bar">
|
||||
<div class="loading-progress" id="loadingProgress"></div>
|
||||
</div>
|
||||
<div class="loading-text" id="loadingText">Processing...</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="terminal-button" data-command="DEPLOY">
|
||||
[F1] DEPLOY
|
||||
</button>
|
||||
<button class="terminal-button" data-command="ANALYZE">
|
||||
[F2] ANALYZE
|
||||
</button>
|
||||
<button class="terminal-button" data-command="BACKUP">
|
||||
[F3] BACKUP
|
||||
</button>
|
||||
<button class="terminal-button" data-command="SHUTDOWN">
|
||||
[F4] SHUTDOWN
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="confirmation-dialog" id="confirmationDialog">
|
||||
<div class="confirmation-message" id="confirmMessage"></div>
|
||||
<div class="confirmation-prompt">CONFIRM ACTION? [Y/N]</div>
|
||||
<div class="confirmation-options">
|
||||
<span class="confirmation-option" id="confirmYes">[Y] YES</span>
|
||||
<span class="confirmation-option" id="confirmNo">[N] NO</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sound-indicator" id="soundIndicator">♪ BEEP</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Terminal State Management
|
||||
const terminal = {
|
||||
history: [],
|
||||
currentCommand: '',
|
||||
isExecuting: false,
|
||||
commandQueue: []
|
||||
};
|
||||
|
||||
// DOM Elements
|
||||
const commandHistory = document.getElementById('commandHistory');
|
||||
const currentCommandEl = document.getElementById('currentCommand');
|
||||
const loadingAnimation = document.getElementById('loadingAnimation');
|
||||
const loadingProgress = document.getElementById('loadingProgress');
|
||||
const loadingText = document.getElementById('loadingText');
|
||||
const confirmationDialog = document.getElementById('confirmationDialog');
|
||||
const confirmMessage = document.getElementById('confirmMessage');
|
||||
const soundIndicator = document.getElementById('soundIndicator');
|
||||
|
||||
// Command Definitions
|
||||
const commands = {
|
||||
DEPLOY: {
|
||||
description: 'Deploy system updates',
|
||||
requiresConfirmation: true,
|
||||
loadingTime: 3000,
|
||||
steps: [
|
||||
'Initializing deployment sequence...',
|
||||
'Checking system integrity...',
|
||||
'Uploading files...',
|
||||
'Verifying checksums...',
|
||||
'Deployment complete!'
|
||||
]
|
||||
},
|
||||
ANALYZE: {
|
||||
description: 'Run system analysis',
|
||||
requiresConfirmation: false,
|
||||
loadingTime: 2000,
|
||||
steps: [
|
||||
'Starting analysis...',
|
||||
'Scanning processes...',
|
||||
'Checking memory usage...',
|
||||
'Analysis complete!'
|
||||
]
|
||||
},
|
||||
BACKUP: {
|
||||
description: 'Create system backup',
|
||||
requiresConfirmation: true,
|
||||
loadingTime: 4000,
|
||||
steps: [
|
||||
'Preparing backup...',
|
||||
'Compressing data...',
|
||||
'Writing to storage...',
|
||||
'Verifying backup integrity...',
|
||||
'Backup successful!'
|
||||
]
|
||||
},
|
||||
SHUTDOWN: {
|
||||
description: 'Shutdown system',
|
||||
requiresConfirmation: true,
|
||||
loadingTime: 2000,
|
||||
steps: [
|
||||
'Initiating shutdown sequence...',
|
||||
'Saving state...',
|
||||
'Closing connections...',
|
||||
'System shutdown complete.'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Sound Effects (using Web Audio API)
|
||||
function playBeep(frequency = 800, duration = 100) {
|
||||
try {
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
oscillator.frequency.value = frequency;
|
||||
oscillator.type = 'square';
|
||||
gainNode.gain.value = 0.1;
|
||||
|
||||
oscillator.start();
|
||||
oscillator.stop(audioContext.currentTime + duration / 1000);
|
||||
|
||||
// Show sound indicator
|
||||
soundIndicator.classList.add('active');
|
||||
setTimeout(() => soundIndicator.classList.remove('active'), 300);
|
||||
} catch (e) {
|
||||
console.log('Audio not supported');
|
||||
}
|
||||
}
|
||||
|
||||
// Add entry to command history
|
||||
function addHistoryEntry(text, type = 'command') {
|
||||
const entry = document.createElement('div');
|
||||
entry.className = `history-entry ${type}`;
|
||||
entry.textContent = type === 'command' ? `> ${text}` : text;
|
||||
commandHistory.appendChild(entry);
|
||||
commandHistory.scrollTop = commandHistory.scrollHeight;
|
||||
|
||||
// Add to history array
|
||||
terminal.history.push({ text, type, timestamp: new Date() });
|
||||
}
|
||||
|
||||
// Show loading animation
|
||||
async function showLoading(command) {
|
||||
loadingAnimation.classList.add('active');
|
||||
const steps = commands[command].steps;
|
||||
const stepDuration = commands[command].loadingTime / steps.length;
|
||||
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
loadingText.textContent = steps[i];
|
||||
loadingProgress.style.width = `${((i + 1) / steps.length) * 100}%`;
|
||||
playBeep(600 + (i * 100), 50);
|
||||
await new Promise(resolve => setTimeout(resolve, stepDuration));
|
||||
}
|
||||
|
||||
loadingAnimation.classList.remove('active');
|
||||
loadingProgress.style.width = '0%';
|
||||
}
|
||||
|
||||
// Show confirmation dialog
|
||||
function showConfirmation(command) {
|
||||
return new Promise((resolve) => {
|
||||
confirmMessage.textContent = `Execute ${command} command?`;
|
||||
confirmationDialog.classList.add('active');
|
||||
playBeep(1000, 100);
|
||||
|
||||
const handleYes = () => {
|
||||
confirmationDialog.classList.remove('active');
|
||||
playBeep(1200, 50);
|
||||
cleanup();
|
||||
resolve(true);
|
||||
};
|
||||
|
||||
const handleNo = () => {
|
||||
confirmationDialog.classList.remove('active');
|
||||
playBeep(400, 100);
|
||||
cleanup();
|
||||
resolve(false);
|
||||
};
|
||||
|
||||
const handleKeypress = (e) => {
|
||||
if (e.key.toLowerCase() === 'y') handleYes();
|
||||
if (e.key.toLowerCase() === 'n') handleNo();
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
document.getElementById('confirmYes').removeEventListener('click', handleYes);
|
||||
document.getElementById('confirmNo').removeEventListener('click', handleNo);
|
||||
document.removeEventListener('keypress', handleKeypress);
|
||||
};
|
||||
|
||||
document.getElementById('confirmYes').addEventListener('click', handleYes);
|
||||
document.getElementById('confirmNo').addEventListener('click', handleNo);
|
||||
document.addEventListener('keypress', handleKeypress);
|
||||
});
|
||||
}
|
||||
|
||||
// Execute command
|
||||
async function executeCommand(command) {
|
||||
if (terminal.isExecuting) return;
|
||||
|
||||
terminal.isExecuting = true;
|
||||
|
||||
// Add command to history
|
||||
addHistoryEntry(command, 'command');
|
||||
currentCommandEl.textContent = '';
|
||||
|
||||
// Check if command exists
|
||||
if (!commands[command]) {
|
||||
addHistoryEntry(`ERROR: Unknown command '${command}'`, 'error');
|
||||
document.body.classList.add('glitch');
|
||||
playBeep(200, 200);
|
||||
setTimeout(() => document.body.classList.remove('glitch'), 500);
|
||||
terminal.isExecuting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Show command description
|
||||
addHistoryEntry(commands[command].description, 'output');
|
||||
|
||||
// Check for confirmation
|
||||
if (commands[command].requiresConfirmation) {
|
||||
const confirmed = await showConfirmation(command);
|
||||
if (!confirmed) {
|
||||
addHistoryEntry('Command cancelled', 'output');
|
||||
terminal.isExecuting = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute with loading animation
|
||||
try {
|
||||
await showLoading(command);
|
||||
addHistoryEntry(`${command} executed successfully`, 'success');
|
||||
playBeep(1600, 100);
|
||||
setTimeout(() => playBeep(1800, 100), 150);
|
||||
} catch (error) {
|
||||
addHistoryEntry(`ERROR: ${command} failed - ${error.message}`, 'error');
|
||||
playBeep(300, 300);
|
||||
}
|
||||
|
||||
terminal.isExecuting = false;
|
||||
}
|
||||
|
||||
// Handle button clicks
|
||||
document.querySelectorAll('.terminal-button').forEach(button => {
|
||||
button.addEventListener('click', async function() {
|
||||
if (terminal.isExecuting) return;
|
||||
|
||||
const command = this.dataset.command;
|
||||
this.classList.add('executing');
|
||||
await executeCommand(command);
|
||||
this.classList.remove('executing');
|
||||
});
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', async (e) => {
|
||||
if (terminal.isExecuting) return;
|
||||
|
||||
// Function key shortcuts
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
await executeCommand('DEPLOY');
|
||||
} else if (e.key === 'F2') {
|
||||
e.preventDefault();
|
||||
await executeCommand('ANALYZE');
|
||||
} else if (e.key === 'F3') {
|
||||
e.preventDefault();
|
||||
await executeCommand('BACKUP');
|
||||
} else if (e.key === 'F4') {
|
||||
e.preventDefault();
|
||||
await executeCommand('SHUTDOWN');
|
||||
}
|
||||
|
||||
// Type command
|
||||
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
|
||||
terminal.currentCommand += e.key.toUpperCase();
|
||||
currentCommandEl.textContent = terminal.currentCommand;
|
||||
playBeep(1000, 30);
|
||||
}
|
||||
|
||||
// Enter to execute typed command
|
||||
if (e.key === 'Enter' && terminal.currentCommand) {
|
||||
await executeCommand(terminal.currentCommand);
|
||||
terminal.currentCommand = '';
|
||||
currentCommandEl.textContent = '';
|
||||
}
|
||||
|
||||
// Backspace
|
||||
if (e.key === 'Backspace' && terminal.currentCommand) {
|
||||
terminal.currentCommand = terminal.currentCommand.slice(0, -1);
|
||||
currentCommandEl.textContent = terminal.currentCommand;
|
||||
playBeep(800, 30);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial beep on load
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => {
|
||||
playBeep(800, 100);
|
||||
setTimeout(() => playBeep(1000, 100), 150);
|
||||
setTimeout(() => playBeep(1200, 100), 300);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Add some random flicker effect
|
||||
setInterval(() => {
|
||||
if (Math.random() > 0.95) {
|
||||
document.body.style.opacity = '0.95';
|
||||
setTimeout(() => {
|
||||
document.body.style.opacity = '1';
|
||||
}, 50);
|
||||
}
|
||||
}, 3000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,854 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zen Philosophy Navigation Center</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Zen color palette - calm, muted tones */
|
||||
--zen-white: #fafaf8;
|
||||
--zen-sand: #e8e3da;
|
||||
--zen-stone: #b8b2a6;
|
||||
--zen-moss: #7a8672;
|
||||
--zen-water: #6b7f8c;
|
||||
--zen-shadow: #4a4a48;
|
||||
--zen-ink: #2c2c2a;
|
||||
|
||||
/* Breathing animation timing */
|
||||
--breathe-in: 3s;
|
||||
--breathe-out: 4s;
|
||||
--transition-flow: cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, var(--zen-white) 0%, var(--zen-sand) 100%);
|
||||
color: var(--zen-ink);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
font-size: 2rem;
|
||||
color: var(--zen-shadow);
|
||||
margin-bottom: 3rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
/* Navigation Center Container */
|
||||
.navigation-center {
|
||||
background: var(--zen-white);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.05);
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Zen decoration - subtle circle pattern */
|
||||
.navigation-center::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, transparent 60%, var(--zen-sand) 100%);
|
||||
top: -150px;
|
||||
right: -150px;
|
||||
opacity: 0.3;
|
||||
animation: breathe var(--breathe-in) ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes breathe {
|
||||
0% { transform: scale(1); opacity: 0.3; }
|
||||
100% { transform: scale(1.1); opacity: 0.2; }
|
||||
}
|
||||
|
||||
/* Top Section - Search and Actions */
|
||||
.nav-top {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Search as Mindful Exploration */
|
||||
.search-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 1rem 3rem 1rem 1.5rem;
|
||||
border: 2px solid transparent;
|
||||
background: var(--zen-sand);
|
||||
border-radius: 30px;
|
||||
font-size: 1rem;
|
||||
color: var(--zen-ink);
|
||||
transition: all 0.4s var(--transition-flow);
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
background: var(--zen-white);
|
||||
border-color: var(--zen-stone);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: var(--zen-stone);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
right: 1.5rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--zen-stone);
|
||||
pointer-events: none;
|
||||
transition: color 0.4s var(--transition-flow);
|
||||
}
|
||||
|
||||
.search-input:focus + .search-icon {
|
||||
color: var(--zen-moss);
|
||||
}
|
||||
|
||||
/* Quick Actions as Zen Shortcuts */
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.action-stone {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background: var(--zen-sand);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--zen-shadow);
|
||||
transition: all 0.3s var(--transition-flow);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.action-stone:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
background: var(--zen-stone);
|
||||
color: var(--zen-white);
|
||||
}
|
||||
|
||||
.action-stone::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(circle at center, var(--zen-water) 0%, transparent 70%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.6s ease;
|
||||
}
|
||||
|
||||
.action-stone:active::after {
|
||||
opacity: 0.3;
|
||||
animation: ripple 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
0% { transform: scale(0); }
|
||||
100% { transform: scale(2); }
|
||||
}
|
||||
|
||||
/* Breadcrumbs as Stepping Path */
|
||||
.stepping-path {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(90deg, var(--zen-sand) 0%, transparent 100%);
|
||||
border-radius: 10px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--zen-stone) transparent;
|
||||
}
|
||||
|
||||
.path-stone {
|
||||
white-space: nowrap;
|
||||
color: var(--zen-shadow);
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
transition: all 0.3s var(--transition-flow);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.path-stone:hover {
|
||||
background: var(--zen-white);
|
||||
color: var(--zen-moss);
|
||||
}
|
||||
|
||||
.path-divider {
|
||||
color: var(--zen-stone);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.path-stone.current {
|
||||
background: var(--zen-moss);
|
||||
color: var(--zen-white);
|
||||
}
|
||||
|
||||
/* Tabs as Meditation Stones */
|
||||
.meditation-stones {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
padding: 1rem;
|
||||
background: var(--zen-sand);
|
||||
border-radius: 15px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--zen-stone) transparent;
|
||||
}
|
||||
|
||||
.stone-tab {
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
background: var(--zen-white);
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
color: var(--zen-shadow);
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
transition: all 0.4s var(--transition-flow);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stone-tab::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, var(--zen-moss) 0%, var(--zen-water) 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s var(--transition-flow);
|
||||
}
|
||||
|
||||
.stone-tab:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stone-tab.active {
|
||||
color: var(--zen-white);
|
||||
}
|
||||
|
||||
.stone-tab.active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.stone-tab span {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Content Area */
|
||||
.content-garden {
|
||||
background: var(--zen-white);
|
||||
border-radius: 15px;
|
||||
padding: 2rem;
|
||||
min-height: 300px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
display: none;
|
||||
animation: fadeInUp 0.6s var(--transition-flow);
|
||||
}
|
||||
|
||||
.content-section.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* State Memory Indicator */
|
||||
.consciousness-indicator {
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--zen-sand);
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--zen-shadow);
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: all 0.4s var(--transition-flow);
|
||||
}
|
||||
|
||||
.consciousness-indicator.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.consciousness-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--zen-moss);
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 0.4; transform: scale(1); }
|
||||
50% { opacity: 1; transform: scale(1.2); }
|
||||
}
|
||||
|
||||
/* Search Results Dropdown */
|
||||
.search-results {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 0.5rem;
|
||||
background: var(--zen-white);
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.3s var(--transition-flow);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.search-results.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.search-result {
|
||||
padding: 1rem 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
border-bottom: 1px solid var(--zen-sand);
|
||||
}
|
||||
|
||||
.search-result:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.search-result:hover {
|
||||
background: var(--zen-sand);
|
||||
}
|
||||
|
||||
.search-result-title {
|
||||
font-weight: 500;
|
||||
color: var(--zen-ink);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.search-result-path {
|
||||
font-size: 0.85rem;
|
||||
color: var(--zen-stone);
|
||||
}
|
||||
|
||||
/* Tooltip for actions */
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-5px);
|
||||
background: var(--zen-ink);
|
||||
color: var(--zen-white);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.85rem;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s var(--transition-flow);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.action-stone:hover .tooltip {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(-50%) translateY(-10px);
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.nav-top {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.meditation-stones {
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.stone-tab {
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.consciousness-indicator {
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Navigation Center - Zen Philosophy Theme</h1>
|
||||
|
||||
<div class="navigation-center">
|
||||
<!-- Top Section with Search and Quick Actions -->
|
||||
<div class="nav-top">
|
||||
<div class="search-container">
|
||||
<input
|
||||
type="text"
|
||||
class="search-input"
|
||||
placeholder="Begin your mindful exploration..."
|
||||
id="searchInput"
|
||||
>
|
||||
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
<div class="search-results" id="searchResults">
|
||||
<!-- Search results will be populated here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quick-actions">
|
||||
<button class="action-stone" data-action="home">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
|
||||
<polyline points="9 22 9 12 15 12 15 22"></polyline>
|
||||
</svg>
|
||||
<span class="tooltip">Return to beginning</span>
|
||||
</button>
|
||||
<button class="action-stone" data-action="bookmark">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="m19 21-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
<span class="tooltip">Mark this moment</span>
|
||||
</button>
|
||||
<button class="action-stone" data-action="settings">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path d="M12 1v6m0 6v6m11-11h-6m-6 0H1"></path>
|
||||
</svg>
|
||||
<span class="tooltip">Adjust your path</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Breadcrumbs as Stepping Path -->
|
||||
<div class="stepping-path" id="steppingPath">
|
||||
<a href="#" class="path-stone" data-path="root">Garden</a>
|
||||
<span class="path-divider">›</span>
|
||||
<a href="#" class="path-stone" data-path="meditation">Meditation</a>
|
||||
<span class="path-divider">›</span>
|
||||
<a href="#" class="path-stone current" data-path="breathing">Breathing</a>
|
||||
</div>
|
||||
|
||||
<!-- Tabs as Meditation Stones -->
|
||||
<div class="meditation-stones" id="meditationStones">
|
||||
<button class="stone-tab active" data-tab="breathing">
|
||||
<span>Breathing</span>
|
||||
</button>
|
||||
<button class="stone-tab" data-tab="mindfulness">
|
||||
<span>Mindfulness</span>
|
||||
</button>
|
||||
<button class="stone-tab" data-tab="movement">
|
||||
<span>Movement</span>
|
||||
</button>
|
||||
<button class="stone-tab" data-tab="reflection">
|
||||
<span>Reflection</span>
|
||||
</button>
|
||||
<button class="stone-tab" data-tab="wisdom">
|
||||
<span>Wisdom</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="content-garden">
|
||||
<div class="content-section active" data-content="breathing">
|
||||
<h2>The Art of Breathing</h2>
|
||||
<p>In the garden of consciousness, breath is the first step on the path to awareness. Each inhale brings new possibilities, each exhale releases what no longer serves.</p>
|
||||
<br>
|
||||
<p>Begin with simple observation. Notice the natural rhythm without forcing change. Like water finding its level, let your breath find its own peaceful cadence.</p>
|
||||
<br>
|
||||
<p>Practice the 4-7-8 technique: Inhale for 4 counts, hold for 7, exhale for 8. This ancient pattern calms the mind and prepares the spirit for deeper meditation.</p>
|
||||
</div>
|
||||
|
||||
<div class="content-section" data-content="mindfulness">
|
||||
<h2>Present Moment Awareness</h2>
|
||||
<p>Mindfulness is the art of being fully present. Like a still pond reflecting the sky, the mindful mind mirrors reality without distortion.</p>
|
||||
<br>
|
||||
<p>Start with your senses. What do you see, hear, feel in this exact moment? Each sensation is a doorway to presence, a bridge from thinking to being.</p>
|
||||
<br>
|
||||
<p>When thoughts arise, observe them like clouds passing through an empty sky. No need to grasp or push away - simply witness and return to now.</p>
|
||||
</div>
|
||||
|
||||
<div class="content-section" data-content="movement">
|
||||
<h2>Meditation in Motion</h2>
|
||||
<p>Movement meditation transforms everyday actions into spiritual practice. Walking, stretching, even washing dishes can become paths to enlightenment.</p>
|
||||
<br>
|
||||
<p>Try walking meditation: Each step deliberate, each movement conscious. Feel the earth beneath your feet, the air around your body. This is moving zen.</p>
|
||||
<br>
|
||||
<p>Gentle yoga or tai chi brings harmony between body and mind. Flow like water, bend like bamboo - strength through flexibility, power through peace.</p>
|
||||
</div>
|
||||
|
||||
<div class="content-section" data-content="reflection">
|
||||
<h2>The Mirror of Self</h2>
|
||||
<p>Reflection deepens understanding. Like a mountain lake that reveals both surface and depths, contemplation shows us who we truly are.</p>
|
||||
<br>
|
||||
<p>Journal your thoughts without judgment. Let words flow like a stream, carrying insights from the depths of consciousness to the light of awareness.</p>
|
||||
<br>
|
||||
<p>Ask yourself: What am I grateful for today? What lessons has life offered? In quiet reflection, wisdom emerges naturally, like flowers blooming in their season.</p>
|
||||
</div>
|
||||
|
||||
<div class="content-section" data-content="wisdom">
|
||||
<h2>Ancient Teachings</h2>
|
||||
<p>"The pine teaches silence, the rock teaches patience, the water teaches persistence." These natural teachers surround us, offering lessons to those who listen.</p>
|
||||
<br>
|
||||
<p>Zen masters remind us: "Before enlightenment, chop wood, carry water. After enlightenment, chop wood, carry water." Profound truth lives in simple actions.</p>
|
||||
<br>
|
||||
<p>Study the wisdom traditions not as dogma but as maps. Each tradition offers a different path up the same mountain. Find the way that resonates with your spirit.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- State Memory Indicator -->
|
||||
<div class="consciousness-indicator" id="consciousnessIndicator">
|
||||
<span class="consciousness-dot"></span>
|
||||
<span>State remembered</span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// State Management - Conscious Awareness
|
||||
class NavigationState {
|
||||
constructor() {
|
||||
this.state = this.loadState() || {
|
||||
currentTab: 'breathing',
|
||||
breadcrumbs: ['root', 'meditation', 'breathing'],
|
||||
searchHistory: [],
|
||||
bookmarks: [],
|
||||
lastVisit: Date.now()
|
||||
};
|
||||
this.saveStateDebounced = this.debounce(this.saveState.bind(this), 1000);
|
||||
}
|
||||
|
||||
loadState() {
|
||||
try {
|
||||
const saved = localStorage.getItem('zenNavState');
|
||||
return saved ? JSON.parse(saved) : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
saveState() {
|
||||
try {
|
||||
localStorage.setItem('zenNavState', JSON.stringify(this.state));
|
||||
this.showConsciousnessIndicator();
|
||||
} catch (e) {
|
||||
console.error('Could not save state:', e);
|
||||
}
|
||||
}
|
||||
|
||||
showConsciousnessIndicator() {
|
||||
const indicator = document.getElementById('consciousnessIndicator');
|
||||
indicator.classList.add('visible');
|
||||
setTimeout(() => {
|
||||
indicator.classList.remove('visible');
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
updateTab(tabName) {
|
||||
this.state.currentTab = tabName;
|
||||
this.saveStateDebounced();
|
||||
}
|
||||
|
||||
updateBreadcrumbs(path) {
|
||||
const index = this.state.breadcrumbs.indexOf(path);
|
||||
if (index > -1) {
|
||||
this.state.breadcrumbs = this.state.breadcrumbs.slice(0, index + 1);
|
||||
} else {
|
||||
this.state.breadcrumbs.push(path);
|
||||
}
|
||||
this.saveStateDebounced();
|
||||
}
|
||||
|
||||
addSearchTerm(term) {
|
||||
if (!this.state.searchHistory.includes(term)) {
|
||||
this.state.searchHistory.unshift(term);
|
||||
this.state.searchHistory = this.state.searchHistory.slice(0, 10);
|
||||
this.saveStateDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Navigation State
|
||||
const navState = new NavigationState();
|
||||
|
||||
// Tab Navigation - Meditation Stones
|
||||
const tabs = document.querySelectorAll('.stone-tab');
|
||||
const contents = document.querySelectorAll('.content-section');
|
||||
|
||||
function switchTab(tabName) {
|
||||
tabs.forEach(tab => {
|
||||
if (tab.dataset.tab === tabName) {
|
||||
tab.classList.add('active');
|
||||
} else {
|
||||
tab.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
contents.forEach(content => {
|
||||
if (content.dataset.content === tabName) {
|
||||
content.classList.add('active');
|
||||
} else {
|
||||
content.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
navState.updateTab(tabName);
|
||||
updateBreadcrumbsForTab(tabName);
|
||||
}
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
switchTab(tab.dataset.tab);
|
||||
});
|
||||
});
|
||||
|
||||
// Breadcrumb Navigation - Stepping Path
|
||||
function updateBreadcrumbsForTab(tabName) {
|
||||
const pathElement = document.getElementById('steppingPath');
|
||||
const newPath = ['root', 'meditation', tabName];
|
||||
|
||||
pathElement.innerHTML = newPath.map((step, index) => {
|
||||
const isLast = index === newPath.length - 1;
|
||||
const stone = `<a href="#" class="path-stone ${isLast ? 'current' : ''}" data-path="${step}">${step.charAt(0).toUpperCase() + step.slice(1)}</a>`;
|
||||
const divider = isLast ? '' : '<span class="path-divider">›</span>';
|
||||
return stone + divider;
|
||||
}).join('');
|
||||
|
||||
// Re-attach breadcrumb listeners
|
||||
attachBreadcrumbListeners();
|
||||
navState.updateBreadcrumbs(tabName);
|
||||
}
|
||||
|
||||
function attachBreadcrumbListeners() {
|
||||
document.querySelectorAll('.path-stone').forEach(stone => {
|
||||
stone.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const path = stone.dataset.path;
|
||||
if (path !== 'root' && path !== 'meditation') {
|
||||
switchTab(path);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Search Functionality - Mindful Exploration
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const searchResults = document.getElementById('searchResults');
|
||||
|
||||
const searchableContent = [
|
||||
{ title: 'Breathing Techniques', path: 'Meditation > Breathing', tab: 'breathing' },
|
||||
{ title: '4-7-8 Breathing Method', path: 'Meditation > Breathing > Techniques', tab: 'breathing' },
|
||||
{ title: 'Present Moment Practice', path: 'Meditation > Mindfulness', tab: 'mindfulness' },
|
||||
{ title: 'Walking Meditation', path: 'Meditation > Movement', tab: 'movement' },
|
||||
{ title: 'Tai Chi Basics', path: 'Meditation > Movement > Forms', tab: 'movement' },
|
||||
{ title: 'Journaling for Insight', path: 'Meditation > Reflection', tab: 'reflection' },
|
||||
{ title: 'Zen Teachings', path: 'Meditation > Wisdom', tab: 'wisdom' },
|
||||
{ title: 'Natural Teachers', path: 'Meditation > Wisdom > Nature', tab: 'wisdom' }
|
||||
];
|
||||
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const query = e.target.value.toLowerCase().trim();
|
||||
|
||||
if (query.length > 2) {
|
||||
const results = searchableContent.filter(item =>
|
||||
item.title.toLowerCase().includes(query) ||
|
||||
item.path.toLowerCase().includes(query)
|
||||
);
|
||||
|
||||
if (results.length > 0) {
|
||||
displaySearchResults(results);
|
||||
searchResults.classList.add('visible');
|
||||
} else {
|
||||
searchResults.classList.remove('visible');
|
||||
}
|
||||
} else {
|
||||
searchResults.classList.remove('visible');
|
||||
}
|
||||
});
|
||||
|
||||
searchInput.addEventListener('blur', (e) => {
|
||||
setTimeout(() => {
|
||||
searchResults.classList.remove('visible');
|
||||
if (e.target.value.trim()) {
|
||||
navState.addSearchTerm(e.target.value.trim());
|
||||
}
|
||||
}, 200);
|
||||
});
|
||||
|
||||
function displaySearchResults(results) {
|
||||
searchResults.innerHTML = results.map(result => `
|
||||
<div class="search-result" data-tab="${result.tab}">
|
||||
<div class="search-result-title">${result.title}</div>
|
||||
<div class="search-result-path">${result.path}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Add click handlers to results
|
||||
searchResults.querySelectorAll('.search-result').forEach(result => {
|
||||
result.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
const tab = result.dataset.tab;
|
||||
switchTab(tab);
|
||||
searchInput.value = '';
|
||||
searchResults.classList.remove('visible');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Quick Actions - Zen Shortcuts
|
||||
const actionButtons = document.querySelectorAll('.action-stone');
|
||||
|
||||
actionButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const action = button.dataset.action;
|
||||
|
||||
switch(action) {
|
||||
case 'home':
|
||||
switchTab('breathing');
|
||||
searchInput.value = '';
|
||||
break;
|
||||
case 'bookmark':
|
||||
const currentTab = navState.state.currentTab;
|
||||
if (!navState.state.bookmarks.includes(currentTab)) {
|
||||
navState.state.bookmarks.push(currentTab);
|
||||
navState.saveState();
|
||||
button.style.color = 'var(--zen-moss)';
|
||||
}
|
||||
break;
|
||||
case 'settings':
|
||||
// In a real app, this would open settings
|
||||
alert('Settings would open here - adjust your meditation preferences');
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Restore state on load
|
||||
window.addEventListener('load', () => {
|
||||
// Restore last tab
|
||||
if (navState.state.currentTab) {
|
||||
switchTab(navState.state.currentTab);
|
||||
}
|
||||
|
||||
// Highlight bookmarked items
|
||||
if (navState.state.bookmarks.includes(navState.state.currentTab)) {
|
||||
const bookmarkButton = document.querySelector('[data-action="bookmark"]');
|
||||
bookmarkButton.style.color = 'var(--zen-moss)';
|
||||
}
|
||||
|
||||
// Initialize breadcrumb listeners
|
||||
attachBreadcrumbListeners();
|
||||
});
|
||||
|
||||
// Smooth scroll for tab container
|
||||
const tabContainer = document.querySelector('.meditation-stones');
|
||||
let isDown = false;
|
||||
let startX;
|
||||
let scrollLeft;
|
||||
|
||||
tabContainer.addEventListener('mousedown', (e) => {
|
||||
if (e.target.classList.contains('stone-tab')) return;
|
||||
isDown = true;
|
||||
startX = e.pageX - tabContainer.offsetLeft;
|
||||
scrollLeft = tabContainer.scrollLeft;
|
||||
});
|
||||
|
||||
tabContainer.addEventListener('mouseleave', () => {
|
||||
isDown = false;
|
||||
});
|
||||
|
||||
tabContainer.addEventListener('mouseup', () => {
|
||||
isDown = false;
|
||||
});
|
||||
|
||||
tabContainer.addEventListener('mousemove', (e) => {
|
||||
if (!isDown) return;
|
||||
e.preventDefault();
|
||||
const x = e.pageX - tabContainer.offsetLeft;
|
||||
const walk = (x - startX) * 2;
|
||||
tabContainer.scrollLeft = scrollLeft - walk;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,896 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Handcrafted Paper Content Card</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Patrick+Hand&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Patrick Hand', cursive;
|
||||
background: linear-gradient(135deg, #f5f1e8 0%, #fafaf0 100%);
|
||||
min-height: 100vh;
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Wood texture background */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-image:
|
||||
repeating-linear-gradient(90deg, rgba(139, 90, 43, 0.03) 0px, transparent 1px, transparent 2px, rgba(139, 90, 43, 0.03) 3px),
|
||||
repeating-linear-gradient(0deg, rgba(139, 90, 43, 0.03) 0px, transparent 1px, transparent 2px, rgba(139, 90, 43, 0.03) 3px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Kalam', cursive;
|
||||
font-size: 2.5rem;
|
||||
color: #4a3f36;
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h1::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 200px;
|
||||
height: 2px;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 2"><path d="M0,1 Q25,0 50,1 T100,1" stroke="%234a3f36" fill="none" stroke-width="0.5"/></svg>');
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2.5rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.paper-card {
|
||||
position: relative;
|
||||
background: #fefef4;
|
||||
padding: 1.5rem;
|
||||
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
|
||||
transition: transform 0.3s ease, filter 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.paper-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -2px;
|
||||
background: #fefef4;
|
||||
z-index: -1;
|
||||
clip-path: polygon(
|
||||
0% 2%, 2% 0%, 15% 1%, 30% 0%, 45% 2%, 60% 0%, 75% 1%, 90% 0%, 100% 2%,
|
||||
99% 15%, 100% 30%, 98% 45%, 100% 60%, 99% 75%, 100% 90%, 98% 100%,
|
||||
85% 99%, 70% 100%, 55% 98%, 40% 100%, 25% 99%, 10% 100%, 0% 98%,
|
||||
1% 85%, 0% 70%, 2% 55%, 0% 40%, 1% 25%, 0% 10%
|
||||
);
|
||||
}
|
||||
|
||||
.paper-card:hover {
|
||||
transform: translateY(-4px) rotate(0.5deg);
|
||||
filter: drop-shadow(0 8px 16px rgba(0,0,0,0.15));
|
||||
}
|
||||
|
||||
/* Paper texture overlay */
|
||||
.paper-texture {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.3;
|
||||
background-image:
|
||||
repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(0,0,0,.02) 35px, rgba(0,0,0,.02) 70px),
|
||||
repeating-linear-gradient(-45deg, transparent, transparent 35px, rgba(0,0,0,.02) 35px, rgba(0,0,0,.02) 70px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Bookmark ribbon */
|
||||
.bookmark-ribbon {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: 20px;
|
||||
width: 30px;
|
||||
height: 60px;
|
||||
background: #d32f2f;
|
||||
clip-path: polygon(0 0, 100% 0, 100% 85%, 50% 100%, 0 85%);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.bookmark-ribbon::before {
|
||||
content: '★';
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.bookmark-ribbon.active {
|
||||
background: #fbc02d;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
|
||||
/* Content preview */
|
||||
.card-preview {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: #f5f5dc;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-bottom: 1rem;
|
||||
clip-path: polygon(
|
||||
0% 1%, 1% 0%, 99% 0%, 100% 1%,
|
||||
100% 99%, 99% 100%, 1% 100%, 0% 99%
|
||||
);
|
||||
}
|
||||
|
||||
.card-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
filter: sepia(0.1) contrast(0.9);
|
||||
}
|
||||
|
||||
.sketch-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M10,10 Q50,5 90,10 M10,90 Q50,95 90,90 M10,10 Q5,50 10,90 M90,10 Q95,50 90,90" stroke="%23000" fill="none" stroke-width="0.2" opacity="0.3"/></svg>');
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-family: 'Kalam', cursive;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
color: #5a6c7d;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Handwritten metadata */
|
||||
.metadata {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: #7a8694;
|
||||
}
|
||||
|
||||
.metadata span {
|
||||
position: relative;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.metadata span::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 1"><path d="M0,0.5 Q5,0 10,0.5 T20,0.5" stroke="%237a8694" fill="none" stroke-width="0.5"/></svg>');
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Paper cutout action buttons */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.paper-button {
|
||||
padding: 0.7rem 1.2rem;
|
||||
background: #fefef4;
|
||||
border: none;
|
||||
font-family: 'Patrick Hand', cursive;
|
||||
font-size: 1rem;
|
||||
color: #4a3f36;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.1));
|
||||
}
|
||||
|
||||
.paper-button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
background: inherit;
|
||||
z-index: -1;
|
||||
clip-path: polygon(
|
||||
0% 5%, 5% 0%, 95% 0%, 100% 5%,
|
||||
100% 95%, 95% 100%, 5% 100%, 0% 95%
|
||||
);
|
||||
}
|
||||
|
||||
.paper-button:hover {
|
||||
transform: translateY(-2px) rotate(-1deg);
|
||||
filter: drop-shadow(4px 4px 8px rgba(0,0,0,0.15));
|
||||
}
|
||||
|
||||
.paper-button:active {
|
||||
transform: translateY(0) rotate(0);
|
||||
filter: drop-shadow(1px 1px 2px rgba(0,0,0,0.1));
|
||||
}
|
||||
|
||||
.paper-button.primary {
|
||||
background: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.paper-button.secondary {
|
||||
background: #fff3e0;
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
/* Share paper airplane */
|
||||
.share-button {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.paper-airplane {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.paper-airplane.flying {
|
||||
animation: fly-away 1.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fly-away {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) rotate(0deg) scale(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate(200px, -200px) rotate(45deg) scale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Modal as unfolding paper */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-overlay.active {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.paper-modal {
|
||||
background: #fefef4;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
position: relative;
|
||||
filter: drop-shadow(0 10px 30px rgba(0,0,0,0.3));
|
||||
transform: scale(0.7) rotateX(90deg);
|
||||
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform-style: preserve-3d;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
.modal-overlay.active .paper-modal {
|
||||
transform: scale(1) rotateX(0);
|
||||
}
|
||||
|
||||
.paper-modal::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -3px;
|
||||
background: #fefef4;
|
||||
z-index: -1;
|
||||
clip-path: polygon(
|
||||
0% 3%, 3% 0%, 20% 2%, 40% 0%, 60% 2%, 80% 0%, 97% 3%, 100% 3%,
|
||||
98% 20%, 100% 40%, 98% 60%, 100% 80%, 97% 97%, 97% 100%,
|
||||
80% 98%, 60% 100%, 40% 98%, 20% 100%, 3% 97%, 0% 97%,
|
||||
2% 80%, 0% 60%, 2% 40%, 0% 20%
|
||||
);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 2.5rem;
|
||||
overflow-y: auto;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-family: 'Kalam', cursive;
|
||||
font-size: 2rem;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #7a8694;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
font-family: 'Patrick Hand', cursive;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
line-height: 1.8;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.modal-body p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Fold lines on modal */
|
||||
.fold-lines {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.1;
|
||||
background-image:
|
||||
linear-gradient(90deg, #000 1px, transparent 1px),
|
||||
linear-gradient(0deg, #000 1px, transparent 1px);
|
||||
background-size: 200px 200px;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/* Coffee stain decoration */
|
||||
.coffee-stain {
|
||||
position: absolute;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(139, 90, 43, 0.1) 0%, rgba(139, 90, 43, 0.05) 50%, transparent 70%);
|
||||
top: 20px;
|
||||
right: 30px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
/* Pencil sketch decorations */
|
||||
.pencil-sketch {
|
||||
position: absolute;
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sketch-1 {
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><circle cx="25" cy="25" r="20" fill="none" stroke="%234a3f36" stroke-width="0.5" stroke-dasharray="1,2"/><path d="M15,25 Q25,15 35,25 T25,35" fill="none" stroke="%234a3f36" stroke-width="0.5"/></svg>');
|
||||
}
|
||||
|
||||
.sketch-2 {
|
||||
top: 50%;
|
||||
right: -20px;
|
||||
width: 40px;
|
||||
height: 100px;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 100"><path d="M20,10 Q10,30 20,50 T20,90" fill="none" stroke="%234a3f36" stroke-width="0.5"/><circle cx="20" cy="30" r="5" fill="none" stroke="%234a3f36" stroke-width="0.5"/><circle cx="20" cy="70" r="5" fill="none" stroke="%234a3f36" stroke-width="0.5"/></svg>');
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
.loading-sketch {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 0.5rem;
|
||||
animation: sketch-draw 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes sketch-draw {
|
||||
0%, 100% {
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M5,10 L10,10" stroke="%234a3f36" fill="none" stroke-width="1"/></svg>');
|
||||
}
|
||||
50% {
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M5,10 L15,10" stroke="%234a3f36" fill="none" stroke-width="1"/></svg>');
|
||||
}
|
||||
}
|
||||
|
||||
/* Share popup */
|
||||
.share-popup {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #fefef4;
|
||||
padding: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.share-popup.active {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.share-popup::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
background: #fefef4;
|
||||
z-index: -1;
|
||||
clip-path: polygon(
|
||||
0% 5%, 5% 0%, 95% 0%, 100% 5%,
|
||||
100% 85%, 90% 90%, 50% 100%, 10% 90%, 0% 85%
|
||||
);
|
||||
}
|
||||
|
||||
.share-options {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.share-option {
|
||||
padding: 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.2rem;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.share-option:hover {
|
||||
transform: scale(1.2) rotate(5deg);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Content Card - Handcrafted Paper Theme</h1>
|
||||
|
||||
<div class="content-grid">
|
||||
<!-- Article Card -->
|
||||
<div class="paper-card" data-id="1">
|
||||
<div class="paper-texture"></div>
|
||||
<div class="bookmark-ribbon" onclick="toggleBookmark(event, this)"></div>
|
||||
<div class="coffee-stain"></div>
|
||||
|
||||
<div class="card-preview">
|
||||
<div class="card-image">
|
||||
<img src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 200'><rect fill='%23e8dcc6' width='400' height='200'/><text x='50%' y='50%' text-anchor='middle' dy='.3em' fill='%234a3f36' font-family='Patrick Hand' font-size='24'>Mountain Sketch</text><path d='M50,150 L150,50 L250,150 M200,100 L300,150' stroke='%234a3f36' fill='none' stroke-width='2'/><circle cx='320' cy='40' r='20' fill='none' stroke='%234a3f36' stroke-width='1.5'/></svg>" alt="Mountain landscape">
|
||||
<div class="sketch-overlay"></div>
|
||||
</div>
|
||||
<h3 class="card-title">The Art of Mountain Sketching</h3>
|
||||
<p class="card-description">Exploring the timeless techniques of capturing mountain landscapes with pencil and paper...</p>
|
||||
</div>
|
||||
|
||||
<div class="metadata">
|
||||
<span>📅 March 15, 2024</span>
|
||||
<span>✏️ 5 min read</span>
|
||||
<span>👁️ 1.2k views</span>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="paper-button primary" onclick="openModal(1)">
|
||||
Read More
|
||||
</button>
|
||||
<button class="paper-button secondary share-button" onclick="toggleShare(event, this)">
|
||||
Share ✈️
|
||||
<svg class="paper-airplane" viewBox="0 0 20 20">
|
||||
<path d="M2,2 L18,10 L2,18 L5,10 Z" fill="#e65100"/>
|
||||
</svg>
|
||||
<div class="share-popup">
|
||||
<div class="share-options">
|
||||
<button class="share-option" onclick="shareVia('twitter')">🐦</button>
|
||||
<button class="share-option" onclick="shareVia('facebook')">📘</button>
|
||||
<button class="share-option" onclick="shareVia('email')">📧</button>
|
||||
<button class="share-option" onclick="shareVia('copy')">📋</button>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pencil-sketch sketch-1"></div>
|
||||
</div>
|
||||
|
||||
<!-- Recipe Card -->
|
||||
<div class="paper-card" data-id="2">
|
||||
<div class="paper-texture"></div>
|
||||
<div class="bookmark-ribbon" onclick="toggleBookmark(event, this)"></div>
|
||||
|
||||
<div class="card-preview">
|
||||
<div class="card-image">
|
||||
<img src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 200'><rect fill='%23f5e6d3' width='400' height='200'/><text x='50%' y='30%' text-anchor='middle' dy='.3em' fill='%234a3f36' font-family='Patrick Hand' font-size='24'>Grandma's Recipe</text><circle cx='200' cy='120' r='40' fill='none' stroke='%234a3f36' stroke-width='2'/><path d='M160,120 Q200,80 240,120' fill='none' stroke='%234a3f36' stroke-width='1.5'/><circle cx='180' cy='110' r='3' fill='%234a3f36'/><circle cx='220' cy='110' r='3' fill='%234a3f36'/></svg>" alt="Recipe illustration">
|
||||
<div class="sketch-overlay"></div>
|
||||
</div>
|
||||
<h3 class="card-title">Handwritten Apple Pie Recipe</h3>
|
||||
<p class="card-description">A treasured family recipe passed down through generations, written on aged paper...</p>
|
||||
</div>
|
||||
|
||||
<div class="metadata">
|
||||
<span>🥧 Dessert</span>
|
||||
<span>⏱️ 2 hours</span>
|
||||
<span>⭐ 4.9 rating</span>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="paper-button primary" onclick="openModal(2)">
|
||||
View Recipe
|
||||
</button>
|
||||
<button class="paper-button secondary share-button" onclick="toggleShare(event, this)">
|
||||
Share ✈️
|
||||
<svg class="paper-airplane" viewBox="0 0 20 20">
|
||||
<path d="M2,2 L18,10 L2,18 L5,10 Z" fill="#e65100"/>
|
||||
</svg>
|
||||
<div class="share-popup">
|
||||
<div class="share-options">
|
||||
<button class="share-option" onclick="shareVia('twitter')">🐦</button>
|
||||
<button class="share-option" onclick="shareVia('facebook')">📘</button>
|
||||
<button class="share-option" onclick="shareVia('email')">📧</button>
|
||||
<button class="share-option" onclick="shareVia('copy')">📋</button>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pencil-sketch sketch-2"></div>
|
||||
</div>
|
||||
|
||||
<!-- Journal Entry Card -->
|
||||
<div class="paper-card" data-id="3">
|
||||
<div class="paper-texture"></div>
|
||||
<div class="bookmark-ribbon active" onclick="toggleBookmark(event, this)"></div>
|
||||
|
||||
<div class="card-preview">
|
||||
<div class="card-image">
|
||||
<img src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 200'><rect fill='%23ede4d3' width='400' height='200'/><text x='50%' y='30%' text-anchor='middle' dy='.3em' fill='%234a3f36' font-family='Patrick Hand' font-size='20'>Travel Journal</text><path d='M100,100 Q150,80 200,100 T300,100' stroke='%234a3f36' fill='none' stroke-width='1' stroke-dasharray='2,3'/><path d='M150,130 L170,110 L190,120 L210,105 L230,115 L250,100' stroke='%234a3f36' fill='none' stroke-width='1.5'/></svg>" alt="Journal sketch">
|
||||
<div class="sketch-overlay"></div>
|
||||
</div>
|
||||
<h3 class="card-title">Adventures in Tuscany</h3>
|
||||
<p class="card-description">Hand-drawn maps and stories from a summer journey through Italian countryside...</p>
|
||||
</div>
|
||||
|
||||
<div class="metadata">
|
||||
<span>📍 Travel</span>
|
||||
<span>📖 8 pages</span>
|
||||
<span>💭 32 comments</span>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="paper-button primary" onclick="openModal(3)">
|
||||
Open Journal
|
||||
</button>
|
||||
<button class="paper-button secondary share-button" onclick="toggleShare(event, this)">
|
||||
Share ✈️
|
||||
<svg class="paper-airplane" viewBox="0 0 20 20">
|
||||
<path d="M2,2 L18,10 L2,18 L5,10 Z" fill="#e65100"/>
|
||||
</svg>
|
||||
<div class="share-popup">
|
||||
<div class="share-options">
|
||||
<button class="share-option" onclick="shareVia('twitter')">🐦</button>
|
||||
<button class="share-option" onclick="shareVia('facebook')">📘</button>
|
||||
<button class="share-option" onclick="shareVia('email')">📧</button>
|
||||
<button class="share-option" onclick="shareVia('copy')">📋</button>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Modal Overlay -->
|
||||
<div class="modal-overlay" onclick="closeModal(event)">
|
||||
<div class="paper-modal" onclick="event.stopPropagation()">
|
||||
<div class="fold-lines"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Content Title</h2>
|
||||
<button class="close-button" onclick="closeModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Content will be dynamically inserted -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Content data
|
||||
const contentData = {
|
||||
1: {
|
||||
title: "The Art of Mountain Sketching",
|
||||
content: `
|
||||
<p>There's something magical about sitting on a mountainside with nothing but a sketchbook and pencil. The rough texture of handmade paper beneath your fingers connects you to centuries of artists who've attempted to capture nature's grandeur.</p>
|
||||
|
||||
<p>I learned this technique from an old artist in the Alps, who showed me how to use quick, confident strokes to suggest the massive scale of mountain peaks. "Don't try to draw every rock," he said, his weathered hands moving the pencil with practiced ease. "Let the paper breathe."</p>
|
||||
|
||||
<p>The key is in the observation. Watch how shadows play across the ridges at different times of day. Notice how atmospheric perspective makes distant peaks appear lighter, almost ghostlike. These subtleties, captured in graphite on paper, can convey more emotion than any photograph.</p>
|
||||
|
||||
<p>My favorite papers for mountain sketching are those with a bit of tooth - rough enough to catch the graphite and create texture, but smooth enough for fine details. I often carry several weights, switching between them as the drawing develops.</p>
|
||||
|
||||
<p>Remember, the goal isn't perfection. It's about capturing a moment, a feeling, the essence of standing small before nature's monuments. Let your lines be loose, your shading suggestive. The best mountain sketches feel alive, as if the wind might blow through them at any moment.</p>
|
||||
`
|
||||
},
|
||||
2: {
|
||||
title: "Grandma's Handwritten Apple Pie Recipe",
|
||||
content: `
|
||||
<p><em>Found tucked between the pages of her cookbook, written in fountain pen on paper now yellowed with age...</em></p>
|
||||
|
||||
<p><strong>For the Crust (makes enough for top and bottom):</strong><br>
|
||||
2½ cups flour<br>
|
||||
1 tsp salt<br>
|
||||
1 cup cold butter (Grandma's note: "must be COLD!")<br>
|
||||
¼ to ½ cup ice water</p>
|
||||
|
||||
<p><strong>For the Filling:</strong><br>
|
||||
6-8 tart apples (she preferred Granny Smith)<br>
|
||||
¾ cup sugar (adjust to taste)<br>
|
||||
2 tbsp flour<br>
|
||||
1 tsp cinnamon<br>
|
||||
¼ tsp nutmeg<br>
|
||||
Pinch of salt<br>
|
||||
2 tbsp butter</p>
|
||||
|
||||
<p><em>Her handwritten notes in the margin:</em> "The secret is in the apples - they should make you pucker just a little when raw. And always, always use real butter. None of that margarine nonsense."</p>
|
||||
|
||||
<p><strong>Instructions:</strong><br>
|
||||
Mix flour and salt. Cut in cold butter until mixture resembles coarse crumbs. Add ice water gradually until dough comes together. Divide in half, wrap, and chill for at least an hour.</p>
|
||||
|
||||
<p>Peel and slice apples thin. Toss with sugar, flour, and spices. Roll out bottom crust, fill with apples, dot with butter. Cover with top crust, seal edges, cut vents.</p>
|
||||
|
||||
<p>Bake at 425°F for 15 minutes, then reduce to 350°F for 35-45 minutes until golden brown. <em>(Another note: "If the edges brown too quick, cover with foil")</em></p>
|
||||
|
||||
<p><em>At the bottom, in shaky handwriting:</em> "Made this for your grandfather every Sunday. Now it's your turn to carry on the tradition. All my love, Grandma"</p>
|
||||
`
|
||||
},
|
||||
3: {
|
||||
title: "Adventures in Tuscany - Travel Journal",
|
||||
content: `
|
||||
<p><strong>Day 1 - Arrival in Florence</strong><br>
|
||||
<em>Sketched from the Piazzale Michelangelo at sunset</em></p>
|
||||
|
||||
<p>The paper of this journal already feels different here - maybe it's the Tuscan air, or maybe it's just my imagination. The city spreads below like a Renaissance painting come to life. Terra cotta roofs catch the golden hour light, and I try to capture it with quick pencil strokes.</p>
|
||||
|
||||
<p><strong>Day 3 - The Hills of Chianti</strong><br>
|
||||
<em>Wine stains on this page are intentional... mostly</em></p>
|
||||
|
||||
<p>Spent the morning sketching vineyards. The way the vines create these perfect lines across the hillsides - it's like nature's own ruled paper. Met an old vintner who laughed at my attempts to draw his ancient olive trees. "You cannot draw age," he said, "you must feel it." I think I understand.</p>
|
||||
|
||||
<p><strong>Day 5 - Siena's Secret Gardens</strong><br>
|
||||
<em>Pressed flowers between these pages</em></p>
|
||||
|
||||
<p>Found a hidden garden behind the cathedral. The kind of place that makes you want to fill every page with drawings. Stone benches worn smooth by centuries, fountains singing softly, cats lounging in patches of sun. My sketches can't capture the smell of jasmine or the sound of church bells, but they try.</p>
|
||||
|
||||
<p><strong>Day 8 - The Coast at Cinque Terre</strong><br>
|
||||
<em>Salt spray has warped these pages slightly</em></p>
|
||||
|
||||
<p>Different paper would have been better for the coast - this handmade stock doesn't love the humidity. But there's something perfect about the way the moisture makes my ink lines bleed slightly, like the boundaries between sea and sky in the morning mist.</p>
|
||||
|
||||
<p><strong>Last Day - Reflections</strong><br>
|
||||
This journal is fuller than just sketches and words. It holds pressed flowers, wine stains, smudges of local soil, even a few tears of joy. The paper has absorbed this journey, becoming part of the story itself. That's the magic of keeping a handwritten travel journal - it becomes an artifact of the adventure, not just a record of it.</p>
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle bookmark
|
||||
function toggleBookmark(event, element) {
|
||||
event.stopPropagation();
|
||||
element.classList.toggle('active');
|
||||
|
||||
const isActive = element.classList.contains('active');
|
||||
const cardTitle = element.closest('.paper-card').querySelector('.card-title').textContent;
|
||||
|
||||
if (isActive) {
|
||||
showNotification(`"${cardTitle}" added to bookmarks`);
|
||||
} else {
|
||||
showNotification(`"${cardTitle}" removed from bookmarks`);
|
||||
}
|
||||
}
|
||||
|
||||
// Open modal
|
||||
function openModal(contentId) {
|
||||
const modal = document.querySelector('.modal-overlay');
|
||||
const modalTitle = modal.querySelector('.modal-title');
|
||||
const modalBody = modal.querySelector('.modal-body');
|
||||
|
||||
const content = contentData[contentId];
|
||||
modalTitle.textContent = content.title;
|
||||
modalBody.innerHTML = content.content;
|
||||
|
||||
modal.classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
// Close modal
|
||||
function closeModal(event) {
|
||||
if (event && event.target !== event.currentTarget) return;
|
||||
|
||||
const modal = document.querySelector('.modal-overlay');
|
||||
modal.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Toggle share popup
|
||||
function toggleShare(event, button) {
|
||||
event.stopPropagation();
|
||||
|
||||
// Close all other share popups
|
||||
document.querySelectorAll('.share-popup').forEach(popup => {
|
||||
if (popup !== button.querySelector('.share-popup')) {
|
||||
popup.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
const popup = button.querySelector('.share-popup');
|
||||
popup.classList.toggle('active');
|
||||
}
|
||||
|
||||
// Share functionality
|
||||
function shareVia(platform) {
|
||||
event.stopPropagation();
|
||||
|
||||
const card = event.target.closest('.paper-card');
|
||||
const title = card.querySelector('.card-title').textContent;
|
||||
const shareButton = card.querySelector('.share-button');
|
||||
const airplane = shareButton.querySelector('.paper-airplane');
|
||||
|
||||
// Close popup
|
||||
const popup = shareButton.querySelector('.share-popup');
|
||||
popup.classList.remove('active');
|
||||
|
||||
// Animate paper airplane
|
||||
airplane.classList.add('flying');
|
||||
setTimeout(() => {
|
||||
airplane.classList.remove('flying');
|
||||
}, 1500);
|
||||
|
||||
// Show notification
|
||||
const messages = {
|
||||
twitter: `Shared "${title}" on Twitter`,
|
||||
facebook: `Shared "${title}" on Facebook`,
|
||||
email: `Email composed for "${title}"`,
|
||||
copy: `Link copied for "${title}"`
|
||||
};
|
||||
|
||||
showNotification(messages[platform]);
|
||||
}
|
||||
|
||||
// Show notification
|
||||
function showNotification(message) {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = 'notification';
|
||||
notification.textContent = message;
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #fefef4;
|
||||
padding: 1rem 2rem;
|
||||
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
|
||||
z-index: 2000;
|
||||
font-family: 'Patrick Hand', cursive;
|
||||
color: #4a3f36;
|
||||
clip-path: polygon(
|
||||
0% 5%, 5% 0%, 95% 0%, 100% 5%,
|
||||
100% 95%, 95% 100%, 5% 100%, 0% 95%
|
||||
);
|
||||
animation: slide-up 0.3s ease-out;
|
||||
`;
|
||||
|
||||
// Add animation keyframes if not already present
|
||||
if (!document.querySelector('#notification-styles')) {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'notification-styles';
|
||||
style.textContent = `
|
||||
@keyframes slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.style.animation = 'slide-up 0.3s ease-out reverse';
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// Close share popups when clicking outside
|
||||
document.addEventListener('click', () => {
|
||||
document.querySelectorAll('.share-popup.active').forEach(popup => {
|
||||
popup.classList.remove('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Add hover effect to cards for subtle animation
|
||||
document.querySelectorAll('.paper-card').forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = `translateY(-4px) rotate(${Math.random() * 2 - 1}deg)`;
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.transform = '';
|
||||
});
|
||||
});
|
||||
|
||||
// Keyboard navigation
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
closeModal();
|
||||
document.querySelectorAll('.share-popup.active').forEach(popup => {
|
||||
popup.classList.remove('active');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,853 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Digital Minimalism Form Wizard</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary: #000000;
|
||||
--secondary: #666666;
|
||||
--tertiary: #999999;
|
||||
--background: #ffffff;
|
||||
--surface: #fafafa;
|
||||
--accent: #0066ff;
|
||||
--error: #ff3333;
|
||||
--success: #00cc88;
|
||||
--border: #e5e5e5;
|
||||
--shadow: rgba(0, 0, 0, 0.05);
|
||||
--transition: 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: var(--background);
|
||||
color: var(--primary);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 3rem;
|
||||
text-align: center;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.hybrid-component {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
padding: 3rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Progress Line - Minimal */
|
||||
.progress-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
width: 0%;
|
||||
transition: width var(--transition);
|
||||
}
|
||||
|
||||
/* Step Indicators - Geometric Dots */
|
||||
.step-indicators {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.step-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: var(--background);
|
||||
border: 2px solid var(--border);
|
||||
transition: all var(--transition);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.step-indicator.active .step-dot {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.step-indicator.completed .step-dot {
|
||||
background: var(--success);
|
||||
border-color: var(--success);
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--tertiary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
transition: color var(--transition);
|
||||
}
|
||||
|
||||
.step-indicator.active .step-label,
|
||||
.step-indicator.completed .step-label {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Step Connection Line */
|
||||
.step-connection {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Form Steps */
|
||||
.form-steps {
|
||||
min-height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-step {
|
||||
display: none;
|
||||
animation: fadeIn var(--transition) ease-out;
|
||||
}
|
||||
|
||||
.form-step.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Form Groups */
|
||||
.form-group {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
color: var(--secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 0.75rem 0;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: transparent;
|
||||
transition: border-color var(--transition);
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.form-input.error {
|
||||
border-color: var(--error);
|
||||
}
|
||||
|
||||
/* Validation Messages */
|
||||
.validation-message {
|
||||
font-size: 0.75rem;
|
||||
margin-top: 0.25rem;
|
||||
height: 1rem;
|
||||
color: var(--error);
|
||||
opacity: 0;
|
||||
transform: translateY(-5px);
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.validation-message.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Radio Groups */
|
||||
.radio-group {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.radio-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-input {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 50%;
|
||||
margin-right: 0.5rem;
|
||||
position: relative;
|
||||
transition: border-color var(--transition);
|
||||
}
|
||||
|
||||
.radio-option input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.radio-option input[type="radio"]:checked + .radio-input {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.radio-option input[type="radio"]:checked + .radio-input::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.form-navigation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 3rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition);
|
||||
font-family: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-button:hover {
|
||||
border-color: var(--primary);
|
||||
background: var(--primary);
|
||||
color: var(--background);
|
||||
}
|
||||
|
||||
.nav-button.primary {
|
||||
background: var(--primary);
|
||||
color: var(--background);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.nav-button.primary:hover {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.nav-button:disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.nav-button:disabled:hover {
|
||||
background: none;
|
||||
color: var(--primary);
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
/* Arrow Icons */
|
||||
.arrow {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.arrow::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-right: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
.arrow.left::before {
|
||||
transform: translate(-50%, -50%) rotate(135deg);
|
||||
}
|
||||
|
||||
/* Save State Indicator */
|
||||
.save-state {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--tertiary);
|
||||
opacity: 0;
|
||||
transition: opacity var(--transition);
|
||||
}
|
||||
|
||||
.save-state.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.save-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--success);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Summary Step */
|
||||
.summary-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--tertiary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
font-size: 1rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Success State */
|
||||
.success-message {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
animation: fadeIn var(--transition) ease-out;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0 auto 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.success-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 25%;
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
border-right: 2px solid var(--success);
|
||||
border-bottom: 2px solid var(--success);
|
||||
transform: translateY(-60%) rotate(45deg);
|
||||
}
|
||||
|
||||
.success-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid var(--success);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 640px) {
|
||||
.hybrid-component {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
padding: 0.625rem 1.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Form Wizard - Digital Minimalism Theme</h1>
|
||||
|
||||
<div class="hybrid-component">
|
||||
<!-- Progress Line -->
|
||||
<div class="progress-line">
|
||||
<div class="progress-fill" id="progressFill"></div>
|
||||
</div>
|
||||
|
||||
<!-- Save State Indicator -->
|
||||
<div class="save-state" id="saveState">
|
||||
<div class="save-indicator"></div>
|
||||
<span>Auto-saved</span>
|
||||
</div>
|
||||
|
||||
<!-- Step Indicators -->
|
||||
<div class="step-indicators">
|
||||
<div class="step-connection"></div>
|
||||
<div class="step-indicator active" data-step="1">
|
||||
<div class="step-dot"></div>
|
||||
<span class="step-label">Personal</span>
|
||||
</div>
|
||||
<div class="step-indicator" data-step="2">
|
||||
<div class="step-dot"></div>
|
||||
<span class="step-label">Account</span>
|
||||
</div>
|
||||
<div class="step-indicator" data-step="3">
|
||||
<div class="step-dot"></div>
|
||||
<span class="step-label">Preferences</span>
|
||||
</div>
|
||||
<div class="step-indicator" data-step="4">
|
||||
<div class="step-dot"></div>
|
||||
<span class="step-label">Review</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Steps -->
|
||||
<div class="form-steps">
|
||||
<!-- Step 1: Personal Information -->
|
||||
<div class="form-step active" data-step="1">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="firstName">First Name</label>
|
||||
<input type="text" class="form-input" id="firstName" required>
|
||||
<div class="validation-message" id="firstNameError">Please enter your first name</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="lastName">Last Name</label>
|
||||
<input type="text" class="form-input" id="lastName" required>
|
||||
<div class="validation-message" id="lastNameError">Please enter your last name</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">Email Address</label>
|
||||
<input type="email" class="form-input" id="email" required>
|
||||
<div class="validation-message" id="emailError">Please enter a valid email address</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Account Setup -->
|
||||
<div class="form-step" data-step="2">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Username</label>
|
||||
<input type="text" class="form-input" id="username" required>
|
||||
<div class="validation-message" id="usernameError">Username must be at least 3 characters</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<input type="password" class="form-input" id="password" required>
|
||||
<div class="validation-message" id="passwordError">Password must be at least 8 characters</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="confirmPassword">Confirm Password</label>
|
||||
<input type="password" class="form-input" id="confirmPassword" required>
|
||||
<div class="validation-message" id="confirmPasswordError">Passwords do not match</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Preferences -->
|
||||
<div class="form-step" data-step="3">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Notification Frequency</label>
|
||||
<div class="radio-group">
|
||||
<label class="radio-option">
|
||||
<input type="radio" name="notifications" value="daily" checked>
|
||||
<div class="radio-input"></div>
|
||||
<span>Daily</span>
|
||||
</label>
|
||||
<label class="radio-option">
|
||||
<input type="radio" name="notifications" value="weekly">
|
||||
<div class="radio-input"></div>
|
||||
<span>Weekly</span>
|
||||
</label>
|
||||
<label class="radio-option">
|
||||
<input type="radio" name="notifications" value="never">
|
||||
<div class="radio-input"></div>
|
||||
<span>Never</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Theme Preference</label>
|
||||
<div class="radio-group">
|
||||
<label class="radio-option">
|
||||
<input type="radio" name="theme" value="light" checked>
|
||||
<div class="radio-input"></div>
|
||||
<span>Light</span>
|
||||
</label>
|
||||
<label class="radio-option">
|
||||
<input type="radio" name="theme" value="dark">
|
||||
<div class="radio-input"></div>
|
||||
<span>Dark</span>
|
||||
</label>
|
||||
<label class="radio-option">
|
||||
<input type="radio" name="theme" value="auto">
|
||||
<div class="radio-input"></div>
|
||||
<span>Auto</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Review -->
|
||||
<div class="form-step" data-step="4">
|
||||
<div class="summary-section">
|
||||
<div class="summary-label">Personal Information</div>
|
||||
<div class="summary-value" id="summaryName">-</div>
|
||||
<div class="summary-value" id="summaryEmail">-</div>
|
||||
</div>
|
||||
<div class="summary-section">
|
||||
<div class="summary-label">Account Details</div>
|
||||
<div class="summary-value" id="summaryUsername">-</div>
|
||||
</div>
|
||||
<div class="summary-section">
|
||||
<div class="summary-label">Preferences</div>
|
||||
<div class="summary-value" id="summaryNotifications">-</div>
|
||||
<div class="summary-value" id="summaryTheme">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Success State -->
|
||||
<div class="form-step" data-step="success">
|
||||
<div class="success-message">
|
||||
<div class="success-icon"></div>
|
||||
<h2 style="font-weight: 300; margin-bottom: 0.5rem;">Account Created</h2>
|
||||
<p style="color: var(--secondary);">Welcome to your minimal digital experience.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="form-navigation">
|
||||
<button class="nav-button" id="prevBtn" disabled>
|
||||
<span class="arrow left"></span>
|
||||
Previous
|
||||
</button>
|
||||
<button class="nav-button primary" id="nextBtn">
|
||||
Next
|
||||
<span class="arrow"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Form Wizard State
|
||||
const formWizard = {
|
||||
currentStep: 1,
|
||||
totalSteps: 4,
|
||||
formData: {},
|
||||
saveTimeout: null
|
||||
};
|
||||
|
||||
// DOM Elements
|
||||
const progressFill = document.getElementById('progressFill');
|
||||
const saveState = document.getElementById('saveState');
|
||||
const prevBtn = document.getElementById('prevBtn');
|
||||
const nextBtn = document.getElementById('nextBtn');
|
||||
|
||||
// Initialize form data from localStorage
|
||||
function initializeFormData() {
|
||||
const savedData = localStorage.getItem('formWizardData');
|
||||
if (savedData) {
|
||||
formWizard.formData = JSON.parse(savedData);
|
||||
restoreFormData();
|
||||
}
|
||||
}
|
||||
|
||||
// Save form data
|
||||
function saveFormData() {
|
||||
// Collect current step data
|
||||
collectStepData();
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('formWizardData', JSON.stringify(formWizard.formData));
|
||||
|
||||
// Show save indicator
|
||||
saveState.classList.add('show');
|
||||
clearTimeout(formWizard.saveTimeout);
|
||||
formWizard.saveTimeout = setTimeout(() => {
|
||||
saveState.classList.remove('show');
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// Restore form data
|
||||
function restoreFormData() {
|
||||
// Restore all form fields
|
||||
Object.keys(formWizard.formData).forEach(key => {
|
||||
const element = document.getElementById(key);
|
||||
if (element) {
|
||||
if (element.type === 'radio') {
|
||||
const radio = document.querySelector(`input[name="${element.name}"][value="${formWizard.formData[key]}"]`);
|
||||
if (radio) radio.checked = true;
|
||||
} else {
|
||||
element.value = formWizard.formData[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Collect data from current step
|
||||
function collectStepData() {
|
||||
const currentStepElement = document.querySelector(`.form-step[data-step="${formWizard.currentStep}"]`);
|
||||
if (!currentStepElement) return;
|
||||
|
||||
// Text inputs
|
||||
currentStepElement.querySelectorAll('.form-input').forEach(input => {
|
||||
if (input.value) {
|
||||
formWizard.formData[input.id] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Radio inputs
|
||||
currentStepElement.querySelectorAll('input[type="radio"]:checked').forEach(radio => {
|
||||
formWizard.formData[radio.name] = radio.value;
|
||||
});
|
||||
}
|
||||
|
||||
// Validate current step
|
||||
function validateStep() {
|
||||
const currentStepElement = document.querySelector(`.form-step[data-step="${formWizard.currentStep}"]`);
|
||||
if (!currentStepElement) return true;
|
||||
|
||||
let isValid = true;
|
||||
|
||||
// Validate text inputs
|
||||
currentStepElement.querySelectorAll('.form-input[required]').forEach(input => {
|
||||
const errorElement = document.getElementById(input.id + 'Error');
|
||||
|
||||
if (!input.value.trim()) {
|
||||
input.classList.add('error');
|
||||
if (errorElement) errorElement.classList.add('show');
|
||||
isValid = false;
|
||||
} else if (input.type === 'email' && !isValidEmail(input.value)) {
|
||||
input.classList.add('error');
|
||||
if (errorElement) errorElement.classList.add('show');
|
||||
isValid = false;
|
||||
} else if (input.id === 'username' && input.value.length < 3) {
|
||||
input.classList.add('error');
|
||||
if (errorElement) errorElement.classList.add('show');
|
||||
isValid = false;
|
||||
} else if (input.id === 'password' && input.value.length < 8) {
|
||||
input.classList.add('error');
|
||||
if (errorElement) errorElement.classList.add('show');
|
||||
isValid = false;
|
||||
} else if (input.id === 'confirmPassword') {
|
||||
const password = document.getElementById('password').value;
|
||||
if (input.value !== password) {
|
||||
input.classList.add('error');
|
||||
if (errorElement) errorElement.classList.add('show');
|
||||
isValid = false;
|
||||
} else {
|
||||
input.classList.remove('error');
|
||||
if (errorElement) errorElement.classList.remove('show');
|
||||
}
|
||||
} else {
|
||||
input.classList.remove('error');
|
||||
if (errorElement) errorElement.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Email validation
|
||||
function isValidEmail(email) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
}
|
||||
|
||||
// Update progress
|
||||
function updateProgress() {
|
||||
const progress = ((formWizard.currentStep - 1) / formWizard.totalSteps) * 100;
|
||||
progressFill.style.width = progress + '%';
|
||||
}
|
||||
|
||||
// Update step indicators
|
||||
function updateStepIndicators() {
|
||||
document.querySelectorAll('.step-indicator').forEach((indicator, index) => {
|
||||
const stepNum = index + 1;
|
||||
indicator.classList.remove('active', 'completed');
|
||||
|
||||
if (stepNum < formWizard.currentStep) {
|
||||
indicator.classList.add('completed');
|
||||
} else if (stepNum === formWizard.currentStep) {
|
||||
indicator.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show step
|
||||
function showStep(stepNumber) {
|
||||
// Hide all steps
|
||||
document.querySelectorAll('.form-step').forEach(step => {
|
||||
step.classList.remove('active');
|
||||
});
|
||||
|
||||
// Show current step
|
||||
const currentStepElement = document.querySelector(`.form-step[data-step="${stepNumber}"]`);
|
||||
if (currentStepElement) {
|
||||
currentStepElement.classList.add('active');
|
||||
}
|
||||
|
||||
// Update navigation buttons
|
||||
prevBtn.disabled = stepNumber === 1;
|
||||
|
||||
if (stepNumber === formWizard.totalSteps) {
|
||||
nextBtn.textContent = 'Submit';
|
||||
nextBtn.innerHTML = 'Submit <span class="arrow"></span>';
|
||||
} else {
|
||||
nextBtn.textContent = 'Next';
|
||||
nextBtn.innerHTML = 'Next <span class="arrow"></span>';
|
||||
}
|
||||
|
||||
// Update progress and indicators
|
||||
updateProgress();
|
||||
updateStepIndicators();
|
||||
|
||||
// Update summary if on review step
|
||||
if (stepNumber === 4) {
|
||||
updateSummary();
|
||||
}
|
||||
}
|
||||
|
||||
// Update summary
|
||||
function updateSummary() {
|
||||
document.getElementById('summaryName').textContent =
|
||||
`${formWizard.formData.firstName || ''} ${formWizard.formData.lastName || ''}`;
|
||||
document.getElementById('summaryEmail').textContent =
|
||||
formWizard.formData.email || '-';
|
||||
document.getElementById('summaryUsername').textContent =
|
||||
`Username: ${formWizard.formData.username || '-'}`;
|
||||
document.getElementById('summaryNotifications').textContent =
|
||||
`Notifications: ${formWizard.formData.notifications || 'daily'}`;
|
||||
document.getElementById('summaryTheme').textContent =
|
||||
`Theme: ${formWizard.formData.theme || 'light'}`;
|
||||
}
|
||||
|
||||
// Next step
|
||||
function nextStep() {
|
||||
if (formWizard.currentStep === formWizard.totalSteps) {
|
||||
// Submit form
|
||||
if (validateStep()) {
|
||||
collectStepData();
|
||||
saveFormData();
|
||||
showStep('success');
|
||||
progressFill.style.width = '100%';
|
||||
// Clear saved data after successful submission
|
||||
localStorage.removeItem('formWizardData');
|
||||
// Hide navigation
|
||||
document.querySelector('.form-navigation').style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
if (validateStep()) {
|
||||
collectStepData();
|
||||
saveFormData();
|
||||
formWizard.currentStep++;
|
||||
showStep(formWizard.currentStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Previous step
|
||||
function prevStep() {
|
||||
if (formWizard.currentStep > 1) {
|
||||
formWizard.currentStep--;
|
||||
showStep(formWizard.currentStep);
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
nextBtn.addEventListener('click', nextStep);
|
||||
prevBtn.addEventListener('click', prevStep);
|
||||
|
||||
// Auto-save on input change
|
||||
document.querySelectorAll('.form-input, input[type="radio"]').forEach(input => {
|
||||
input.addEventListener('change', () => {
|
||||
saveFormData();
|
||||
});
|
||||
});
|
||||
|
||||
// Clear validation on input
|
||||
document.querySelectorAll('.form-input').forEach(input => {
|
||||
input.addEventListener('input', () => {
|
||||
const errorElement = document.getElementById(input.id + 'Error');
|
||||
if (input.value.trim()) {
|
||||
input.classList.remove('error');
|
||||
if (errorElement) errorElement.classList.remove('show');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize
|
||||
initializeFormData();
|
||||
showStep(formWizard.currentStep);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,691 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Playful Animation Media Player</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-pink: #FF6B9D;
|
||||
--primary-yellow: #FFC857;
|
||||
--primary-blue: #4ECDC4;
|
||||
--primary-purple: #A8E6CF;
|
||||
--primary-orange: #FF6F61;
|
||||
--bg-color: #FAF3F0;
|
||||
--text-dark: #2D3436;
|
||||
--white: #FFFFFF;
|
||||
--shadow: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% { transform: translateY(0) scale(1); }
|
||||
50% { transform: translateY(-10px) scale(1.05); }
|
||||
}
|
||||
|
||||
@keyframes wiggle {
|
||||
0%, 100% { transform: rotate(0deg); }
|
||||
25% { transform: rotate(-5deg); }
|
||||
75% { transform: rotate(5deg); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
}
|
||||
|
||||
@keyframes dance {
|
||||
0%, 100% { transform: translateX(0) rotate(0deg); }
|
||||
25% { transform: translateX(-5px) rotate(-2deg); }
|
||||
75% { transform: translateX(5px) rotate(2deg); }
|
||||
}
|
||||
|
||||
@keyframes rainbow {
|
||||
0% { filter: hue-rotate(0deg); }
|
||||
100% { filter: hue-rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-20px); }
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Comic Sans MS', cursive, sans-serif;
|
||||
background: var(--bg-color);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 30px;
|
||||
font-size: 2.5em;
|
||||
animation: wiggle 2s ease-in-out infinite;
|
||||
text-shadow: 3px 3px 0px var(--primary-pink);
|
||||
}
|
||||
|
||||
.media-player {
|
||||
background: var(--white);
|
||||
border-radius: 30px;
|
||||
box-shadow: 0 20px 40px var(--shadow);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Visualizer Section */
|
||||
.visualizer {
|
||||
height: 200px;
|
||||
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-purple) 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wave {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-around;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.wave-bar {
|
||||
width: 6%;
|
||||
background: var(--white);
|
||||
border-radius: 10px 10px 0 0;
|
||||
transition: height 0.2s ease;
|
||||
animation: dance 1s ease-in-out infinite;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.wave-bar:nth-child(odd) {
|
||||
animation-delay: 0.1s;
|
||||
background: var(--primary-yellow);
|
||||
}
|
||||
|
||||
.wave-bar:nth-child(even) {
|
||||
animation-delay: 0.2s;
|
||||
background: var(--primary-pink);
|
||||
}
|
||||
|
||||
/* Current Song Display */
|
||||
.current-song {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
background: var(--white);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.song-title {
|
||||
font-size: 1.5em;
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 5px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.song-artist {
|
||||
color: #7D8E95;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/* Controls Section */
|
||||
.controls {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
background: var(--primary-pink);
|
||||
border: none;
|
||||
color: var(--white);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
box-shadow: 0 5px 15px rgba(255, 107, 157, 0.3);
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
animation: bounce 0.5s ease-in-out;
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 10px 25px rgba(255, 107, 157, 0.5);
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
background: var(--primary-blue);
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
font-size: 2em;
|
||||
box-shadow: 0 5px 15px rgba(78, 205, 196, 0.3);
|
||||
}
|
||||
|
||||
.play-btn:hover {
|
||||
box-shadow: 0 10px 25px rgba(78, 205, 196, 0.5);
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.progress-container {
|
||||
padding: 0 20px 20px;
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: #F0F0F0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--primary-pink) 0%, var(--primary-yellow) 50%, var(--primary-blue) 100%);
|
||||
width: 35%;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
transition: width 0.3s ease;
|
||||
animation: rainbow 3s linear infinite;
|
||||
}
|
||||
|
||||
.progress-handle {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: var(--white);
|
||||
border: 4px solid var(--primary-pink);
|
||||
border-radius: 50%;
|
||||
cursor: grab;
|
||||
box-shadow: 0 2px 10px var(--shadow);
|
||||
animation: pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Playlist Section */
|
||||
.playlist-container {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
background: #F8F8F8;
|
||||
}
|
||||
|
||||
.playlist-item {
|
||||
background: var(--white);
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.playlist-item:hover {
|
||||
transform: translateX(10px);
|
||||
animation: dance 0.5s ease-in-out;
|
||||
box-shadow: 0 5px 15px var(--shadow);
|
||||
}
|
||||
|
||||
.playlist-item.active {
|
||||
background: linear-gradient(135deg, var(--primary-pink) 0%, var(--primary-purple) 100%);
|
||||
color: var(--white);
|
||||
animation: dance 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.playlist-number {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: var(--primary-yellow);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
color: var(--text-dark);
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.playlist-item.active .playlist-number {
|
||||
background: var(--white);
|
||||
animation: bounce 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Quality Selector */
|
||||
.quality-selector {
|
||||
padding: 20px;
|
||||
background: var(--white);
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.quality-bubble {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-purple);
|
||||
color: var(--text-dark);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.9em;
|
||||
position: relative;
|
||||
box-shadow: 0 5px 10px var(--shadow);
|
||||
}
|
||||
|
||||
.quality-bubble:hover {
|
||||
animation: float 1s ease-in-out;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.quality-bubble.active {
|
||||
background: var(--primary-orange);
|
||||
color: var(--white);
|
||||
animation: pulse 1s ease-in-out infinite;
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
.quality-bubble.low { width: 50px; height: 50px; font-size: 0.8em; }
|
||||
.quality-bubble.medium { width: 60px; height: 60px; }
|
||||
.quality-bubble.high { width: 70px; height: 70px; font-size: 1em; }
|
||||
.quality-bubble.ultra { width: 80px; height: 80px; font-size: 1.1em; }
|
||||
|
||||
/* Share Button */
|
||||
.share-section {
|
||||
padding: 20px;
|
||||
background: var(--white);
|
||||
text-align: center;
|
||||
border-top: 2px dashed #E0E0E0;
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
background: linear-gradient(135deg, var(--primary-orange) 0%, var(--primary-pink) 100%);
|
||||
color: var(--white);
|
||||
border: none;
|
||||
padding: 15px 40px;
|
||||
border-radius: 30px;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 5px 15px rgba(255, 111, 97, 0.3);
|
||||
}
|
||||
|
||||
.share-btn:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 25px rgba(255, 111, 97, 0.5);
|
||||
animation: wiggle 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
/* Confetti */
|
||||
.confetti {
|
||||
position: fixed;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
top: -10px;
|
||||
animation: confetti-fall 3s linear forwards;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
@keyframes confetti-fall {
|
||||
0% {
|
||||
transform: translateY(0) rotate(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh) rotate(720deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 600px) {
|
||||
h1 { font-size: 2em; }
|
||||
.control-btn { width: 50px; height: 50px; font-size: 1.2em; }
|
||||
.play-btn { width: 70px; height: 70px; font-size: 1.8em; }
|
||||
.quality-bubble { width: 50px; height: 50px; font-size: 0.8em; }
|
||||
.quality-bubble.low { width: 40px; height: 40px; font-size: 0.7em; }
|
||||
.quality-bubble.medium { width: 50px; height: 50px; }
|
||||
.quality-bubble.high { width: 60px; height: 60px; font-size: 0.9em; }
|
||||
.quality-bubble.ultra { width: 70px; height: 70px; font-size: 1em; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>🎵 Bouncy Music Box 🎵</h1>
|
||||
|
||||
<div class="media-player">
|
||||
<!-- Visualizer -->
|
||||
<div class="visualizer">
|
||||
<div class="wave" id="waveContainer">
|
||||
<!-- Wave bars will be generated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Song Display -->
|
||||
<div class="current-song">
|
||||
<div class="song-title" id="songTitle">Happy Dance Time</div>
|
||||
<div class="song-artist" id="songArtist">The Joyful Beats</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" id="progressBar">
|
||||
<div class="progress-fill" id="progressFill">
|
||||
<div class="progress-handle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="controls">
|
||||
<button class="control-btn" id="prevBtn">⏮️</button>
|
||||
<button class="control-btn play-btn" id="playBtn">▶️</button>
|
||||
<button class="control-btn" id="nextBtn">⏭️</button>
|
||||
</div>
|
||||
|
||||
<!-- Quality Selector -->
|
||||
<div class="quality-selector">
|
||||
<div class="quality-bubble low" data-quality="low">128k</div>
|
||||
<div class="quality-bubble medium" data-quality="medium">256k</div>
|
||||
<div class="quality-bubble high active" data-quality="high">320k</div>
|
||||
<div class="quality-bubble ultra" data-quality="ultra">FLAC</div>
|
||||
</div>
|
||||
|
||||
<!-- Playlist -->
|
||||
<div class="playlist-container">
|
||||
<div class="playlist-item active" data-index="0">
|
||||
<div class="playlist-number">1</div>
|
||||
<div>
|
||||
<div>Happy Dance Time</div>
|
||||
<small>The Joyful Beats</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-item" data-index="1">
|
||||
<div class="playlist-number">2</div>
|
||||
<div>
|
||||
<div>Bouncy Castle Dreams</div>
|
||||
<small>Rainbow Unicorns</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-item" data-index="2">
|
||||
<div class="playlist-number">3</div>
|
||||
<div>
|
||||
<div>Bubble Pop Symphony</div>
|
||||
<small>Giggle Factory</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-item" data-index="3">
|
||||
<div class="playlist-number">4</div>
|
||||
<div>
|
||||
<div>Marshmallow Clouds</div>
|
||||
<small>Sweet Melodies</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-item" data-index="4">
|
||||
<div class="playlist-number">5</div>
|
||||
<div>
|
||||
<div>Confetti Carnival</div>
|
||||
<small>Party Pirates</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Share Section -->
|
||||
<div class="share-section">
|
||||
<button class="share-btn" id="shareBtn">🎉 Share the Joy! 🎉</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Playlist data
|
||||
const playlist = [
|
||||
{ title: 'Happy Dance Time', artist: 'The Joyful Beats' },
|
||||
{ title: 'Bouncy Castle Dreams', artist: 'Rainbow Unicorns' },
|
||||
{ title: 'Bubble Pop Symphony', artist: 'Giggle Factory' },
|
||||
{ title: 'Marshmallow Clouds', artist: 'Sweet Melodies' },
|
||||
{ title: 'Confetti Carnival', artist: 'Party Pirates' }
|
||||
];
|
||||
|
||||
let currentSongIndex = 0;
|
||||
let isPlaying = false;
|
||||
let progress = 35;
|
||||
let animationFrame;
|
||||
let currentQuality = 'high';
|
||||
|
||||
// Create wave bars
|
||||
const waveContainer = document.getElementById('waveContainer');
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'wave-bar';
|
||||
bar.style.height = '20%';
|
||||
bar.style.animationDelay = `${i * 0.1}s`;
|
||||
waveContainer.appendChild(bar);
|
||||
}
|
||||
|
||||
// Visualizer animation
|
||||
function animateVisualizer() {
|
||||
const bars = document.querySelectorAll('.wave-bar');
|
||||
bars.forEach((bar, index) => {
|
||||
if (isPlaying) {
|
||||
const height = Math.random() * 80 + 20;
|
||||
bar.style.height = `${height}%`;
|
||||
|
||||
// More complex animations for higher quality
|
||||
if (currentQuality === 'ultra') {
|
||||
bar.style.transform = `scaleX(${1 + Math.random() * 0.2}) rotate(${Math.random() * 5 - 2.5}deg)`;
|
||||
} else if (currentQuality === 'high') {
|
||||
bar.style.transform = `scaleX(${1 + Math.random() * 0.1})`;
|
||||
}
|
||||
} else {
|
||||
bar.style.height = '20%';
|
||||
bar.style.transform = 'scaleX(1) rotate(0deg)';
|
||||
}
|
||||
});
|
||||
|
||||
animationFrame = requestAnimationFrame(animateVisualizer);
|
||||
}
|
||||
|
||||
// Play/Pause functionality
|
||||
const playBtn = document.getElementById('playBtn');
|
||||
playBtn.addEventListener('click', () => {
|
||||
isPlaying = !isPlaying;
|
||||
playBtn.textContent = isPlaying ? '⏸️' : '▶️';
|
||||
|
||||
if (isPlaying) {
|
||||
animateVisualizer();
|
||||
simulateProgress();
|
||||
// Make playlist item dance more when playing
|
||||
document.querySelector('.playlist-item.active').style.animationDuration = '0.5s';
|
||||
} else {
|
||||
document.querySelector('.playlist-item.active').style.animationDuration = '2s';
|
||||
}
|
||||
});
|
||||
|
||||
// Progress simulation
|
||||
function simulateProgress() {
|
||||
if (isPlaying && progress < 100) {
|
||||
progress += 0.1;
|
||||
document.getElementById('progressFill').style.width = `${progress}%`;
|
||||
requestAnimationFrame(simulateProgress);
|
||||
}
|
||||
}
|
||||
|
||||
// Progress bar click
|
||||
document.getElementById('progressBar').addEventListener('click', (e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
progress = ((e.clientX - rect.left) / rect.width) * 100;
|
||||
document.getElementById('progressFill').style.width = `${progress}%`;
|
||||
});
|
||||
|
||||
// Song navigation
|
||||
function changeSong(index) {
|
||||
// Update current song index
|
||||
currentSongIndex = index;
|
||||
if (currentSongIndex < 0) currentSongIndex = playlist.length - 1;
|
||||
if (currentSongIndex >= playlist.length) currentSongIndex = 0;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('songTitle').textContent = playlist[currentSongIndex].title;
|
||||
document.getElementById('songArtist').textContent = playlist[currentSongIndex].artist;
|
||||
|
||||
// Update playlist active state
|
||||
document.querySelectorAll('.playlist-item').forEach((item, i) => {
|
||||
item.classList.toggle('active', i === currentSongIndex);
|
||||
});
|
||||
|
||||
// Reset progress
|
||||
progress = 0;
|
||||
document.getElementById('progressFill').style.width = '0%';
|
||||
|
||||
// Bounce effect on song change
|
||||
const songDisplay = document.querySelector('.current-song');
|
||||
songDisplay.style.animation = 'bounce 0.5s ease-in-out';
|
||||
setTimeout(() => {
|
||||
songDisplay.style.animation = '';
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Previous/Next buttons
|
||||
document.getElementById('prevBtn').addEventListener('click', () => {
|
||||
changeSong(currentSongIndex - 1);
|
||||
});
|
||||
|
||||
document.getElementById('nextBtn').addEventListener('click', () => {
|
||||
changeSong(currentSongIndex + 1);
|
||||
});
|
||||
|
||||
// Playlist item clicks
|
||||
document.querySelectorAll('.playlist-item').forEach((item, index) => {
|
||||
item.addEventListener('click', () => {
|
||||
changeSong(index);
|
||||
});
|
||||
});
|
||||
|
||||
// Quality selector
|
||||
document.querySelectorAll('.quality-bubble').forEach(bubble => {
|
||||
bubble.addEventListener('click', () => {
|
||||
document.querySelectorAll('.quality-bubble').forEach(b => b.classList.remove('active'));
|
||||
bubble.classList.add('active');
|
||||
currentQuality = bubble.dataset.quality;
|
||||
|
||||
// Bounce all quality bubbles for fun
|
||||
document.querySelectorAll('.quality-bubble').forEach(b => {
|
||||
b.style.animation = 'bounce 0.5s ease-in-out';
|
||||
setTimeout(() => {
|
||||
b.style.animation = '';
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Update visualizer complexity based on quality
|
||||
const bars = document.querySelectorAll('.wave-bar');
|
||||
bars.forEach((bar, i) => {
|
||||
if (currentQuality === 'ultra') {
|
||||
bar.style.width = '5%';
|
||||
} else if (currentQuality === 'high') {
|
||||
bar.style.width = '6%';
|
||||
} else if (currentQuality === 'medium') {
|
||||
bar.style.width = '7%';
|
||||
} else {
|
||||
bar.style.width = '8%';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Share button with confetti
|
||||
document.getElementById('shareBtn').addEventListener('click', () => {
|
||||
// Create confetti burst
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const confetti = document.createElement('div');
|
||||
confetti.className = 'confetti';
|
||||
confetti.style.left = Math.random() * window.innerWidth + 'px';
|
||||
confetti.style.backgroundColor = ['#FF6B9D', '#FFC857', '#4ECDC4', '#A8E6CF', '#FF6F61'][Math.floor(Math.random() * 5)];
|
||||
confetti.style.animationDelay = Math.random() * 0.5 + 's';
|
||||
confetti.style.animationDuration = (Math.random() * 2 + 2) + 's';
|
||||
document.body.appendChild(confetti);
|
||||
|
||||
setTimeout(() => confetti.remove(), 4000);
|
||||
}
|
||||
|
||||
// Bounce the share button
|
||||
const btn = document.getElementById('shareBtn');
|
||||
btn.style.animation = 'bounce 0.5s ease-in-out';
|
||||
setTimeout(() => {
|
||||
btn.style.animation = '';
|
||||
}, 500);
|
||||
|
||||
// Show mock share message
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = '🎊 Shared! 🎊';
|
||||
setTimeout(() => {
|
||||
btn.textContent = originalText;
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
// Start visualizer animation
|
||||
animateVisualizer();
|
||||
|
||||
// Add some fun interactions
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (isPlaying) {
|
||||
const x = e.clientX / window.innerWidth;
|
||||
const y = e.clientY / window.innerHeight;
|
||||
|
||||
// Subtle tilt effect on the player
|
||||
const player = document.querySelector('.media-player');
|
||||
const tiltX = (y - 0.5) * 5;
|
||||
const tiltY = (x - 0.5) * -5;
|
||||
player.style.transform = `perspective(1000px) rotateX(${tiltX}deg) rotateY(${tiltY}deg)`;
|
||||
}
|
||||
});
|
||||
|
||||
// Reset tilt on mouse leave
|
||||
document.addEventListener('mouseleave', () => {
|
||||
document.querySelector('.media-player').style.transform = '';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue