Source identity
| Field | Recorded value |
|---|---|
| Source type | archive member |
| Original record | collection.html |
| Date | Not specified in source |
| Path or archive member | archives/libsrc-1c3f88063121/NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__chris-rock-lab-ai-mineral-id__b8adc02f-b334-4a9e-935d-60bbfadd3535/originals/deploy_6961ef2956ec812de977e691/collection.html |
| Original SHA-256 | a8ef613087a88bd7005d9152e6e35f11861ef23e3d6f060ad649dc57bac3c4b3 |
| Reading-copy SHA-256 | a8ef613087a88bd7005d9152e6e35f11861ef23e3d6f060ad649dc57bac3c4b3 |
| Archive source | {"sourceId": "libsrc-1c3f88063121", "member": "NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__chris-rock-lab-ai-mineral-id__b8adc02f-b334-4a9e-935d-60bbfadd3535/originals/deploy_6961ef2956ec812de977e691/collection.html"} |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| collection.html | {"sourceId": "libsrc-1c3f88063121", "member": "NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__chris-rock-lab-ai-mineral-id__b8adc02f-b334-4a9e-935d-60bbfadd3535/originals/deploy_6961ef2956ec812de977e691/collection.html"} | libsrc-258e18d916cc | archives/libsrc-1c3f88063121/NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__chris-rock-lab-ai-mineral-id__b8adc02f-b334-4a9e-935d-60bbfadd3535/originals/deploy_6961ef2956ec812de977e691/collection.html |
| 07a8d117679f056d4dde51f676a5847e163af2ad | {"sourceId": "libsrc-1c3f88063121", "member": "NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__chris-rock-lab-ai-mineral-id__b8adc02f-b334-4a9e-935d-60bbfadd3535/originals/.content_store/07/07a8d117679f056d4dde51f676a5847e163af2ad"} | libsrc-c420035a1ce2 | archives/libsrc-1c3f88063121/NETLIFY_AI_RECOVERY_2026-10-14_21-10-49/sites/janady13__chris-rock-lab-ai-mineral-id__b8adc02f-b334-4a9e-935d-60bbfadd3535/originals/.content_store/07/07a8d117679f056d4dde51f676a5847e163af2ad |
Recovered source transcript
Download complete source text ↓1,057 lines · 33.8 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Collection | Chris's Rock Lab</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700&family=Geologica:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--obsidian: #0d0d0d;
--slate: #1a1a1a;
--granite: #2d2d2d;
--sandstone: #c4a574;
--amber: #ffbf00;
--copper: #b87333;
--quartz: #e8e4e1;
--crystal: #a8d8ea;
--amethyst: #9966cc;
--emerald: #50c878;
--ruby: #e0115f;
--gold: #ffd700;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
::selection {
background: var(--amber);
color: var(--obsidian);
}
body {
font-family: 'Geologica', sans-serif;
background: var(--obsidian);
color: var(--quartz);
min-height: 100vh;
}
/* Header */
header {
padding: 1.5rem 3rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(196, 165, 116, 0.2);
background: rgba(13, 13, 13, 0.95);
backdrop-filter: blur(20px);
position: sticky;
top: 0;
z-index: 100;
}
.logo {
display: flex;
align-items: center;
gap: 12px;
text-decoration: none;
}
.logo-icon {
font-size: 1.8rem;
filter: drop-shadow(0 0 10px var(--amber));
}
.logo-text {
font-family: 'Cinzel', serif;
font-size: 1.2rem;
font-weight: 600;
color: var(--sandstone);
letter-spacing: 0.1em;
}
.logo-text span {
color: var(--amber);
}
.header-nav {
display: flex;
gap: 1.5rem;
}
.header-nav a {
padding: 0.7rem 1.5rem;
font-size: 0.85rem;
font-weight: 500;
text-decoration: none;
border-radius: 4px;
transition: all 0.3s ease;
}
.nav-link {
color: var(--quartz);
border: 1px solid transparent;
}
.nav-link:hover {
color: var(--amber);
border-color: rgba(255, 191, 0, 0.3);
}
.nav-cta {
background: linear-gradient(135deg, var(--amber), var(--copper));
color: var(--obsidian);
}
.nav-cta:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(255, 191, 0, 0.3);
}
/* Main Container */
main {
max-width: 1400px;
margin: 0 auto;
padding: 3rem;
}
/* Page Header */
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 3rem;
}
.page-title-section h1 {
font-family: 'Cinzel', serif;
font-size: 2.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.page-title-section h1 span {
color: var(--amber);
}
.page-subtitle {
font-size: 1rem;
color: rgba(232, 228, 225, 0.6);
}
/* Stats Bar */
.stats-bar {
display: flex;
gap: 2rem;
background: linear-gradient(145deg, var(--slate), var(--granite));
padding: 1.5rem 2rem;
border-radius: 12px;
border: 1px solid rgba(196, 165, 116, 0.15);
}
.stat {
text-align: center;
}
.stat-value {
font-family: 'Cinzel', serif;
font-size: 2rem;
font-weight: 700;
color: var(--amber);
}
.stat-label {
font-size: 0.75rem;
color: rgba(232, 228, 225, 0.5);
letter-spacing: 0.1em;
}
/* Controls */
.controls {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.search-box {
flex: 1;
min-width: 200px;
padding: 0.9rem 1.2rem;
background: var(--granite);
border: 1px solid rgba(196, 165, 116, 0.2);
border-radius: 8px;
color: var(--quartz);
font-family: 'Geologica', sans-serif;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.search-box::placeholder {
color: rgba(232, 228, 225, 0.4);
}
.search-box:focus {
outline: none;
border-color: var(--amber);
box-shadow: 0 0 0 3px rgba(255, 191, 0, 0.1);
}
.filter-btn {
padding: 0.9rem 1.5rem;
background: var(--granite);
border: 1px solid rgba(196, 165, 116, 0.2);
border-radius: 8px;
color: var(--quartz);
font-family: 'Geologica', sans-serif;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.3s ease;
}
.filter-btn:hover {
border-color: var(--sandstone);
background: rgba(196, 165, 116, 0.1);
}
.filter-btn.active {
background: var(--amber);
color: var(--obsidian);
border-color: var(--amber);
}
.view-toggle {
display: flex;
background: var(--granite);
border-radius: 8px;
overflow: hidden;
border: 1px solid rgba(196, 165, 116, 0.2);
}
.view-btn {
padding: 0.9rem 1.2rem;
background: transparent;
border: none;
color: var(--quartz);
cursor: pointer;
font-size: 1.1rem;
transition: all 0.3s ease;
}
.view-btn:hover {
background: rgba(196, 165, 116, 0.1);
}
.view-btn.active {
background: var(--amber);
color: var(--obsidian);
}
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 6rem 2rem;
text-align: center;
}
.empty-icon {
font-size: 5rem;
opacity: 0.3;
margin-bottom: 1.5rem;
}
.empty-title {
font-family: 'Cinzel', serif;
font-size: 1.5rem;
color: var(--sandstone);
margin-bottom: 0.5rem;
}
.empty-text {
font-size: 1rem;
color: rgba(232, 228, 225, 0.5);
margin-bottom: 2rem;
}
.empty-btn {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 1rem 2rem;
background: linear-gradient(135deg, var(--amber), var(--copper));
color: var(--obsidian);
font-family: 'Geologica', sans-serif;
font-weight: 600;
font-size: 0.95rem;
text-decoration: none;
border-radius: 8px;
transition: all 0.3s ease;
}
.empty-btn:hover {
transform: translateY(-3px);
box-shadow: 0 8px 30px rgba(255, 191, 0, 0.4);
}
/* Collection Grid */
.collection-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
}
.collection-grid.list-view {
grid-template-columns: 1fr;
}
/* Rock Card */
.rock-card {
background: linear-gradient(145deg, var(--slate), var(--granite));
border-radius: 12px;
overflow: hidden;
border: 1px solid rgba(196, 165, 116, 0.15);
transition: all 0.3s ease;
position: relative;
}
.rock-card:hover {
transform: translateY(-5px);
border-color: var(--amber);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
}
.rock-card-image {
width: 100%;
height: 200px;
object-fit: cover;
border-bottom: 1px solid rgba(196, 165, 116, 0.1);
}
.rock-card-content {
padding: 1.5rem;
}
.rock-card-type {
display: inline-block;
padding: 0.3rem 0.8rem;
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.1em;
background: rgba(255, 191, 0, 0.15);
color: var(--amber);
border-radius: 20px;
margin-bottom: 0.8rem;
}
.rock-card-name {
font-family: 'Cinzel', serif;
font-size: 1.3rem;
font-weight: 600;
color: var(--sandstone);
margin-bottom: 0.5rem;
}
.rock-card-date {
font-size: 0.8rem;
color: rgba(232, 228, 225, 0.5);
margin-bottom: 1rem;
}
.rock-card-props {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 1rem;
}
.rock-prop {
padding: 0.4rem 0.8rem;
background: var(--obsidian);
border-radius: 4px;
font-size: 0.75rem;
color: rgba(232, 228, 225, 0.7);
}
.rock-card-value {
font-family: 'Cinzel', serif;
font-size: 1.2rem;
font-weight: 600;
color: var(--gold);
}
.rock-card-actions {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
gap: 0.5rem;
opacity: 0;
transition: opacity 0.3s ease;
}
.rock-card:hover .rock-card-actions {
opacity: 1;
}
.card-action-btn {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(13, 13, 13, 0.8);
border: none;
border-radius: 50%;
color: var(--quartz);
cursor: pointer;
transition: all 0.3s ease;
}
.card-action-btn:hover {
background: var(--ruby);
color: white;
}
/* List View Styles */
.collection-grid.list-view .rock-card {
display: grid;
grid-template-columns: 150px 1fr;
}
.collection-grid.list-view .rock-card-image {
height: 100%;
min-height: 120px;
}
.collection-grid.list-view .rock-card-content {
display: flex;
align-items: center;
gap: 2rem;
}
.collection-grid.list-view .rock-card-info {
flex: 1;
}
.collection-grid.list-view .rock-card-props {
margin-bottom: 0;
}
/* Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 2rem;
}
.modal-overlay.active {
display: flex;
}
.modal {
background: var(--slate);
border-radius: 16px;
max-width: 700px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
border: 1px solid rgba(196, 165, 116, 0.2);
position: relative;
}
.modal-close {
position: absolute;
top: 1rem;
right: 1rem;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--granite);
border: none;
border-radius: 50%;
color: var(--quartz);
cursor: pointer;
font-size: 1.2rem;
transition: all 0.3s ease;
z-index: 10;
}
.modal-close:hover {
background: var(--ruby);
}
.modal-image {
width: 100%;
height: 300px;
object-fit: cover;
}
.modal-content {
padding: 2rem;
}
.modal-type {
display: inline-block;
padding: 0.4rem 1rem;
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.1em;
background: rgba(255, 191, 0, 0.15);
color: var(--amber);
border-radius: 20px;
margin-bottom: 1rem;
}
.modal-name {
font-family: 'Cinzel', serif;
font-size: 2rem;
font-weight: 600;
color: var(--amber);
margin-bottom: 1rem;
}
.modal-date {
font-size: 0.85rem;
color: rgba(232, 228, 225, 0.5);
margin-bottom: 1.5rem;
}
.modal-props {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin-bottom: 2rem;
}
.modal-prop {
background: var(--granite);
padding: 1rem;
border-radius: 8px;
}
.modal-prop-label {
font-size: 0.7rem;
color: rgba(232, 228, 225, 0.5);
letter-spacing: 0.1em;
margin-bottom: 0.3rem;
}
.modal-prop-value {
font-size: 1rem;
font-weight: 600;
color: var(--sandstone);
}
.modal-value {
background: linear-gradient(145deg, rgba(255, 191, 0, 0.1), rgba(184, 115, 51, 0.1));
padding: 1.5rem;
border-radius: 12px;
border: 1px solid rgba(255, 191, 0, 0.2);
text-align: center;
margin-bottom: 1.5rem;
}
.modal-value-label {
font-size: 0.75rem;
color: var(--amber);
letter-spacing: 0.1em;
margin-bottom: 0.5rem;
}
.modal-value-amount {
font-family: 'Cinzel', serif;
font-size: 2.5rem;
font-weight: 700;
color: var(--gold);
}
.modal-actions {
display: flex;
gap: 1rem;
}
.modal-btn {
flex: 1;
padding: 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: 'Geologica', sans-serif;
font-weight: 600;
font-size: 0.9rem;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.modal-btn-delete {
background: transparent;
border: 2px solid var(--ruby);
color: var(--ruby);
}
.modal-btn-delete:hover {
background: var(--ruby);
color: white;
}
.modal-btn-edit {
background: linear-gradient(135deg, var(--amber), var(--copper));
color: var(--obsidian);
}
.modal-btn-edit:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(255, 191, 0, 0.3);
}
/* Toast */
.toast {
position: fixed;
bottom: 2rem;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: var(--emerald);
color: var(--obsidian);
padding: 1rem 2rem;
border-radius: 8px;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
opacity: 0;
transition: all 0.3s ease;
z-index: 1001;
}
.toast.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
.toast.error {
background: var(--ruby);
color: white;
}
/* Responsive */
@media (max-width: 768px) {
header {
padding: 1rem 1.5rem;
}
.header-nav .nav-link {
display: none;
}
main {
padding: 1.5rem;
}
.page-header {
flex-direction: column;
gap: 1.5rem;
}
.stats-bar {
width: 100%;
justify-content: space-around;
}
.page-title-section h1 {
font-size: 1.8rem;
}
.collection-grid {
grid-template-columns: 1fr;
}
.collection-grid.list-view .rock-card {
grid-template-columns: 100px 1fr;
}
.modal-props {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<!-- Header -->
<header>
<a href="index.html" class="logo">
<span class="logo-icon">🪨</span>
<span class="logo-text">CHRIS'S <span>ROCK LAB</span></span>
</a>
<nav class="header-nav">
<a href="index.html" class="nav-link">Home</a>
<a href="scan.html" class="nav-cta">📷 Scan Rock</a>
</nav>
</header>
<!-- Main Content -->
<main>
<!-- Page Header -->
<div class="page-header">
<div class="page-title-section">
<h1>My <span>Collection</span></h1>
<p class="page-subtitle">Your personal rock & mineral museum</p>
</div>
<div class="stats-bar">
<div class="stat">
<div class="stat-value" id="totalCount">0</div>
<div class="stat-label">SPECIMENS</div>
</div>
<div class="stat">
<div class="stat-value" id="totalValue">$0</div>
<div class="stat-label">EST. VALUE</div>
</div>
</div>
</div>
<!-- Controls -->
<div class="controls">
<input type="text" class="search-box" id="searchBox" placeholder="🔍 Search your collection...">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="igneous">Igneous</button>
<button class="filter-btn" data-filter="sedimentary">Sedimentary</button>
<button class="filter-btn" data-filter="metamorphic">Metamorphic</button>
<button class="filter-btn" data-filter="mineral">Mineral</button>
<button class="filter-btn" data-filter="crystal">Crystal</button>
<div class="view-toggle">
<button class="view-btn active" data-view="grid">⊞</button>
<button class="view-btn" data-view="list">☰</button>
</div>
</div>
<!-- Empty State -->
<div class="empty-state" id="emptyState">
<div class="empty-icon">📦</div>
<h2 class="empty-title">No Rocks Yet</h2>
<p class="empty-text">Start building your collection by scanning your first rock!</p>
<a href="scan.html" class="empty-btn">📷 Scan Your First Rock</a>
</div>
<!-- Collection Grid -->
<div class="collection-grid" id="collectionGrid"></div>
</main>
<!-- Rock Detail Modal -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<button class="modal-close" id="modalClose">✕</button>
<img class="modal-image" id="modalImage" alt="Rock specimen">
<div class="modal-content">
<span class="modal-type" id="modalType">TYPE</span>
<h2 class="modal-name" id="modalName">Rock Name</h2>
<p class="modal-date" id="modalDate">Added on --</p>
<div class="modal-props">
<div class="modal-prop">
<div class="modal-prop-label">HARDNESS</div>
<div class="modal-prop-value" id="modalHardness">-</div>
</div>
<div class="modal-prop">
<div class="modal-prop-label">COMPOSITION</div>
<div class="modal-prop-value" id="modalComposition">-</div>
</div>
</div>
<div class="modal-value">
<div class="modal-value-label">💰 ESTIMATED VALUE</div>
<div class="modal-value-amount" id="modalValue">$0</div>
</div>
<div class="modal-actions">
<button class="modal-btn modal-btn-delete" id="deleteBtn">🗑️ Remove</button>
<button class="modal-btn modal-btn-edit" onclick="document.getElementById('modalOverlay').classList.remove('active')">✓ Done</button>
</div>
</div>
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast">Notification</div>
<script>
// State
let collection = [];
let currentFilter = 'all';
let currentView = 'grid';
let selectedRockId = null;
// DOM Elements
const collectionGrid = document.getElementById('collectionGrid');
const emptyState = document.getElementById('emptyState');
const searchBox = document.getElementById('searchBox');
const modalOverlay = document.getElementById('modalOverlay');
const toast = document.getElementById('toast');
// Initialize
document.addEventListener('DOMContentLoaded', () => {
loadCollection();
setupEventListeners();
});
// Load collection from localStorage
function loadCollection() {
const stored = localStorage.getItem('rockCollection');
collection = stored ? JSON.parse(stored) : [];
// Add sample rocks if collection is empty (for demo)
if (collection.length === 0) {
addSampleRocks();
}
renderCollection();
updateStats();
}
// Add sample rocks for demo
function addSampleRocks() {
const sampleRocks = [
{
id: 1,
name: 'Amethyst Cluster',
type: 'CRYSTAL',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM5OTY2Y2MiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM2NjMzOTkiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIiBmb250LXNpemU9IjQ4Ij7wn5OEPC90ZXh0Pjwvc3ZnPg==',
date: new Date(Date.now() - 86400000 * 5).toISOString(),
properties: {
hardness: '7 Mohs',
composition: 'SiO₂ with Fe',
value: '$150-400'
}
},
{
id: 2,
name: 'Pyrite Cube',
type: 'MINERAL',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmZmQ3MDAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNiODg2MGIiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIiBmb250LXNpemU9IjQ4Ij7inKg8L3RleHQ+PC9zdmc+',
date: new Date(Date.now() - 86400000 * 12).toISOString(),
properties: {
hardness: '6-6.5 Mohs',
composition: 'FeS₂',
value: '$25-80'
}
},
{
id: 3,
name: 'River Granite',
type: 'IGNEOUS ROCK',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM2NjY2NjYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMzMzMzMzMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIiBmb250LXNpemU9IjQ4Ij7wn6qdPC90ZXh0Pjwvc3ZnPg==',
date: new Date(Date.now() - 86400000 * 20).toISOString(),
properties: {
hardness: '6-7 Mohs',
composition: 'Quartz, Feldspar',
value: '$15-50'
}
},
{
id: 4,
name: 'Malachite Slab',
type: 'MINERAL',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1MGM4NzgiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMyZThhNTciLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGRvbWluYW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIiBmb250LXNpemU9IjQ4Ij7wn5KaPC90ZXh0Pjwvc3ZnPg==',
date: new Date(Date.now() - 86400000 * 30).toISOString(),
properties: {
hardness: '3.5-4 Mohs',
composition: 'Cu₂CO₃(OH)₂',
value: '$75-250'
}
}
];
collection = sampleRocks;
localStorage.setItem('rockCollection', JSON.stringify(collection));
}
// Setup event listeners
function setupEventListeners() {
// Search
searchBox.addEventListener('input', (e) => {
renderCollection(e.target.value);
});
// Filter buttons
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderCollection();
});
});
// View toggle
document.querySelectorAll('.view-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentView = btn.dataset.view;
collectionGrid.classList.toggle('list-view', currentView === 'list');
});
});
// Modal close
document.getElementById('modalClose').addEventListener('click', () => {
modalOverlay.classList.remove('active');
});
modalOverlay.addEventListener('click', (e) => {
if (e.target === modalOverlay) {
modalOverlay.classList.remove('active');
}
});
// Delete button
document.getElementById('deleteBtn').addEventListener('click', () => {
if (selectedRockId) {
deleteRock(selectedRockId);
}
});
}
// Render collection
function renderCollection(searchTerm = '') {
let filtered = collection;
// Apply filter
if (currentFilter !== 'all') {
filtered = filtered.filter(rock =>
rock.type.toLowerCase().includes(currentFilter.toLowerCase())
);
}
// Apply search
if (searchTerm) {
filtered = filtered.filter(rock =>
rock.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
rock.type.toLowerCase().includes(searchTerm.toLowerCase())
);
}
// Show empty state or grid
if (filtered.length === 0 && collection.length === 0) {
emptyState.style.display = 'flex';
collectionGrid.style.display = 'none';
} else {
emptyState.style.display = 'none';
collectionGrid.style.display = 'grid';
}
// Render cards
collectionGrid.innerHTML = filtered.map(rock => `
<div class="rock-card" onclick="openRockModal(${rock.id})">
<div class="rock-card-actions">
<button class="card-action-btn" onclick="event.stopPropagation(); deleteRock(${rock.id})">🗑️</button>
</div>
<img class="rock-card-image" src="${rock.image}" alt="${rock.name}">
<div class="rock-card-content">
<div class="rock-card-info">
<span class="rock-card-type">${rock.type}</span>
<h3 class="rock-card-name">${rock.name}</h3>
<p class="rock-card-date">Added ${formatDate(rock.date)}</p>
<div class="rock-card-props">
<span class="rock-prop">${rock.properties.hardness}</span>
</div>
</div>
<div class="rock-card-value">${rock.properties.value}</div>
</div>
</div>
`).join('');
}
// Update stats
function updateStats() {
document.getElementById('totalCount').textContent = collection.length;
// Calculate total value (rough estimate from ranges)
let totalMin = 0;
let totalMax = 0;
collection.forEach(rock => {
const valueStr = rock.properties.value;
const matches = valueStr.match(/\$(\d+)(?:-(\d+))?/);
if (matches) {
totalMin += parseInt(matches[1]);
totalMax += parseInt(matches[2] || matches[1]);
}
});
const avgValue = Math.round((totalMin + totalMax) / 2);
document.getElementById('totalValue').textContent = '$' + avgValue.toLocaleString();
}
// Open rock modal
function openRockModal(id) {
const rock = collection.find(r => r.id === id);
if (!rock) return;
selectedRockId = id;
document.getElementById('modalImage').src = rock.image;
document.getElementById('modalType').textContent = rock.type;
document.getElementById('modalName').textContent = rock.name;
document.getElementById('modalDate').textContent = 'Added ' + formatDate(rock.date);
document.getElementById('modalHardness').textContent = rock.properties.hardness;
document.getElementById('modalComposition').textContent = rock.properties.composition;
document.getElementById('modalValue').textContent = rock.properties.value;
modalOverlay.classList.add('active');
}
// Delete rock
function deleteRock(id) {
collection = collection.filter(r => r.id !== id);
localStorage.setItem('rockCollection', JSON.stringify(collection));
renderCollection();
updateStats();
modalOverlay.classList.remove('active');
showToast('Rock removed from collection', 'error');
}
// Format date
function formatDate(dateStr) {
const date = new Date(dateStr);
const now = new Date();
const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24));
if (diffDays === 0) return 'today';
if (diffDays === 1) return 'yesterday';
if (diffDays < 7) return `${diffDays} days ago`;
if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
return date.toLocaleDateString();
}
// Show toast
function showToast(message, type = 'success') {
toast.textContent = (type === 'success' ? '✅ ' : '🗑️ ') + message;
toast.className = 'toast' + (type === 'error' ? ' error' : '');
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 3000);
}
</script>
</body>
</html>