unknown
Enhanced UI and background
d1d94fb
Raw
History Blame Contribute Delete
30.7 kB
{% extends 'base.html' %}
{% load static %}
{% block title %}Constellation Database — Constellar{% endblock %}
{% block extra_css %}
<style>
/* ── search ── */
.search-wrap { position: relative; max-width: 480px; margin: 0 auto; }
.search-input {
width: 100%;
background: rgba(255,255,255,.04);
border: 1px solid var(--border);
border-radius: 10px;
padding: .8rem 1rem .8rem 2.75rem;
font-size: .875rem;
font-family: 'Inter', sans-serif;
color: var(--text);
outline: none;
transition: border-color .15s;
}
.search-input:focus { border-color: rgba(99,102,241,.45); }
.search-input::placeholder { color: var(--muted); }
.search-icon {
position: absolute; left: .875rem; top: 50%;
transform: translateY(-50%);
color: var(--muted); pointer-events: none;
}
/* ── view toggle ── */
.view-toggle-wrap {
display: inline-flex;
background: rgba(255,255,255,.04);
border: 1px solid var(--border);
border-radius: 8px;
padding: 3px;
gap: 2px;
}
.view-toggle {
font-family: 'Inter', sans-serif;
font-size: .78125rem; font-weight: 500;
display: inline-flex; align-items: center; gap: .375rem;
padding: .45rem .875rem;
border-radius: 6px;
border: none; cursor: pointer;
background: transparent;
color: var(--muted);
transition: all .15s;
}
.view-toggle.active { background: var(--accent); color: #fff; }
.view-toggle:not(.active):hover { color: #fff; }
/* ── grid card ── */
.db-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
cursor: pointer;
transition: border-color .2s, transform .2s;
}
.db-card:hover {
border-color: rgba(99,102,241,.35);
transform: translateY(-3px);
}
.db-card-img {
position: relative;
width: 100%;
aspect-ratio: 4/3;
background: rgba(99,102,241,.05);
overflow: hidden;
}
.db-card-img img {
width: 100%; height: 100%;
object-fit: cover;
display: block;
transition: transform .3s;
}
.db-card:hover .db-card-img img { transform: scale(1.04); }
.db-card-body { padding: 1rem 1.125rem 1.25rem; }
.db-card-body h3 {
font-size: .9375rem; font-weight: 600;
margin: 0 0 .375rem; color: #fff;
}
.db-card-body p {
font-size: .75rem; color: var(--muted);
line-height: 1.6; margin: 0;
}
/* ── list row ── */
.db-row {
display: flex; align-items: center; gap: 1rem;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: .875rem 1.125rem;
cursor: pointer;
transition: border-color .2s, background .2s;
}
.db-row:hover {
border-color: rgba(99,102,241,.3);
background: rgba(99,102,241,.05);
}
.db-row-img {
width: 52px; height: 52px; flex-shrink: 0;
border-radius: 9px;
overflow: hidden;
background: rgba(99,102,241,.08);
}
.db-row-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.db-row h3 { font-size: .875rem; font-weight: 600; margin: 0 0 .2rem; color: #fff; }
.db-row p { font-size: .75rem; color: var(--muted); margin: 0; line-height: 1.5; }
/* ── filter chips ── */
.chip {
font-family: 'Inter', sans-serif;
font-size: .75rem; font-weight: 500;
padding: .4rem .875rem;
border-radius: 999px;
border: 1px solid var(--border);
background: rgba(255,255,255,.03);
color: var(--muted);
cursor: pointer;
transition: all .15s;
}
.chip:hover { color: #fff; border-color: rgba(255,255,255,.18); }
.chip.active {
background: var(--accent-dim);
border-color: rgba(99,102,241,.45);
color: #a5b4fc;
}
mark {
background: rgba(99,102,241,.3);
color: inherit;
border-radius: 3px;
padding: 0 1px;
}
/* ── detail modal ── */
.modal-backdrop {
position: fixed; inset: 0;
background: rgba(3,7,18,.8);
backdrop-filter: blur(6px);
z-index: 100;
display: none;
align-items: center; justify-content: center;
padding: 1.5rem;
}
.modal-backdrop.open { display: flex; }
.modal-card {
background: #0a1226;
border: 1px solid rgba(255,255,255,.1);
border-radius: 16px;
max-width: 480px; width: 100%;
max-height: 88vh;
overflow-y: auto;
animation: modalIn .22s ease;
}
@keyframes modalIn {
from { opacity: 0; transform: translateY(14px) scale(.98); }
to { opacity: 1; transform: none; }
}
.modal-img {
width: 100%; aspect-ratio: 16/10;
object-fit: cover; display: block;
}
.modal-close {
position: absolute; top: 1rem; right: 1rem;
width: 32px; height: 32px;
border-radius: 8px;
border: 1px solid rgba(255,255,255,.15);
background: rgba(6,13,31,.7);
color: #fff; cursor: pointer;
display: flex; align-items: center; justify-content: center;
backdrop-filter: blur(6px);
transition: background .15s;
}
.modal-close:hover { background: rgba(255,255,255,.12); }
.modal-type {
display: inline-block;
font-size: .625rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase;
padding: .25rem .625rem;
border-radius: 5px;
background: var(--accent-dim);
color: #a5b4fc;
margin-bottom: .75rem;
}
.fact-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.75rem;
}
.fact-card h3 {
font-size: .9375rem; font-weight: 600;
margin: 0 0 .625rem; color: #a5b4fc;
}
.fact-card p { font-size: .8125rem; color: var(--muted); line-height: 1.7; margin: 0; }
</style>
{% endblock %}
{% block content %}
<!-- ════ HEADER ════ -->
<section class="section" style="padding-top:4rem; padding-bottom:2rem; text-align:center;">
<div class="tag" style="margin-bottom:1.25rem;">IAU Official Catalog</div>
<h1 style="font-family:'Orbitron',monospace; font-size:clamp(1.6rem,3.5vw,2.5rem); font-weight:900; margin:0 0 1rem; letter-spacing:-.01em;">
All 88 Constellations
</h1>
<p style="font-size:.9375rem; color:var(--muted); line-height:1.7; max-width:520px; margin:0 auto 2.25rem;">
The complete catalog of officially recognized constellations — each with its mythology and place in the night sky.
</p>
<!-- search -->
<div class="search-wrap" style="margin-bottom:1.25rem;">
<div class="search-icon">
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="8"/><path stroke-linecap="round" d="m21 21-4.35-4.35"/>
</svg>
</div>
<input type="text" placeholder="Search constellations…" class="search-input" id="searchInput" />
</div>
<!-- filter chips -->
<div style="display:flex; gap:.5rem; justify-content:center; flex-wrap:wrap; margin-bottom:1.25rem;">
<button class="chip active" data-filter="all">All 88</button>
<button class="chip" data-filter="zodiac">Zodiac</button>
<button class="chip" data-filter="northern">Northern Sky</button>
<button class="chip" data-filter="southern">Southern Sky</button>
</div>
<!-- view toggle + count -->
<div style="display:flex; align-items:center; justify-content:center; gap:1rem; flex-wrap:wrap;">
<div class="view-toggle-wrap">
<button class="view-toggle active" data-view="grid" id="gridBtn">
<svg width="13" height="13" fill="currentColor" viewBox="0 0 20 20"><path d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/></svg>
Grid
</button>
<button class="view-toggle" data-view="list" id="listBtn">
<svg width="13" height="13" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"/></svg>
List
</button>
</div>
<p style="font-size:.75rem; color:var(--muted); margin:0;" id="resultsCount">
Showing <span id="filteredCount">88</span> of 88
</p>
</div>
</section>
<!-- ════ CATALOG ════ -->
<section style="padding:0 1.5rem 3rem;">
<div style="max-width:1280px; margin:0 auto;">
<div style="display:grid; grid-template-columns:repeat(auto-fill,minmax(250px,1fr)); gap:1rem;" id="gridView"></div>
<div style="display:none; flex-direction:column; gap:.625rem;" id="listView"></div>
<!-- no results -->
<div style="text-align:center; padding:3.5rem 1rem; display:none;" id="noResults">
<div style="width:52px; height:52px; border-radius:13px; background:rgba(255,255,255,.04); border:1px solid var(--border); display:flex; align-items:center; justify-content:center; margin:0 auto 1.125rem;">
<svg width="22" height="22" fill="none" stroke="rgba(221,228,240,.4)" stroke-width="1.6" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="8"/><path stroke-linecap="round" d="m21 21-4.35-4.35M8 11h6"/>
</svg>
</div>
<h3 style="font-size:.9375rem; font-weight:600; margin:0 0 .375rem;">No constellations found</h3>
<p style="font-size:.8125rem; color:var(--muted); margin:0;">Try a different search term.</p>
</div>
</div>
</section>
<!-- ════ FACTS ════ -->
<section style="border-top:1px solid var(--border);">
<div class="section" style="padding-top:3.5rem; padding-bottom:1rem;">
<div style="text-align:center; margin-bottom:2rem;">
<p class="section-label" style="justify-content:center;">Background</p>
<h2 style="font-family:'Orbitron',monospace; font-size:clamp(1.2rem,2.5vw,1.6rem); font-weight:700; margin:0;">
A Brief History
</h2>
</div>
<div style="display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:1rem;">
<div class="fact-card">
<h3>Ancient origins</h3>
<p>Many constellations date back to ancient Mesopotamia and were later adopted by the Greeks and Romans, who wove them into their mythology.</p>
</div>
<div class="fact-card">
<h3>Celestial navigation</h3>
<p>Before modern instruments, sailors used constellations like Ursa Minor — home of the North Star — to navigate open seas.</p>
</div>
<div class="fact-card">
<h3>Modern standard</h3>
<p>The 88 official constellations were standardized by the International Astronomical Union in 1922, defining the sky map still used today.</p>
</div>
</div>
</div>
</section>
<!-- ════ DETAIL MODAL ════ -->
<div class="modal-backdrop" id="modalBackdrop">
<div class="modal-card" style="position:relative;">
<button class="modal-close" id="modalClose" aria-label="Close">
<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
<img class="modal-img" id="modalImg" src="" alt="">
<div style="padding:1.5rem 1.75rem 1.75rem;">
<span class="modal-type" id="modalType"></span>
<h3 id="modalName" style="font-family:'Orbitron',monospace; font-size:1.25rem; font-weight:700; margin:0 0 .625rem; color:#fff;"></h3>
<p id="modalDesc" style="font-size:.875rem; color:var(--muted); line-height:1.75; margin:0 0 1.5rem;"></p>
<a href="{{ CHATBOT_BASE_URL }}" class="btn-accent" style="padding:.6rem 1.25rem; display:inline-block;" id="modalChat">
Ask the AI assistant about this →
</a>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
const constellationData = [
{ id: 0, name: "Andromeda", description: "The Royal Sea Monster Bait, a princess chained to a rock as sacrifice", image: "image0.webp" },
{ id: 1, name: "Antlia", description: "The Air Pump, a scientific instrument invented in the 17th century", image: "image1.webp" },
{ id: 2, name: "Apus", description: "The Bird of Paradise, representing exotic birds from New Guinea", image: "image2.webp" },
{ id: 3, name: "Aquarius", description: "The Water-Bearer, representing a figure pouring water from a jar", image: "image3.webp" },
{ id: 4, name: "Aquila", description: "The Thunderbolt Eagle, Zeus's eagle that carried his thunderbolts", image: "image4.webp" },
{ id: 5, name: "Ara", description: "The Altar, where gods formed an alliance before battling the Titans", image: "image5.webp" },
{ id: 6, name: "Aries", description: "The Ram with the golden fleece from Greek mythology", image: "image6.webp" },
{ id: 7, name: "Auriga", description: "The Charioteer, a skilled chariot driver in Greek mythology", image: "image7.webp" },
{ id: 8, name: "Boötes", description: "The Herdsman, said to be hunting the bears of Ursa Major and Minor", image: "image8.webp" },
{ id: 9, name: "Caelum", description: "The Chisel, a tool used by sculptors to create artistic works", image: "image9.webp" },
{ id: 10, name: "Camelopardalis", description: "The Giraffe, one of the larger but fainter constellations", image: "image10.webp" },
{ id: 11, name: "Cancer", description: "The Crab sent by Hera to distract Hercules during his labors", image: "image11.webp" },
{ id: 12, name: "Canes Venatici", description: "The Hunting Dogs, represented by two dogs named Asterion and Chara", image: "image12.webp" },
{ id: 13, name: "Canis Major", description: "The Big Dog, containing Sirius, the brightest star in our night sky", image: "image13.webp" },
{ id: 14, name: "Canis Minor", description: "The Small Dog, companion to Orion the hunter", image: "image14.webp" },
{ id: 15, name: "Capricornus", description: "The Sea Goat, a mythological creature with a goat's head and fish's tail", image: "image15.webp" },
{ id: 16, name: "Carina", description: "The Keel of the ship Argo Navis from Jason and the Argonauts", image: "image16.webp" },
{ id: 17, name: "Cassiopeia", description: "The Vain Queen whose boasting led to her daughter being sacrificed", image: "image17.webp" },
{ id: 18, name: "Centaurus", description: "The Centaur, containing Alpha Centauri, the closest star system to our own", image: "image18.webp" },
{ id: 19, name: "Cepheus", description: "The King, husband of Cassiopeia and father of Andromeda", image: "image19.webp" },
{ id: 20, name: "Cetus", description: "The Whale or sea monster sent to devour Andromeda", image: "image20.webp" },
{ id: 21, name: "Chamaeleon", description: "The Chameleon, a small southern constellation named after the reptile", image: "image21.webp" },
{ id: 22, name: "Circinus", description: "The Compass used for drawing circles, a modern scientific constellation", image: "image22.webp" },
{ id: 23, name: "Columba", description: "The Dove, representing the bird that Noah sent from the Ark", image: "image23.webp" },
{ id: 24, name: "Coma Berenices", description: "Berenice's Hair, named after Queen Berenice II who sacrificed her hair", image: "image24.webp" },
{ id: 25, name: "Corona Australis", description: "The Southern Crown, representing a crown worn by centaurs", image: "image25.webp" },
{ id: 26, name: "Corona Borealis", description: "The Northern Crown, a crown given to Ariadne by Dionysus", image: "image26.webp" },
{ id: 27, name: "Corvus", description: "The Raven, Apollo's sacred bird in Greek mythology", image: "image27.webp" },
{ id: 28, name: "Crater", description: "The Cup, representing the cup of Apollo in Greek mythology", image: "image28.webp" },
{ id: 29, name: "Crux", description: "The Southern Cross, the smallest constellation but one of the most recognizable", image: "image29.webp" },
{ id: 30, name: "Cygnus", description: "The Swan, representing Zeus in disguise or the friend of Phaethon", image: "image30.webp" },
{ id: 31, name: "Delphinus", description: "The Dolphin, representing the creature that saved the poet Arion", image: "image31.webp" },
{ id: 32, name: "Dorado", description: "The Goldfish or Swordfish, home to most of the Large Magellanic Cloud", image: "image32.webp" },
{ id: 33, name: "Draco", description: "The Dragon that guarded the golden apples in the Garden of Hesperides", image: "image33.webp" },
{ id: 34, name: "Equuleus", description: "The Little Horse, representing the brother of Pegasus given to Castor", image: "image34.webp" },
{ id: 35, name: "Eridanus", description: "The River, representing the path Phaethon fell after failing to drive the sun chariot", image: "image35.webp" },
{ id: 36, name: "Fornax", description: "The Furnace, a modern constellation representing a chemist's furnace", image: "image36.webp" },
{ id: 37, name: "Gemini", description: "The Twins Castor and Pollux from Greek mythology", image: "image37.webp" },
{ id: 38, name: "Grus", description: "The Crane, a southern bird constellation created in the 16th century", image: "image38.webp" },
{ id: 39, name: "Hercules", description: "The Strong Man, representing the Greek hero known for his twelve labors", image: "image39.webp" },
{ id: 40, name: "Horologium", description: "The Pendulum Clock, a modern constellation representing a timekeeping device", image: "image40.webp" },
{ id: 41, name: "Hydra", description: "The Water Serpent, the largest constellation in the sky", image: "image41.webp" },
{ id: 42, name: "Hydrus", description: "The Watersnake, a southern constellation distinct from Hydra", image: "image42.webp" },
{ id: 43, name: "Indus", description: "The Indian, representing a native of the East Indies", image: "image43.webp" },
{ id: 44, name: "Lacerta", description: "The Lizard, a small and faint northern constellation", image: "image44.webp" },
{ id: 45, name: "Leo", description: "The Lion that Hercules slew as his first labor", image: "image45.webp" },
{ id: 46, name: "Leo Minor", description: "The Little Lion, a small constellation near Leo", image: "image46.webp" },
{ id: 47, name: "Lepus", description: "The Hare/Rabbit hunted by Orion and his dogs", image: "image47.webp" },
{ id: 48, name: "Libra", description: "The Scales, the only zodiac constellation representing an inanimate object", image: "image48.webp" },
{ id: 49, name: "Lupus", description: "The Wolf, an ancient constellation associated with various myths", image: "image49.webp" },
{ id: 50, name: "Lynx", description: "The Lynx, named for the keen eyesight needed to see its faint stars", image: "image50.webp" },
{ id: 51, name: "Lyra", description: "The Harp played by Orpheus, containing the bright star Vega", image: "image51.webp" },
{ id: 52, name: "Mensa", description: "The Table Mountain in South Africa, containing part of the Large Magellanic Cloud", image: "image52.webp" },
{ id: 53, name: "Microscopium", description: "The Microscope, a modern constellation representing the scientific instrument", image: "image53.webp" },
{ id: 54, name: "Monoceros", description: "The Unicorn, a faint constellation between Orion and Hydra", image: "image54.webp" },
{ id: 55, name: "Musca", description: "The Fly, a small southern constellation near the Southern Cross", image: "image55.webp" },
{ id: 56, name: "Norma", description: "The Level, a carpenter's square used for measuring right angles", image: "image56.webp" },
{ id: 57, name: "Octans", description: "The Octant, a measuring instrument containing the south celestial pole", image: "image57.webp" },
{ id: 58, name: "Ophiuchus", description: "The Serpent-Bearer, representing the healer Asclepius holding a serpent", image: "image58.webp" },
{ id: 59, name: "Orion", description: "The Hunter with his distinctive belt of three stars", image: "image59.webp" },
{ id: 60, name: "Pavo", description: "The Peacock, a southern constellation representing the beautiful bird", image: "image60.webp" },
{ id: 61, name: "Pegasus", description: "The Winged Horse that sprang from Medusa's blood", image: "image61.webp" },
{ id: 62, name: "Perseus", description: "The Greek Hero who rescued Andromeda from Cetus", image: "image62.webp" },
{ id: 63, name: "Phoenix", description: "The Firebird that rises from its own ashes", image: "image63.webp" },
{ id: 64, name: "Pictor", description: "The Painter's Easel, a modern constellation representing an artist's tool", image: "image64.webp" },
{ id: 65, name: "Pisces", description: "The Fishes, representing Venus and Cupid transformed to escape Typhon", image: "image65.webp" },
{ id: 66, name: "Piscis Austrinus", description: "The Southern Fish, drinking the water poured by Aquarius", image: "image66.webp" },
{ id: 67, name: "Puppis", description: "The Stern of the ship Argo Navis from Greek mythology", image: "image67.webp" },
{ id: 68, name: "Pyxis", description: "The Compass of the ship Argo Navis, a navigational instrument", image: "image68.webp" },
{ id: 69, name: "Reticulum", description: "The Reticle, a small net-like pattern used in telescope eyepieces", image: "image69.webp" },
{ id: 70, name: "Sagitta", description: "The Arrow shot by Hercules or used by Apollo to slay the Cyclops", image: "image70.webp" },
{ id: 71, name: "Sagittarius", description: "The Archer, a centaur with a bow pointed at Scorpius", image: "image71.webp" },
{ id: 72, name: "Scorpius", description: "The Scorpion that killed Orion in Greek mythology", image: "image72.webp" },
{ id: 73, name: "Sculptor", description: "The Sculptor, representing an artist's studio with tools", image: "image73.webp" },
{ id: 74, name: "Scutum", description: "The Shield, created to honor King John Sobieski of Poland", image: "image74.webp" },
{ id: 75, name: "Serpens", description: "The Serpent, the only constellation split into two parts (Caput and Cauda)", image: "image75.webp" },
{ id: 76, name: "Sextans", description: "The Sextant, a navigational instrument used by astronomers", image: "image76.webp" },
{ id: 77, name: "Taurus", description: "The Bull, representing Zeus disguised to abduct Europa", image: "image77.webp" },
{ id: 78, name: "Telescopium", description: "The Telescope, honoring the invention that revolutionized astronomy", image: "image78.webp" },
{ id: 79, name: "Triangulum", description: "The Triangle, one of the smallest but oldest recognized constellations", image: "image79.webp" },
{ id: 80, name: "Triangulum Australe", description: "The Southern Triangle, a small but distinctive southern constellation", image: "image80.webp" },
{ id: 81, name: "Tucana", description: "The Toucan, home to the Small Magellanic Cloud galaxy", image: "image81.webp" },
{ id: 82, name: "Ursa Major", description: "The Big Bear, containing the famous Big Dipper asterism", image: "image82.webp" },
{ id: 83, name: "Ursa Minor", description: "The Small Bear, containing Polaris, the North Star", image: "image83.webp" },
{ id: 84, name: "Vela", description: "The Sails of the ship Argo Navis, containing several bright stars", image: "image84.webp" },
{ id: 85, name: "Virgo", description: "The Young Maiden, the second largest constellation containing bright star Spica", image: "image85.webp" },
{ id: 86, name: "Volans", description: "The Flying Fish, a small southern constellation", image: "image86.webp" },
{ id: 87, name: "Vulpecula", description: "The Little Fox, home to the famous Dumbbell Nebula", image: "image87.webp" }
];
// zodiac + hemisphere classification (by IAU quadrant of constellation center)
const ZODIAC = new Set([3, 6, 11, 15, 37, 45, 48, 65, 71, 72, 77, 85]);
const NORTHERN = new Set([0, 4, 7, 8, 10, 12, 14, 17, 19, 24, 26, 30, 31, 33, 34, 39, 44, 46, 50, 51, 58, 59, 61, 62, 70, 75, 79, 82, 83, 87]);
function typeOf(c) {
return ZODIAC.has(c.id) ? 'zodiac' : NORTHERN.has(c.id) ? 'northern' : 'southern';
}
function typeLabel(c) {
const t = typeOf(c);
return t === 'zodiac' ? 'Zodiac' : t === 'northern' ? 'Northern Sky' : 'Southern Sky';
}
let currentView = 'grid';
let currentFilter = 'all';
let currentSearch = '';
let filteredData = [...constellationData];
const searchInput = document.getElementById('searchInput');
const gridView = document.getElementById('gridView');
const listView = document.getElementById('listView');
const noResults = document.getElementById('noResults');
const filteredCount = document.getElementById('filteredCount');
const gridBtn = document.getElementById('gridBtn');
const listBtn = document.getElementById('listBtn');
function escapeRegex(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
function highlight(text) {
if (!currentSearch) return text;
return text.replace(new RegExp(escapeRegex(currentSearch), 'gi'), m => `<mark>${m}</mark>`);
}
function createConstellationCard(c) {
return `
<div class="db-card" data-constellation="${c.id}">
<div class="db-card-img">
<img src="/static/images/${c.image}" alt="${c.name}" loading="lazy" />
</div>
<div class="db-card-body">
<h3>${highlight(c.name)}</h3>
<p>${highlight(c.description)}</p>
</div>
</div>
`;
}
function createConstellationListItem(c) {
return `
<div class="db-row" data-constellation="${c.id}">
<div class="db-row-img">
<img src="/static/images/${c.image}" alt="${c.name}" loading="lazy" />
</div>
<div>
<h3>${highlight(c.name)}</h3>
<p>${highlight(c.description)}</p>
</div>
</div>
`;
}
function renderConstellations() {
if (filteredData.length === 0) {
gridView.style.display = 'none';
listView.style.display = 'none';
noResults.style.display = 'block';
} else {
noResults.style.display = 'none';
if (currentView === 'grid') {
gridView.style.display = 'grid';
listView.style.display = 'none';
gridView.innerHTML = filteredData.map(createConstellationCard).join('');
} else {
gridView.style.display = 'none';
listView.style.display = 'flex';
listView.innerHTML = filteredData.map(createConstellationListItem).join('');
}
}
filteredCount.textContent = filteredData.length;
}
function applyFilters() {
const term = currentSearch.toLowerCase();
filteredData = constellationData.filter(c => {
const matchesSearch = !term ||
c.name.toLowerCase().includes(term) ||
c.description.toLowerCase().includes(term);
const matchesFilter = currentFilter === 'all' || typeOf(c) === currentFilter;
return matchesSearch && matchesFilter;
});
renderConstellations();
}
searchInput.addEventListener('input', (e) => {
currentSearch = e.target.value.trim();
applyFilters();
});
document.querySelectorAll('.chip').forEach(chip => {
chip.addEventListener('click', () => {
document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
chip.classList.add('active');
currentFilter = chip.dataset.filter;
applyFilters();
});
});
// ── detail modal ──
const modalBackdrop = document.getElementById('modalBackdrop');
const modalImg = document.getElementById('modalImg');
const modalName = document.getElementById('modalName');
const modalDesc = document.getElementById('modalDesc');
const modalType = document.getElementById('modalType');
function openModal(c) {
modalImg.src = `/static/images/${c.image}`;
modalImg.alt = c.name;
modalName.textContent = c.name;
modalDesc.textContent = c.description;
modalType.textContent = typeLabel(c);
modalBackdrop.classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeModal() {
modalBackdrop.classList.remove('open');
document.body.style.overflow = '';
}
document.getElementById('modalClose').addEventListener('click', closeModal);
modalBackdrop.addEventListener('click', (e) => {
if (e.target === modalBackdrop) closeModal();
});
function handleCatalogClick(e) {
const el = e.target.closest('[data-constellation]');
if (!el) return;
const c = constellationData.find(x => x.id == el.dataset.constellation);
if (c) openModal(c);
}
gridView.addEventListener('click', handleCatalogClick);
listView.addEventListener('click', handleCatalogClick);
function setActiveView(view) {
currentView = view;
gridBtn.classList.toggle('active', view === 'grid');
listBtn.classList.toggle('active', view === 'list');
renderConstellations();
}
gridBtn.addEventListener('click', () => setActiveView('grid'));
listBtn.addEventListener('click', () => setActiveView('list'));
document.addEventListener('keydown', (e) => {
if (e.key === '/' && !e.ctrlKey && !e.altKey && !e.metaKey && document.activeElement !== searchInput) {
e.preventDefault();
searchInput.focus();
}
if (e.key === 'Escape') {
if (modalBackdrop.classList.contains('open')) {
closeModal();
} else if (document.activeElement === searchInput) {
searchInput.value = '';
currentSearch = '';
applyFilters();
searchInput.blur();
}
}
});
renderConstellations();
</script>
{% endblock %}