/* ==========================================================================
   colin.cool
   Palette is a histology stain set: crystal-violet darkfield ground, bone
   slide glass, antique brass instrument, one stain per specimen.
   ========================================================================== */

:root {
  --field:      #0A0612;
  --field-2:    #150E24;
  --ink:        #EDE6D8;
  --ink-dim:    #9A8FA8;
  --brass:      #C09A4E;
  --brass-dim:  #7A6436;

  --eosin:      #E8557E;
  --methylene:  #48A0E8;
  --safranin:   #F2793C;
  --malachite:  #3FC0A0;
  --crystal:    #9B6BEF;
  --gentian:    #D46BE0;
  --alarm:      #E2453C;

  --mono: 'IBM Plex Mono', ui-monospace, monospace;
  --sans: 'Archivo', system-ui, sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* .lens and .field both set display, which would otherwise beat [hidden] */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  background:
    radial-gradient(115% 85% at 50% 46%, var(--field-2) 0%, var(--field) 64%),
    var(--field);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

.stage { height: 100%; position: relative; }

/* ---------------------------------------------------------------- stage 1 */

.lens {
  height: 100%;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  transition: opacity .38s ease;
}

.sr {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The bench layer. Same vmin basis as the barrel, so the two never drift
   apart; overflow stays visible so the slide can run past the viewBox. */
.rig {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100vmin;
  height: 100vmin;
  transform: translate(-50%, -50%);
  overflow: visible;
  pointer-events: none;
}

.scope {
  position: relative;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: block;
  border-radius: 50%;
  transition: opacity .3s ease;
  filter: drop-shadow(0 24px 44px rgba(0,0,0,.75));
}

/* 70.4vmin == the 704 viewBox against the bench's 1000 */
.scope-svg {
  display: block;
  width: 70.4vmin;
  height: auto;
}

.scope:focus-visible { outline: 2px solid var(--brass); outline-offset: 12px; }

/* --- instrument line work --- */

.ln {
  fill: none;
  stroke: var(--brass-dim);
  stroke-width: 2;
}
.hair { fill: none; stroke: var(--brass-dim); stroke-width: 1; opacity: .45; }
.dim  { fill: #05030A; stroke: var(--brass-dim); stroke-width: 1; opacity: .55; }

.head   { fill: #0C0818; stroke: var(--brass-dim); stroke-width: 2; }
.collar { fill: #0E0A1C; stroke: var(--brass-dim); stroke-width: 2; }

/* knurling and graduations, drawn as dashes around a circle rather than
   a hundred hand-placed ticks */
/* The two dashed rings counter-rotate, so the graduations drift past the
   knurling. Slow enough to read as machinery idling, not as an animation. */
.knurl {
  fill: none; stroke: var(--brass-dim);
  stroke-width: 34; stroke-dasharray: 6 29.6; opacity: .4;
  transform-box: fill-box;
  transform-origin: center;
  animation: spin-ccw 80s linear infinite;
}
.ticks {
  fill: none; stroke: var(--brass-dim);
  stroke-width: 22; stroke-dasharray: 3 27; opacity: .26;
  transform-box: fill-box;
  transform-origin: center;
  animation: spin-cw 120s linear infinite;
}
@keyframes spin-cw  { to { transform: rotate(360deg); } }
@keyframes spin-ccw { to { transform: rotate(-360deg); } }

.slide { fill: rgba(237,230,216,.055); stroke: rgba(237,230,216,.22); stroke-width: 1; }
.cover { fill: rgba(72,160,232,.05); stroke: rgba(237,230,216,.16); stroke-width: 1; }

.preview { opacity: .4; transition: opacity .35s ease; }
.glint   { fill: none; stroke: rgba(237,230,216,.18); stroke-width: 3; stroke-linecap: round; }

/* the one live part of the instrument, brass at rest, stain on approach */
.pupil {
  fill: none;
  stroke: var(--brass-dim);
  stroke-width: 2;
  transition: stroke .3s ease;
}
.halo {
  fill: none;
  stroke: var(--eosin);
  stroke-width: 2;
  transform-box: fill-box;
  transform-origin: center;
  animation: pulse 3.4s ease-out infinite;
}
@keyframes pulse {
  0%        { transform: scale(1);    opacity: .26; }
  70%, 100% { transform: scale(1.5);  opacity: 0; }
}
.scope:hover .pupil,
.scope:focus-visible .pupil { stroke: var(--eosin); }
.scope:hover .preview,
.scope:focus-visible .preview { opacity: .62; }

/* ---------------------------------------------------------------- stage 2 */

.field {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

#medium { display: block; width: 100%; height: 100%; touch-action: none; }

.back {
  position: absolute;
  top: clamp(1rem, 3vw, 2rem);
  left: clamp(1rem, 3vw, 2rem);
  background: none;
  border: 1px solid var(--brass-dim);
  border-radius: 50%;
  color: var(--brass);
  font-size: 1rem;
  line-height: 1;
  width: 2.5rem;
  height: 2.5rem;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
.back:hover { border-color: var(--brass); color: var(--ink); }
.back:focus-visible { outline: 2px solid var(--brass); outline-offset: 3px; }

.readout {
  position: absolute;
  bottom: clamp(1rem, 3vw, 2.2rem);
  left: 50%;
  transform: translateX(-50%);
  max-width: 88vw;
  font-family: var(--mono);
  font-size: .64rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  text-align: center;
  color: var(--brass-dim);
  pointer-events: none;
}

/* The roster is the manifest AND the no-JS / screen-reader route in. It is
   only hidden once the canvas is actually running. */
.roster {
  list-style: none;
  display: grid;
  gap: .5rem;
  font-family: var(--mono);
  font-size: .9rem;
}
.roster a { color: var(--ink); text-decoration-color: var(--brass-dim); }

.js .roster {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.js .roster a:focus-visible {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: max-content; height: auto;
  clip-path: none;
  background: var(--field-2);
  border: 1px solid var(--brass);
  border-radius: 4px;
  padding: .7rem 1.2rem;
  outline: 2px solid var(--brass);
  outline-offset: 3px;
  z-index: 5;
}

/* ------------------------------------------------------------- badge tray */

/* A rack of slides along the top. Nothing is here until the first find, and
   the empty slots then show how many are left so the hunt has an end. */
.tray {
  position: absolute;
  top: clamp(1rem, 3vw, 2rem);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .4rem;
  max-width: min(92vw, 40rem);
}

.badge, .slot {
  width: 2.1rem;
  height: 1.5rem;
  border-radius: 2px;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: .56rem;
  letter-spacing: .06em;
}

/* not yet found */
.slot {
  border: 1px solid rgba(122,100,54,.22);
  background: rgba(5,3,10,.5);
}

/* found, a stained slide with its label */
.badge {
  border: 1px solid var(--stain, var(--brass-dim));
  color: var(--stain, var(--brass));
  background: color-mix(in srgb, var(--stain, #7A6436) 12%, transparent);
  cursor: default;
  animation: badge-in .45s cubic-bezier(.2,.7,.3,1) both;
}
@keyframes badge-in {
  from { opacity: 0; transform: translateY(-8px) scale(.9); }
}

/* the tray stays quiet until you look at it */
.tray { opacity: .55; transition: opacity .3s ease; }
.tray:hover { opacity: 1; }

/* ----------------------------------------------------------------- keypad */

/* A row of specimen wells. Quiet until you go near it. */
.wells {
  position: absolute;
  right: clamp(1rem, 3vw, 2.4rem);
  bottom: clamp(1rem, 3vw, 2.4rem);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .55rem;
  opacity: .22;
  transition: opacity .35s ease;
}
.wells:hover, .wells:focus-within { opacity: 1; }

.well {
  width: 1.35rem;
  height: 1.35rem;
  padding: 0;
  border: 1px solid var(--brass-dim);
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 30%, rgba(237,230,216,.07), rgba(5,3,10,.65));
  cursor: pointer;
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.well-zero { grid-column: 2; }
.well:hover { border-color: var(--brass); }
.well:focus-visible { outline: 1px solid var(--brass); outline-offset: 3px; }

/* a press stains the well */
.well.lit {
  border-color: var(--safranin);
  box-shadow: 0 0 11px 1px rgba(242,121,60,.55);
  background: radial-gradient(circle at 34% 30%, rgba(242,121,60,.5), rgba(242,121,60,.12));
}

.wells.ok .well {
  border-color: var(--malachite);
  box-shadow: 0 0 13px 2px rgba(63,192,160,.5);
  background: radial-gradient(circle at 34% 30%, rgba(63,192,160,.5), rgba(63,192,160,.12));
}
.wells.bad .well {
  border-color: var(--alarm);
  box-shadow: 0 0 13px 2px rgba(226,69,60,.5);
  background: radial-gradient(circle at 34% 30%, rgba(226,69,60,.5), rgba(226,69,60,.12));
}
/* a known combination, neither the answer nor a miss */
.wells.found .well {
  border-color: var(--crystal);
  box-shadow: 0 0 13px 2px rgba(155,107,239,.45);
  background: radial-gradient(circle at 34% 30%, rgba(155,107,239,.45), rgba(155,107,239,.1));
}

/* --------------------------------------------------------------- the vial */

.vial {
  position: absolute;
  inset: clamp(.75rem, 4vw, 3rem);
  z-index: 6;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  background: rgba(8,5,15,.97);
  border: 1px solid var(--brass-dim);
  border-radius: 3px;
  box-shadow: 0 30px 80px rgba(0,0,0,.7);
  animation: vial-in .32s cubic-bezier(.2,.7,.3,1) both;
}
@keyframes vial-in { from { opacity: 0; transform: translateY(10px); } }

.vial-bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .6rem;
  border-bottom: 1px solid rgba(122,100,54,.3);
}

/* one tab per document, added server side */
.tabs { display: flex; flex: 1; gap: .35rem; flex-wrap: wrap; }

.tab {
  background: none;
  border: 1px solid transparent;
  border-radius: 2px;
  color: var(--brass-dim);
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  padding: .45rem .8rem;
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.tab:hover { color: var(--ink); }
.tab:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
.tab[aria-selected="true"] {
  color: var(--eosin);
  border-color: var(--eosin);
  background: rgba(232,85,126,.08);
}

.copy, .shut {
  background: none;
  border: 1px solid var(--brass-dim);
  border-radius: 2px;
  color: var(--brass);
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  padding: .45rem .9rem;
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
.shut { padding: .45rem .7rem; letter-spacing: 0; }
.copy:hover, .shut:hover { border-color: var(--brass); color: var(--ink); }
.copy:focus-visible, .shut:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
.copy.done { border-color: var(--malachite); color: var(--malachite); }

.vial-body {
  margin: 0;
  padding: clamp(1rem, 3vw, 2rem);
  overflow: auto;
  font-family: var(--mono);
  font-size: .76rem;
  line-height: 1.62;
  color: var(--ink-dim);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  tab-size: 2;
}
.vial-body:focus-visible { outline: 1px solid var(--brass-dim); outline-offset: -1px; }

/* --------------------------------------------------------- the transition */

/* the eyepiece aperture, blown up until it swallows the page */
.aperture {
  position: absolute;
  border-radius: 50%;
  background: var(--field);
  box-shadow: 0 0 0 2px rgba(232,85,126,.5), 0 0 70px 14px rgba(155,107,239,.26);
  pointer-events: none;
  z-index: 3;
}

.entering .lens { opacity: 0; }
.entering .scope { opacity: 0; }

.field { opacity: 0; transition: opacity .5s ease .18s; }
.field.visible { opacity: 1; }
/* focus rack: the specimen resolves as you settle on the eyepiece */
#medium { transition: filter .9s cubic-bezier(.2,.7,.3,1), transform .9s cubic-bezier(.2,.7,.3,1); }
.field.racking #medium { filter: blur(16px); transform: scale(1.12); }

/* ------------------------------------------------------------- responsive */

@media (max-width: 620px) {
  .scope-svg { width: 80vmin; }
}

/* fingers need a bigger well than a cursor does */
@media (pointer: coarse) {
  .wells { gap: .7rem; opacity: .3; }
  .well { width: 2rem; height: 2rem; }
}

/* ---------------------------------------------------------------- motion */

@media (prefers-reduced-motion: reduce) {
  .halo { animation: none; opacity: .45; }
  .knurl, .ticks { animation: none; }
  .badge { animation: none; }
  #medium { transition: none; }
  .field { transition: opacity .2s linear; }
  .field.racking #medium { filter: none; transform: none; }
  .aperture { display: none; }
}
