* { box-sizing: border-box; }

:root {
    --aqua:         #8fd6ea;
    --aqua-bright:  #d6f5ff;
    --aqua-dim:     #5f93a5;
    --ink:          #c2d2dc;
    --ink-dim:      #8198a6;
    --panel-bg:     rgba(12, 26, 40, 0.62);
    --panel-border: #1d3445;
    --panel-edge:   rgba(143, 214, 234, 0.14);
    --glow:         rgba(120, 200, 230, 0.45);
}

html, body {
    margin: 0;
    padding: 0;
    min-height: 100vh;
    /* Deep-water ambient: a soft pool of light spilling from the surface over a
       dark vertical gradient, so the page reads as "underwater" depth instead
       of a flat black void. Fixed so it doesn't slide as the page scrolls. */
    background:
        radial-gradient(135% 90% at 50% -18%, rgba(42, 112, 142, 0.22), transparent 55%),
        linear-gradient(180deg, #07131d 0%, #050b12 45%, #03070b 100%);
    background-attachment: fixed;
    color: var(--ink);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* Anchor the page from the top so it doesn't float in a black void when the
   content is shorter than the viewport — vertical-centering left big dead gaps
   above the title and below the footer. Scrolls normally when taller. */
body {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding: 0.8vh 0 2.6vh;
}

#tank {
    position: relative;
    width: min(92vw, 1600px);
    aspect-ratio: 5 / 3;
    /* Clamp tank height to a fraction of the viewport so on short / small
       laptop screens the controls + legends below the tank stay on-screen.
       Browser honours both width and max-height: it reduces width to keep
       the 5:3 aspect when the height clamp kicks in. */
    max-height: 75vh;
    margin: 0 auto 1vh;
    background:
        radial-gradient(ellipse at 30% 0%, rgba(120,180,220,0.18), transparent 60%),
        linear-gradient(180deg, #1e3a52 0%, #0e2236 70%, #1a1810 100%);
    /* Aquarium frame: a chunky dark tank rim, a thin bright glass highlight
       just inside it, and a deep cast shadow so the whole thing reads as a
       physical lit tank sitting off the page rather than a flush <div>. */
    border: 5px solid #16242f;
    box-shadow:
        inset 0 0 0 1px rgba(143, 214, 234, 0.18),
        inset 0 0 80px rgba(0,0,0,0.72),
        0 18px 52px rgba(0,0,0,0.55),
        0 0 0 1px rgba(0,0,0,0.6);
    overflow: hidden;
    border-radius: 16px;
}

/* Fullscreen aquarium mode — toggled via the "fullscreen" button or 'f'
   key. Hides every chrome element, makes the tank fill the viewport
   edge-to-edge, drops the border / shadow / rounded corners so it's
   truly immersive. The page either enters real browser-fullscreen (Esc
   exits) or just applies this class without the API call, depending on
   whether requestFullscreen succeeds. */
body.fullscreen-tank #stats,
body.fullscreen-tank #controls,
body.fullscreen-tank #legend,
body.fullscreen-tank #plant-legend,
body.fullscreen-tank #orn-legend { display: none; }

/* Read-only mode (proxy connection without ?admin=KEY): hide the write
   surfaces — feeder, reset, day/night toggle, and the OBJECT placement
   legend. The PLANT legend stays visible (informational — visitors get to
   see the aquascape species), just non-interactive. Fish + plant tooltips
   still work. The status bar text in JS also switches to a read-only hint. */
body.read-only #controls,
body.read-only #orn-legend { display: none; }
body.read-only #tank { cursor: pointer; }   /* not crosshair */
/* Plant legend is info-only in read-only — don't make it look clickable. */
body.read-only #plant-legend .item { cursor: default; }
body.read-only #plant-legend .item:hover { background: transparent; border-color: transparent; }
body.fullscreen-tank #tank {
    width: 100vw;
    max-height: none;
    aspect-ratio: auto;
    height: 100vh;
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: inset 0 0 60px rgba(0,0,0,0.7);
}

/* ---- page chrome: title + "what is this" blurb above the tank, a small
   footer below. Aquatic palette to match the tank. Hidden in fullscreen so
   the immersive view stays edge-to-edge; kept in read-only (public) mode —
   that's exactly where a first-time visitor needs the framing. */
#page-head {
    width: min(98vw, 1700px);
    margin: 1vh auto 1.1vh;
    text-align: center;
}
#page-head .kicker {
    font-size: 0.64rem;
    letter-spacing: 0.4em;
    text-transform: uppercase;
    color: var(--aqua-dim);
    /* pad-left offsets the trailing letter-space so the eyebrow reads centered */
    margin: 0 0 0.7rem 0.4em;
}
#page-head h1 {
    margin: 0 0 0.65rem;
    font-size: clamp(2rem, 5.6vw, 3.2rem);
    font-weight: 700;
    letter-spacing: 0.14em;
    /* Gradient wordmark + soft glow — richer than a flat fill. */
    background: linear-gradient(180deg, #e6faff 0%, #8fd6ea 55%, #58b0cd 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    filter: drop-shadow(0 0 20px var(--glow));
}
#page-head .blurb {
    margin: 0 auto;
    max-width: 80ch;
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--ink-dim);
}
#page-head .blurb strong { color: var(--aqua-bright); font-weight: 600; }

