/* ============================================================
   Gallery — 3-D label surfaces and count-free distributions.
   Inherits the site's tokens (paper ground, ink type, teal/ochre);
   adds only what the viewers and charts need.
   ============================================================ */

.gal__lead {
  max-width: 62ch;
  color: var(--ink-soft);
  margin-bottom: 2rem;
}

.gal__n {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  color: var(--ink-faint);
  display: block;
  margin-top: 0.4rem;
}

/* ---------------------------------------------------------- cases */
.gal__cases {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(310px, 100%), 1fr));
  gap: 1.6rem;
  margin-bottom: 3.2rem;
}

.gal__case {
  background: var(--paper-card);
  border: 1px solid var(--line-soft);
  border-radius: 3px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* the turntable stage. A fixed aspect keeps the grid from reflowing as frames of
   different sizes load -- the renders are cropped to each specimen, so their native
   proportions differ from case to case. */
.gal__stage {
  position: relative;
  aspect-ratio: 4 / 5;
  background:
    radial-gradient(120% 90% at 50% 0%, #ffffff 0%, var(--paper-deep) 100%);
  display: block;
  /* ONE FINGER SCROLLS THE PAGE, two fingers turn the specimen. touch-action:none
     here made the viewer swallow every vertical swipe that began on it, so the only way
     past the gallery on a phone was to find a gap between cards. */
  touch-action: pan-y;
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
}
.gal__stage.is-dragging { cursor: grabbing; }
.gal__stage:focus-visible { outline: 2px solid var(--ochre); outline-offset: 3px; }

/* contain, not cover: cropping a specimen to fill a box would cut off the very rib
   or transitional segment the case is an example of */
.gal__frame {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  -webkit-user-drag: none;
  pointer-events: none;
}

.gal__stage.is-loading::after,
.gal__spin {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 22px; height: 22px;
  border: 2px solid var(--line);
  border-top-color: var(--teal);
  border-radius: 50%;
  animation: gal-spin 0.9s linear infinite;
  content: "";
}
.gal__stage:not(.is-loading) .gal__spin { display: none; }
@keyframes gal-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .gal__spin { animation-duration: 3s; }
}

.gal__meta { padding: 1.05rem 1.15rem 1.15rem; display: flex; flex-direction: column; gap: 0.5rem; }
.gal__meta h3 {
  font-family: var(--font-display);
  font-size: 1.14rem;
  line-height: 1.25;
  color: var(--ink);
}
.gal__meta p { font-size: 0.9rem; color: var(--ink-soft); line-height: 1.55; }

.gal__row { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; margin-top: 0.2rem; }

.gal__btn {
  font-family: var(--font-body);
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  padding: 0.42rem 0.85rem;
  border: 1px solid var(--teal);
  background: transparent;
  color: var(--teal);
  border-radius: 2px;
  cursor: pointer;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.gal__btn:hover, .gal__btn:focus-visible { background: var(--teal); color: var(--paper-card); }
.gal__btn.is-on { background: var(--teal-deep); border-color: var(--teal-deep); color: var(--paper-card); }
.gal__btn:focus-visible { outline: 2px solid var(--ochre); outline-offset: 2px; }

.gal__id { font-family: var(--font-mono); font-size: 0.74rem; color: var(--ink-faint); }

/* an object movie has no chrome to announce itself, so the affordance has to be said */
.gal__hint {
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  color: var(--teal-deep);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.gal__hint::before {
  content: "";
  width: 15px; height: 15px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  border-left-color: transparent;
  border-right-color: transparent;
}

.gal__err { padding: 1rem; font-size: 0.85rem; color: var(--ochre); }

/* ---------------------------------------------------------- distributions */
.gal__dist {
  display: grid;
  /* WIDE panels. A 760-unit viewBox squeezed into a 330px card scales the type down to
     about 8px; at 560px+ the scale factor is near 1 and the labels read at their design
     size. This is the fix for "the axis titles are unreadable" -- not a bigger font. */
  grid-template-columns: repeat(auto-fit, minmax(min(560px, 100%), 1fr));
  gap: 1.6rem 1.8rem;
}

.gal__panel {
  background: var(--paper-card);
  border: 1px solid var(--line-soft);
  border-radius: 3px;
  padding: 1.05rem 1.15rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.gal__panel--wide { grid-column: 1 / -1; }

.gal__panel h4 {
  font-family: var(--font-display);
  font-size: 1.02rem;
  line-height: 1.3;
  color: var(--ink);
}
.gal__sub { font-size: 0.82rem; color: var(--ink-faint); line-height: 1.45; }
.gal__panel figcaption {
  font-size: 0.83rem;
  color: var(--ink-soft);
  line-height: 1.5;
  margin-top: 0.35rem;
}

/* wide charts scroll inside their own box rather than the page */
.gal__svg { width: 100%; height: auto; display: block; margin-top: 0.4rem; overflow: visible; }

.gal__bar { fill: var(--teal); opacity: 0.82; transition: opacity 0.12s var(--ease); }
.gal__bar:hover { opacity: 1; }
.gal__axis { stroke: var(--line); stroke-width: 1; }
.gal__tick { font-family: var(--font-mono); font-size: 9px; fill: var(--ink-faint); }
.gal__dot { fill: var(--ink-faint); opacity: 0.34; }
.gal__dot--flag { fill: var(--ochre); opacity: 0.95; }

.gal__note {
  margin-top: 2rem;
  padding-left: 1rem;
  border-left: 2px solid var(--ochre);
  max-width: 66ch;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

/* --- chart type sized to survive scaling into a card ---------------------
   The first version drew 9px ticks into a 520-unit viewBox, which lands near
   6px on screen. These are drawn into a 760-unit box at 17-19px so they stay
   legible at any card width. */
.gal__tick    { font-family: var(--font-mono); font-size: 17px; fill: var(--ink-soft); }
.gal__axtitle { font-family: var(--font-body); font-size: 19px; fill: var(--ink); letter-spacing: 0.01em; }
.gal__grid    { stroke: var(--line-soft); stroke-width: 1; }
.gal__axis    { stroke: var(--line); stroke-width: 1.5; }

/* the count printed on each bar: the reader gets the number without reading the axis */
.gal__val { font-family: var(--font-mono); font-size: 18px; fill: var(--ink); font-weight: 600; }
.gal__tick    { font-size: 19px; }
.gal__axtitle { font-size: 22px; font-weight: 600; }

/* fitted density curves: a filled area under a firm line, with the observations
   themselves ticked along the axis so the model never stands on its own */
.gal__dens-fill { fill: var(--teal); fill-opacity: 0.16; stroke: none; }
.gal__dens-line { fill: none; stroke: var(--teal-deep); stroke-width: 2.4; stroke-linejoin: round; }
.gal__rug { stroke: var(--ink-faint); stroke-width: 1; opacity: 0.5; }

/* Control legend. An orbit viewer has no chrome that announces what it does, and the
   previous version answered that by spinning on its own -- which took control away from
   the reader instead of handing it to them. Naming the gestures does the job without
   moving anything. */
.gal__keys {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  list-style: none;
  margin: 0.15rem 0 0;
  padding: 0;
  font-size: 0.76rem;
  color: var(--ink-faint);
  letter-spacing: 0.01em;
}
.gal__keys b {
  font-weight: 600;
  color: var(--teal-deep);
  font-variant: small-caps;
  letter-spacing: 0.05em;
  margin-right: 0.28rem;
}
/* the fallback can only rotate, so it says only that */
.gal__keys--limited { color: var(--ochre); }

.gal__stage canvas { display: block; width: 100%; height: 100%; cursor: grab; }
.gal__stage canvas:active { cursor: grabbing; }

/* ============================================================
   Viewer chrome. A bone render with no frame of reference is a picture; with axis
   labels, named standard views and a scale bar it is something a reader can check a
   claim against. All of it sits OVER the canvas so none of it costs layout height.
   ============================================================ */

.gal__stage canvas { display: block; width: 100%; height: 100%; cursor: grab; }
.gal__stage canvas:active { cursor: grabbing; }

/* anatomical axes. S/I/L/R rather than an XYZ triad: nobody reads a spine in x. */
.gal__gizmo {
  position: absolute; inset: 0; pointer-events: none;
  font-family: var(--font-mono); font-size: 0.66rem; font-weight: 600;
  color: var(--ink-faint); letter-spacing: 0.06em;
}
/* Each label is placed from the CENTRE by the transform the viewer computes, so the
   letters track the specimen as it turns. Fixed corners would be lying the moment the
   camera moved off the one view they were written for. */
.gal__gizmo .gz {
  position: absolute; top: 50%; left: 50%;
  margin: -0.4em 0 0 -0.35em;
  will-change: transform;
}

/* scale bar: recomputed every frame, because it is only true for one camera distance */
.gal__scale {
  position: absolute; left: 14px; bottom: 14px; pointer-events: none;
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); font-size: 0.68rem; color: var(--ink-soft);
}
.gal__scale i {
  display: block; height: 6px; width: 0;
  border: 1px solid var(--ink-soft); border-top: 0;
  transition: width 0.05s linear;
}

/* the case's phenotype, said once, where the eye lands first */
.gal__tag {
  position: absolute; top: 12px; right: 12px;
  font-family: var(--font-body); font-size: 0.68rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--paper-card);
  background: var(--teal-deep); padding: 0.22rem 0.5rem; border-radius: 2px;
  opacity: 0.92;
}

/* standard views, named as a radiologist would ask for them */
.gal__bar {
  display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap;
  padding: 0.5rem 0.7rem;
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  background: var(--paper-deep);
}
.gal__vb {
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.02em;
  padding: 0.28rem 0.5rem; border: 1px solid transparent; border-radius: 2px;
  background: transparent; color: var(--ink-soft); cursor: pointer;
  transition: background 0.14s var(--ease), color 0.14s var(--ease);
}
.gal__vb:hover { background: var(--paper-card); color: var(--ink); }
.gal__vb.is-on { background: var(--teal-deep); color: var(--paper-card); }
.gal__vb:focus-visible { outline: 2px solid var(--ochre); outline-offset: 1px; }
.gal__vb--wide { margin-left: auto; font-family: var(--font-body); font-size: 0.74rem; }
.gal__bar-sep { flex: 1 1 auto; }

/* group toggles: any spine hides its own posterior elements from most angles, so
   controlling occlusion is not decoration */
.gal__groups { display: flex; flex-wrap: wrap; gap: 0.3rem 0.9rem; margin: 0.2rem 0 0.1rem; }
.gal__grp {
  display: inline-flex; align-items: center; gap: 0.32rem;
  font-size: 0.76rem; color: var(--ink-soft); cursor: pointer; user-select: none;
}
.gal__grp input { accent-color: var(--teal-deep); width: 13px; height: 13px; }
.gal__grp em { font-family: var(--font-mono); font-size: 0.68rem; color: var(--ink-faint); font-style: normal; }

.gal__degraded { color: var(--ochre); }

/* section headings inside the distributions block */
.gal__secttl {
  font-family: var(--font-display); font-size: 1.3rem; color: var(--ink);
  margin: 2.4rem 0 0.2rem; padding-top: 1.4rem; border-top: 1px solid var(--line-soft);
}
.gal__secttl:first-child { margin-top: 0.6rem; border-top: 0; padding-top: 0; }
.gal__secnote { max-width: 64ch; color: var(--ink-soft); font-size: 0.9rem; margin-bottom: 0.9rem; }

/* a published reference value drawn on the axis, so a distribution can be read against
   what is known rather than only against itself */
.gal__ref { stroke: var(--ochre); stroke-width: 2; stroke-dasharray: 6 5; opacity: 0.85; }
.gal__reftxt { font-family: var(--font-mono); font-size: 15px; fill: var(--ochre); }

/* Overlaid densities. Two series is a comparison (female against male); five or six is
   a GRADIENT down the spine, and the ramp has to read in order at a glance -- so it runs
   cool to warm rather than through arbitrary hues. Fills stay light because six stacked
   translucent areas otherwise turn to mud. */
.gal__s0-fill { fill: #1c6b73; fill-opacity: 0.17; }
.gal__s0-line { fill: none; stroke: #1c6b73; stroke-width: 2.2; }
.gal__s1-fill { fill: #c98a1e; fill-opacity: 0.15; }
.gal__s1-line { fill: none; stroke: #c98a1e; stroke-width: 2.2; }
.gal__s2-fill { fill: #4b8f3f; fill-opacity: 0.14; }
.gal__s2-line { fill: none; stroke: #4b8f3f; stroke-width: 2.2; }
.gal__s3-fill { fill: #8c5bb0; fill-opacity: 0.14; }
.gal__s3-line { fill: none; stroke: #8c5bb0; stroke-width: 2.2; }
.gal__s4-fill { fill: #c1502e; fill-opacity: 0.14; }
.gal__s4-line { fill: none; stroke: #c1502e; stroke-width: 2.2; }
.gal__s5-fill { fill: #2f6fae; fill-opacity: 0.14; }
.gal__s5-line { fill: none; stroke: #2f6fae; stroke-width: 2.2; }

/* download progress: a few megabytes with no signal reads as a dead page */
.gal__pct {
  position: absolute; inset: 0; margin: auto;
  width: 4em; height: 1.2em; top: calc(50% + 26px);
  text-align: center;
  font-family: var(--font-mono); font-size: 0.74rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.gal__stage:not(.is-loading) .gal__pct { display: none; }

/* the touch equivalents, shown only where there is a touch screen */
.gal__touch { display: none; color: var(--ink-faint); }
@media (hover: none) and (pointer: coarse) {
  .gal__touch { display: inline; }
}

/* the reference band sits under the ridges as ground, not as another series */
.gal__refband { fill: var(--ochre); fill-opacity: 0.10; }

/* Uncertainty. Drawn UNDER the estimate it belongs to, in the same hue but lighter, so
   it reads as the estimate being uncertain rather than as a second series. */
.gal__ci-band { fill: var(--teal-deep); fill-opacity: 0.16; stroke: none; }
.gal__whisk { stroke: var(--ink); stroke-width: 1.4; opacity: 0.65; }

/* The construction diagram under a panel. Collapsed by default -- forty-seven open
   diagrams would bury the plots they explain. */
.gal__howto {
  margin-top: .7rem;
  border-top: 1px solid rgba(26, 28, 24, .12);
  padding-top: .5rem;
}

/* The construction diagram, inset in the plot it explains. */
.gal__inset-bg {
  fill: var(--paper, #f4f2ec);
  stroke: var(--line-soft, rgba(26, 28, 24, .16));
  stroke-width: 1;
}
.gal__inset-cap {
  font-family: var(--font-mono);
  font-size: 13px;
  fill: var(--ink-faint);
  letter-spacing: .04em;
}

/* superseded by the rules above; kept harmless for any cached markup */
.gal__howto > summary {
  cursor: pointer;
  font-size: .84rem;
  letter-spacing: .01em;
  opacity: .78;
  list-style: none;
}
.gal__howto > summary::-webkit-details-marker { display: none; }
.gal__howto > summary::before {
  content: "\25B8";
  display: inline-block;
  margin-right: .45rem;
  transition: transform .15s ease;
}
.gal__howto[open] > summary::before { transform: rotate(90deg); }
.gal__howto > summary:hover { opacity: 1; }
.gal__howto > summary:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
.gal__howto img {
  display: block;
  width: 100%;
  height: auto;
  margin-top: .6rem;
  border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .gal__howto > summary::before { transition: none; }
}

/* The inset grows where it sits. No modal, no second surface. */
.gal__inset.is-clickable { cursor: zoom-in; }
.gal__inset.is-clickable:hover .gal__inset-bg,
.gal__inset.is-clickable:focus-visible .gal__inset-bg {
  stroke: currentColor;
  stroke-width: 1.6;
}
.gal__inset.is-zoomed { cursor: zoom-out; }
.gal__inset.is-zoomed .gal__inset-bg {
  stroke: currentColor;
  stroke-width: 1.2;
  filter: drop-shadow(0 6px 18px rgba(20, 22, 18, .30));
}
.gal__inset.is-clickable:focus-visible { outline: none; }
@media (prefers-reduced-motion: reduce) {
  .gal__inset { transition: none !important; }
}

/* A threshold that is part of a definition, not decoration. */
.gal__thresh {
  stroke: var(--ochre, #b8860b);
  stroke-width: 1.6;
  stroke-dasharray: 7 5;
  opacity: .85;
}