#page-foot {
    width: min(94vw, 1080px);
    margin: 2.6vh auto 0;
    padding-top: 1.2vh;
    border-top: 1px solid var(--panel-border);
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.72rem;
    color: var(--ink-dim);
}
#page-foot a { color: var(--aqua); text-decoration: none; border-bottom: 1px dotted #35505e; }
#page-foot a:hover { color: var(--aqua-bright); border-color: var(--aqua-bright); }

body.fullscreen-tank #page-head,
body.fullscreen-tank #page-foot { display: none; }

/* Suspended particles drifting through the water volume. Pure-CSS
   animation; positions, sizes, opacities and drift vectors are randomised
   per-mote in JS so the layer feels like real suspended matter. */
#water-fx {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
    z-index: 0;
}

.mote {
    position: absolute;
    border-radius: 50%;
    background: #c2dcf0;
    box-shadow: 0 0 3px rgba(190, 220, 240, 0.45);
    animation: drift 30s ease-in-out infinite;
    will-change: transform;
}

@keyframes drift {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(var(--dx), var(--dy)); }
}

/* Z-order, back to front:
 *   water-fx (0) → substrate bg → sand-surface (1) → fish-layer (2)
 *   → plant-layer (3) → night/golden/noon overlays (6/7) → status (10+).
 * So fish swim OVER both substrate layers but are hidden BEHIND plants. */
#plant-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 3;
}

#fish-layer {
    position: absolute;
    inset: 0;
    z-index: 2;
}

.plant {
    position: absolute;
    bottom: 5%;
    transform-origin: bottom center;
    pointer-events: auto;
    cursor: pointer;
    animation: sway 5s ease-in-out infinite;
    opacity: 0.9;
    /* Growth: clip-path reveals only the lower --growth% of the sprite, so
       saplings appear as a tiny stub at the substrate that gains height
       (and leaves higher up in the sprite) as it grows. */
    --growth: 100%;
    clip-path: inset(calc(100% - var(--growth, 100%)) 0 0 0);
    /* Death: single hue-rotate green → warm + opacity fade. Originally
       this was a 4-stage filter chain (drop-shadow + hue-rotate +
       saturate + brightness) with a 12s transition, which forced Chrome
       to rasterize every plant on the CPU every frame and burned 2+
       cores in a single tab. drop-shadow ALONE composites on the GPU;
       chaining filters breaks that. So: shadow lives on the sprite-
       wrapper SVG (cheap), death is a single GPU-friendly filter +
       opacity, and we transition only opacity (cheap CSS prop). */
    --death: 0;
    filter: hue-rotate(calc(var(--death) * -0.45deg));
    opacity: calc(1 - var(--death) * 0.006);
    transition: clip-path 12s linear, opacity 12s linear;
    contain: layout paint;
}
.plant svg {
    display: block;
    overflow: visible;
    /* Fill container height; auto width preserves aspect from viewBox. */
    height: 100%;
    width: auto;
}

/* Tank ornaments — static substrate decor, no growth/sway/clip-path. */
.ornament {
    position: absolute;
    bottom: 5%;
    transform: translateX(-50%);
    pointer-events: auto;
    cursor: pointer;
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
    z-index: 3;
}
.ornament svg {
    display: block;
    overflow: visible;
    height: 100%;
    width: auto;
}
.plant.v1 { animation-duration: 6.5s; animation-delay: -1.5s; }
.plant.v2 { animation-duration: 7s;   animation-delay: -3s; }
.plant.v3 { animation-duration: 5.5s; animation-delay: -4s; }

@keyframes sway {
    0%, 100% { transform: rotate(-2.5deg); }
    50%      { transform: rotate( 2.5deg); }
}

/* ---- Bubbles ----
   Each bubble positions itself directly in the tank via --bx (set as a
   percentage by JS on every animation iteration), so successive rises
   come up at a fresh random x rather than from fixed emitter points. */
.bubble {
    position: absolute;
    left: var(--bx, 50%);
    margin-left: -3px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.85), rgba(180,220,255,0.15));
    box-shadow: 0 0 4px rgba(200,230,255,0.4);
    opacity: 0;
    bottom: 5%;
    animation: rise 5s ease-in infinite;
    pointer-events: none;
}

@keyframes rise {
    0%   { bottom: 5%;  opacity: 0;   transform: translateX(0); }
    8%   {              opacity: 0.7; }
    50%  {              transform: translateX(3px); }
    92%  {              opacity: 0.5; }
    100% { bottom: 95%; opacity: 0;   transform: translateX(-3px); }
}

/* Fish + food render to a single <canvas> inside #fish-layer — DOM-sprite
   approach burned ~25% CPU on a 7950X with 50 fish (5000+ style recalcs
   per frame from per-element transforms and filter chains). Canvas paint
   loop is ~10x cheaper. Tail wag is baked into the static sprite for now;
   swim-bob is a sine offset applied per-frame in JS. The drop-shadow that
   was per-fish is gone — too expensive at scale, and the sprite atlas
   already has subtle shading. */
#fish-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;   /* TANK handles clicks; we hit-test fish in JS */
}

#legend .glyph svg {
    display: inline-block;
    vertical-align: middle;
    overflow: visible;
}

.substrate {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 5%;
    background: linear-gradient(180deg, rgba(60,40,20,0.6), rgba(40,28,12,1));
    pointer-events: none;
}

/* Slowly-shifting sand surface. SVG path morphs between three wave shapes
   over 90s via SMIL; sits on top of the substrate so plants still root
   into solid ground but the visible top edge undulates. */
.sand-surface {
    position: absolute;
    bottom: 4.2%;
    left: 0;
    width: 100%;
    height: 2.2%;
    pointer-events: none;
    z-index: 1;
}

/* Noon overlay: vibrant cyan/aqua "sunlight through water" from the
   surface. Peaks at phase 0.5; ramps in/out with --noon (set in JS). */
.noon-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 6;
    background: radial-gradient(ellipse 95% 85% at 50% -5%,
        rgba(140, 250, 230, 0.65) 0%,
        rgba(90, 220, 210, 0.42) 35%,
        rgba(60, 190, 200, 0.18) 75%,
        transparent 100%);
    opacity: var(--noon, 0);
    transition: opacity 0.6s linear;
    mix-blend-mode: screen;
}

/* Sunbeams: angled light shafts sweeping down from the surface, driven
   by --noon (only visible during the day). Each beam pulses on its own
   timer + rotation for a shifting, caustic-light feel. */
.sunbeams {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
    opacity: var(--noon, 0);
    transition: opacity 0.6s linear;
    z-index: 6;
    mix-blend-mode: screen;
}
.sunbeams .beam {
    position: absolute;
    top: -15%;
    left: var(--bx, 50%);
    width: 56px;
    height: 140%;
    background: linear-gradient(180deg,
        rgba(255, 250, 220, 0.50),
        rgba(255, 250, 220, 0.20) 50%,
        transparent);
    transform-origin: top center;
    transform: rotate(8deg);
    /* No blur — Chrome's CSS blur is per-frame CPU work, and 5 beams
       with infinite animations were a top-tier hotspot. The gradient
       is wide and soft enough on its own. */
    animation: beam-pulse 7s ease-in-out infinite;
    animation-delay: var(--bd, 0s);
    will-change: opacity, transform;
}
@keyframes beam-pulse {
    0%, 100% { opacity: 0.55; transform: rotate(5deg)  translateX(-6px); }
    50%      { opacity: 1;    transform: rotate(11deg) translateX(8px); }
}

/* Golden overlay: warm reds/oranges from the surface for golden hour
   (dawn ~0.25 and dusk ~0.75). Sharp peaks via --golden. Brighter +
   more saturated than the original so dawn/dusk really glow. */
.golden-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 6;
    background:
        linear-gradient(180deg,
            rgba(255, 105, 145, 0.45) 0%,
            rgba(255, 70, 80, 0.15) 18%,
            transparent 30%),
        linear-gradient(180deg,
            rgba(255, 110, 30, 0.90) 0%,
            rgba(255, 75, 20, 0.65) 28%,
            rgba(220, 50, 15, 0.38) 55%,
            rgba(90, 25, 10, 0.08) 100%);
    opacity: var(--golden, 0);
    transition: opacity 0.6s linear;
    mix-blend-mode: screen;
}

/* Night dim. Cool blue moonlight rather than pitch black. */
.night-overlay {
    position: absolute;
    inset: 0;
    background: rgba(12, 22, 44, 0.42);
    pointer-events: none;
    z-index: 7;
    opacity: calc(1 - var(--day, 1));
    transition: opacity 0.6s linear;
    mix-blend-mode: multiply;
}

#tank { cursor: crosshair; }

/* Food rendered on #fish-canvas alongside fish — see canvas notes above. */

#status {
    position: absolute;
    top: 8px;
    left: 12px;
    font-size: 12px;
    color: #6a8;
    z-index: 10;
    letter-spacing: 0.05em;
}

/* In-tank clock showing current day/night phase as HH:MM (half-hour
   granularity, driven by the `ph` field on tick messages). */
#clock {
    position: absolute;
    top: 8px;
    right: 12px;
    z-index: 11;
    font-size: 14px;
    color: #cde;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.05em;
    text-shadow: 0 0 4px rgba(0,0,0,0.85), 0 0 2px rgba(0,0,0,0.9);
    opacity: 0.7;
    pointer-events: none;
}

/* Stats — an instrument readout strip: contained panel matching the tank
   chrome, micro-labelled fields separated by thin rules. */
#stats {
    width: auto;
    max-width: min(94vw, 1080px);
    margin: 1.5vh auto 0;
    display: flex;
    gap: 0.3em 0;
    flex-wrap: wrap;
    justify-content: center;
    font-size: 12px;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 10px;
    padding: 0.55em 1em;
    box-shadow: inset 0 0 24px rgba(0,0,0,0.35);
}

#controls {
    width: min(98vw, 1700px);
    margin: 1.2vh auto 0;
    display: flex;
    gap: 0.5em;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
}

#controls button {
    background: transparent;
    border: 1px solid #345;
    color: #8aa;
    font: inherit;
    font-size: 12px;
    padding: 4px 10px;
    border-radius: 3px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
#controls button:hover {
    background: rgba(200, 80, 80, 0.18);
    border-color: #a55;
    color: #fbb;
}

#feeder,
#daynight {
    display: flex;
    align-items: center;
    gap: 0.5em;
    color: #cde;
    font-size: 12px;
    border: 1px solid #345;
    border-radius: 3px;
    padding: 3px 8px;
}
#daynight label { cursor: pointer; user-select: none; }
#daynight input[type="checkbox"] {
    margin-right: 0.3em;
    accent-color: #5c8;
    cursor: pointer;
}
#daynight select {
    background: #0c1620;
    border: 1px solid #345;
    color: #cde;
    padding: 2px 4px;
    border-radius: 3px;
    font: inherit;
    font-size: 12px;
    cursor: pointer;
}
#feeder .muted { color: #678; }
#feeder label { cursor: pointer; user-select: none; }
#feeder input[type="checkbox"] {
    margin-right: 0.3em;
    accent-color: #5c8;
    cursor: pointer;
}
#feeder input[type="number"] {
    width: 4em;
    background: transparent;
    border: 1px solid #345;
    color: #cde;
    padding: 2px 4px;
    border-radius: 3px;
    font: inherit;
    font-variant-numeric: tabular-nums;
}
#feeder input[type="number"]:focus { outline: 1px solid #4a7; border-color: #4a7; }
#feeder select,
#preset-sel {
    background: #0c1620;
    border: 1px solid #345;
    color: #cde;
    padding: 2px 4px;
    border-radius: 3px;
    font: inherit;
    font-size: 12px;
    cursor: pointer;
}

/* Click-a-fish / click-a-plant tooltip — shown briefly on left-click. */
#fish-tip,
#plant-tip {
    position: fixed;
    transform: translate(-50%, 0);
    background: rgba(10, 18, 30, 0.94);
    border: 1px solid rgba(120, 160, 200, 0.3);
    padding: 6px 10px;
    border-radius: 4px;
    color: #cde;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
    z-index: 100;
    opacity: 0;
    transition: opacity 0.15s;
    white-space: nowrap;
}
#fish-tip.show, #plant-tip.show { opacity: 1; }
#fish-tip .tip-name, #plant-tip .tip-name {
    font-size: 13px;
    font-weight: 600;
    margin-bottom: 2px;
}
#fish-tip .tip-detail, #plant-tip .tip-detail { color: #99b; }

#stats .stat label {
    color: var(--aqua-dim);
    margin-right: 0.45em;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    font-size: 10px;
}

#stats .stat {
    white-space: nowrap;
    padding: 0 0.9em;
    border-right: 1px solid var(--panel-edge);
}
#stats .stat:last-child { border-right: 0; }

/* Species legend — a contained panel matching the tank chrome, laid out as an
   even grid so the fish align in tidy columns instead of orphaning a stray
   half-row. The ::before is a centered section caption spanning all columns. */
#legend {
    width: auto;
    max-width: min(900px, 94vw);
    margin: 1.2vh auto 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.3em 1.4em;
    font-size: 13px;
    color: var(--ink);
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 12px;
    padding: 0.85em 1.3em 1em;
    box-shadow: inset 0 0 30px rgba(0,0,0,0.32), 0 6px 22px rgba(0,0,0,0.28);
}
#legend::before {
    content: "species in this tank";
    flex-basis: 100%;          /* forces its own full-width row above the items */
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.24em;
    font-size: 0.6rem;
    color: var(--aqua-dim);
    margin-bottom: 0.5em;
}

/* Plant + ornament legends — same panel language as the fish legend so the
   admin view reads as one consistent, evenly-spaced stack. Click a species /
   ornament to make it the "next placed" choice, then left-click the tank's
   substrate band to drop one. The plant legend is also shown (read-only) on
   the public site. */
#plant-legend,
#orn-legend {
    width: auto;
    max-width: min(900px, 94vw);
    margin: 1.2vh auto 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.3em 1.2em;
    font-size: 13px;
    color: var(--ink);
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 12px;
    padding: 0.85em 1.3em 1em;
    box-shadow: inset 0 0 30px rgba(0,0,0,0.32), 0 6px 22px rgba(0,0,0,0.28);
}
#plant-legend::before,
#orn-legend::before {
    flex-basis: 100%;
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.24em;
    font-size: 0.6rem;
    color: var(--aqua-dim);
    margin-bottom: 0.5em;
}
#plant-legend::before { content: "plants in this tank"; }
#orn-legend::before  { content: "objects \00b7 click, then place in the tank"; }
#plant-legend .item,
#orn-legend .item {
    display: flex;
    align-items: center;
    gap: 0.5em;
    cursor: pointer;
    user-select: none;
    padding: 0.3em 0.55em;
    border-radius: 6px;
    border: 1px solid transparent;
    color: var(--ink);
    transition: background-color 0.12s, border-color 0.12s;
}
#plant-legend .item:hover,
#orn-legend .item:hover {
    background: rgba(143, 214, 234, 0.10);
}
#plant-legend .item.selected,
#orn-legend .item.selected {
    background: rgba(80, 180, 110, 0.16);
    border-color: #4a8;
    color: var(--aqua-bright);
}
/* Fixed-aspect cell that contains a scaled SVG. The cell is sized in
   CSS; the SVG inside gets width/height: 100% so its viewBox scales to
   fit (default preserveAspectRatio="xMidYMid meet"). overflow: hidden
   clips any sprite-overflow content so it can't bleed into the text. */
#plant-legend .glyph,
#orn-legend .glyph {
    display: inline-block;
    width: 30px;
    height: 36px;
    overflow: hidden;
    flex-shrink: 0;
    vertical-align: middle;
}
#plant-legend .glyph svg,
#orn-legend .glyph svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: hidden;
}

#legend .item {
    display: flex;
    align-items: center;
    gap: 0.55em;
    cursor: pointer;
    user-select: none;
    padding: 0.3em 0.5em;
    border-radius: 6px;
    color: var(--ink);
    transition: background-color 0.12s;
}
#legend .item:hover {
    background: rgba(143, 214, 234, 0.10);
}
#legend .item:active {
    background: rgba(143, 214, 234, 0.18);
}

#legend .glyph {
    font-size: 18px;
}
