/* Product library: pinned frame, zone nav, hidden list, page states. Tokens and the font come from styles.css. */

.body-slot { touch-action: pan-y pinch-zoom; cursor: grab; }
.body-slot:active { cursor: grabbing; }
.body-controls {
  grid-area: controls;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 0;
}
.body-controls button,.view-products {
  min-width: 44px;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--control-border);
  border-radius: 6px;
  color: var(--ink);
  background: var(--paper);
  font-size: var(--text-body);
}
.body-controls button:hover,.body-controls button:active { background: var(--accent); }
.body-controls p { flex-basis: 100%; margin: 0; text-align: center; font-size: var(--text-meta); color: var(--muted); }
.view-products { display: none; }

:root {
  --header-h: 0px;
  --pin-top: 0px;
  --strip-h: clamp(140px, 24vh, 220px);
}
/* A custom property never falls back at use time, so the svh value only replaces the vh value where svh parses. */
@supports (height: 1svh) {
  :root {
    --strip-h: clamp(140px, 24svh, 220px);
  }
}
html {
  scroll-behavior: auto;
  scroll-padding-top: calc(var(--header-h) + 16px);
  scrollbar-gutter: stable;
}

/* One pinned frame, the first child of .shelf-track. Rows above the zone nav scroll away (--pin-top is negative),
   the rest stays put while the page scrolls through the track, whose height the shelf owns. Phone layout first. */
.library-pin {
  position: sticky;
  top: var(--pin-top);
  height: calc(100vh - var(--pin-top));
  height: calc(100dvh - var(--pin-top));
  display: grid;
  gap: 0 16px;
  padding: 0 var(--gutter);
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto auto var(--strip-h) auto minmax(0, 1fr);
  grid-template-areas: "lead lead" "key pause" "nav nav" "body body" "search search" "shelf shelf";
}
.library-lead {
  grid-area: lead;
  padding: 28px 0 18px;
}
.library-lead .intro-note {
  margin-top: 14px;
  max-width: 70ch;
  font-size: var(--text-body);
  line-height: 1.6;
}
.stock-key {
  grid-area: key;
  align-self: center;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin: 0;
  padding: 0 0 12px;
  list-style: none;
  font-size: var(--text-label);
  color: var(--muted);
}
.stock-key li {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 24px;
}
.stock-key li::before {
  content: '';
  width: 14px;
  height: 3px;
  background: var(--control-border);
}
.stock-key .key-in-stock::before {
  background: var(--stock-dot);
}
.stock-key .key-low-stock::before {
  background: var(--low-stock);
}
.pause-toggle {
  grid-area: pause;
  align-self: start;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--target);
  padding: 0 14px;
  border: 1px solid var(--control-border);
  background: transparent;
  color: var(--ink);
  font-size: var(--text-meta);
  white-space: nowrap;
}
.pause-toggle::before {
  content: '';
  width: 8px;
  height: 10px;
  border-left: 3px solid;
  border-right: 3px solid;
}
.pause-toggle[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
}
.zone-nav {
  grid-area: nav;
  min-width: 0;
  display: flex;
  gap: 6px;
  align-items: center;
  overflow-x: auto;
  scrollbar-width: thin;
  padding: 8px 0;
  background: var(--paper);
}
.zone-nav a {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--target);
  padding: 0 13px;
  border: 1px solid var(--line);
  font-size: var(--text-meta);
  white-space: nowrap;
}
.zone-nav a:focus-visible {
  outline-offset: -4px;
}
.zone-nav a span {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.zone-nav a:hover {
  background: #eef0e9;
}
.zone-nav a[aria-current] {
  background: var(--accent);
  border-color: var(--accent);
}
.zone-nav a[aria-disabled="true"] {
  opacity: .45;
  cursor: default;
}
.zone-nav a[aria-disabled="true"]:hover {
  background: transparent;
}
.search-box {
  grid-area: search;
  min-width: 0;
  padding: 6px 0 0;
}
.search-box .search-field {
  display: block;
  max-width: none;
}
/* 16px at every width: smaller type makes iOS zoom the page on focus. */
.search-box .search-field input {
  font-size: var(--text-body);
}
.search-status {
  min-height: 20px;
  margin: 4px 0 0;
  font-size: var(--text-meta);
  color: var(--muted);
}
.body-slot,.shelf-slot {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.body-slot {
  grid-area: body;
}
.shelf-slot {
  grid-area: shelf;
}
.body-poster,.body-canvas,.shelf-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}
/* The whole body at every size, as the 3D view frames it before a zone is chosen. */
.body-poster {
  object-fit: contain;
}
.body-canvas,.shelf-canvas {
  transition: opacity .4s ease;
}
html.is-loading .body-canvas,html.is-loading .shelf-canvas,.is-lost>canvas {
  opacity: 0;
}
.body-hotspots {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.shelf-loading {
  display: none;
  position: absolute;
  top: 40px;
  left: 0;
  margin: 0;
  font-size: var(--text-meta);
  color: var(--muted);
}
html.is-loading .shelf-loading {
  display: block;
}
.library-empty {
  grid-area: shelf;
  align-self: start;
  z-index: 2;
  margin-top: 40px;
}
.library-empty p {
  margin: 0 0 6px;
  font-size: 20px;
  font-weight: 500;
}
.library-empty .text-button,.library-error .text-button {
  font-size: var(--text-meta);
}

/* Hidden product list: visually hidden in 3D; the focused entry shows as a chip at the top of the shelf column. */
.library-list {
  grid-area: shelf;
  align-self: start;
  position: relative;
  z-index: 3;
  min-width: 0;
}
.library-list:focus {
  outline: none;
}
.library-list ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
html:not(.no-3d):not(.layout-phone) .library-list {
  height: 0;
}
html:not(.no-3d):not(.layout-phone) .library-list :is(h2,a) {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
}
html:not(.no-3d):not(.layout-phone) .library-list :is(h2,a):focus-visible {
  top: 40px;
  left: 8px;
  width: auto;
  max-width: calc(100% - 16px);
  height: auto;
  overflow: visible;
  clip: auto;
  white-space: normal;
  padding: 11px 16px;
  background: var(--paper);
  border: 1px solid var(--control-border);
  box-shadow: 0 10px 30px #232a2414;
  font-size: var(--text-meta);
  font-weight: 600;
  line-height: 1.5;
}

/* No-3D and data error: the track stays in the page at its natural height (the shelf no longer sizes it). */
html:is(.no-3d,.data-error) .shelf-track {
  height: auto;
}

/* No-3D: the poster stays and the hidden list becomes a visible card grid. */
html.no-3d .library-pin {
  position: static;
  height: auto;
  grid-template-rows: none;
}
html.no-3d :is(.shelf-slot,.body-canvas,.body-hotspots) {
  display: none;
}
html.no-3d .body-slot {
  height: var(--strip-h);
}
html:is(.no-3d,.layout-phone) .library-list h2 {
  margin: 28px 0 12px;
  font-size: 21px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -.6px;
}
html:is(.no-3d,.layout-phone) .library-list ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
html:is(.no-3d,.layout-phone) .library-list a {
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: 100%;
  min-height: var(--target);
  padding: 12px;
  border: 1px solid var(--line);
  font-size: var(--text-meta);
  line-height: 1.5;
}
html:is(.no-3d,.layout-phone) .library-list a:hover {
  background: #eef0e9;
}
html:is(.no-3d,.layout-phone) .library-list img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 2 / 3;
  object-fit: contain;
}

/* Data error: only the heading, intro and one message with a catalog link. */
html.data-error .library-pin {
  display: block;
  position: static;
  height: auto;
}
html.data-error .library-pin>:not(.library-lead,.library-error),html.data-error .skip-link {
  display: none;
}
.library-error {
  padding: 0 0 56px;
}
.library-error p {
  margin: 0 0 8px;
  font-size: var(--text-body);
  line-height: 1.6;
}
.library-noscript {
  margin: 24px var(--gutter);
  font-size: var(--text-body);
}
.library-noscript a {
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* Tablet (641-1023 px, or a desktop width under 640 px tall): header and zone nav stay, body and shelf side by side. */
@media (min-width:641px) {
  .site-header {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--paper);
  }
  .library-pin {
    gap: 0 24px;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto auto minmax(0, 1fr) auto;
    grid-template-areas: "lead search" "key pause" "nav nav" "body shelf" "controls shelf";
  }
  .search-box {
    align-self: end;
    padding-bottom: 14px;
  }
  html.no-3d .body-slot {
    position: sticky;
    top: calc(var(--header-h) + 16px);
    align-self: start;
    height: auto;
    aspect-ratio: 2 / 3;
    max-height: calc(100vh - var(--header-h) - 32px);
    max-height: calc(100dvh - var(--header-h) - 32px);
  }
}

/* Desktop (1024 px and wider, at least 640 px tall): three pinned columns under the header row. */
@media (min-width:1024px) and (min-height:640px) {
  .library-pin {
    grid-template-columns: minmax(240px, 24%) minmax(0, 1fr) minmax(320px, 38%);
    grid-template-rows: auto auto auto minmax(0, 1fr) auto;
    grid-template-areas: "nav nav search" "lead body shelf" "key body shelf" "pause body shelf" "pause controls shelf";
  }
  .search-box {
    align-self: center;
    padding: 8px 0 0;
  }
  .library-lead {
    padding-top: 36px;
  }
  .pause-toggle {
    justify-self: start;
    margin-top: 8px;
  }
}
/* Short desktops (1024 x 640): the 16px intro is taller, so trim padding to keep the view controls on screen. */
@media (min-width:1024px) and (min-height:640px) and (max-height:760px) {
  .library-lead {
    padding-top: 20px;
  }
  .body-controls {
    padding: 6px 0;
  }
}

/* Product view: a native modal dialog with its own scroll container; fades in and out where supported. */
:root {
  --low-stock-tint: #f1d5ad; /* apricot sand approved at gate 4, the same literal as STATUS[low-stock].tint in library-shelf.js */
}
html.view-open {
  overflow: hidden;
}
.product-view {
  width: min(1120px, calc(100vw - 32px));
  max-width: none;
  height: min(880px, calc(100vh - 32px));
  height: min(880px, calc(100dvh - 32px));
  max-height: none;
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--control-border);
  border-radius: 4px;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 24px 80px rgb(0 0 0 / 20%);
  opacity: 0;
  transition: opacity .18s ease-out, overlay .18s allow-discrete, display .18s allow-discrete, background-color .18s ease-out,
    border-color .18s ease-out, box-shadow .18s ease-out;
}
.product-view[open] {
  opacity: 1;
}
@starting-style {
  .product-view[open] {
    opacity: 0;
  }
}
.product-view::backdrop {
  background: rgb(23 32 25 / 60%);
}
.pv-scroll {
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 32px 48px;
}
.pv-bar {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 -32px;
  padding: 6px 32px;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
}
.pv-back {
  min-height: var(--target);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-size: var(--text-meta);
  font-weight: 650;
  text-decoration: underline;
  text-underline-offset: 4px;
}
.pv-grid {
  display: grid;
  grid-template-columns: minmax(240px, 5fr) minmax(0, 7fr);
  gap: 24px 40px;
  padding-top: 24px;
}
.pv-card {
  --tint: var(--notice-bg);
  --bar: var(--control-border);
  position: sticky;
  top: 80px;
  align-self: start;
}
.pv-card[data-stock="in-stock"] {
  --tint: var(--accent);
  --bar: var(--stock-dot);
}
.pv-card[data-stock="low-stock"] {
  --tint: var(--low-stock-tint);
  --bar: var(--low-stock);
}
.pv-panel {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--tint);
}
.pv-art {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  mix-blend-mode: multiply;
  opacity: .55;
}
.pv-content {
  position: relative;
  padding-bottom: 22px;
}
.pv-figure {
  position: relative;
  aspect-ratio: 3 / 4;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.pv-vial {
  width: 54%;
  height: auto;
  margin-bottom: 4%;
  filter: drop-shadow(5px 9px 8px #35402b18);
}
.pv-stamp {
  position: absolute;
  top: 14px;
  right: 16px;
  font-size: var(--text-meta);
}
.pv-status {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
  padding: 12px 20px 0;
  border-top: 3px solid var(--bar);
  font-size: var(--text-label);
}
.pv-name {
  margin: 10px 20px 0;
  font-size: clamp(26px, 3vw, 38px);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -.03em;
  overflow-wrap: anywhere;
}
.pv-name:focus {
  outline: none;
}
.pv-sizes {
  margin: 4px 20px 0;
  font-size: var(--text-meta);
  color: var(--muted);
}
.pv-detail {
  min-width: 0;
}
.product-view .pv-table {
  padding: 0;
  border: 0;
}
.pv-detail .product-note {
  margin: 18px 0 0;
}
.pv-detail .pv-research-heading {
  margin: 28px 0 0;
}
.pv-detail .research-summary {
  margin: 6px 0 8px;
}
/* Readability pass, 6 November 2026: the shared catalog components inside the product view, set to the page's type roles. */
.pv-detail :is(.research-summary,.research-detail,.product-note) {
  max-width: 70ch;
  font-size: var(--text-body);
  line-height: 1.6;
}
.pv-detail .product-note {
  font-size: var(--text-small);
}
.product-view .pv-table thead th {
  font-size: var(--text-meta);
}
.product-view .pv-table :is(td,tbody th) {
  font-size: var(--text-small);
}
.pv-detail :is(.pv-research-heading,.research-sources h3) {
  font-size: var(--text-label);
}
.pv-detail :is(.research-sources li,.catalog-footnote) {
  font-size: var(--text-meta);
}
.pv-detail .catalog-footnote {
  flex-direction: column;
  gap: 6px;
  margin-top: 24px;
  padding: 18px 0;
}
.pv-contact h3,.pv-more h3 {
  margin: 24px 0 10px;
  font-size: 19px;
}
.pv-contact p {
  margin: 0;
  max-width: 70ch;
  font-size: var(--text-body);
  line-height: 1.6;
}
.pv-more ul {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}
.pv-more a {
  display: flex;
  align-items: center;
  min-height: var(--target);
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--text-small);
  line-height: 1.5;
}
.pv-more a:hover {
  background: #eef0e9;
}
@media (max-width:640px) {
  .product-view {
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    margin: 0;
    border: 0;
    border-radius: 0;
  }
  .pv-scroll {
    padding: 0 20px 40px;
  }
  .pv-bar {
    margin: 0 -20px;
    padding: 6px 20px;
  }
  .pv-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .pv-card {
    position: relative;
    top: auto;
  }
}
@media (prefers-reduced-motion:reduce) {
  .product-view {
    transition: none;
  }
}

/* Body hotspots (Task 9): library-body.js builds one button per body zone and moves it with CSS transforms only.
   Hidden while loading and while the body context is lost, because the canvas under them is hidden then too. */
html.is-loading .body-hotspots,.body-slot.is-lost .body-hotspots {
  visibility: hidden;
}
.hotspot {
  position: absolute;
  left: calc(var(--target) / -2);
  top: calc(var(--target) / -2);
  width: var(--target);
  height: var(--target);
  padding: 0;
  border: 0;
  background: none;
  pointer-events: auto;
  opacity: var(--face, 1);
  transition: opacity .3s ease;
}
.hotspot.is-dim {
  opacity: calc(var(--face, 1) * .42);
}
.hotspot.is-off {
  visibility: hidden;
  pointer-events: none;
}
.hotspot-dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--stock-dot);
  box-shadow: 0 0 0 2px #fff;
  transition: transform .25s ease;
}
.hotspot:hover .hotspot-dot,.hotspot.is-hover .hotspot-dot,.hotspot.is-active .hotspot-dot {
  transform: scale(1.35);
}
.hotspot-dot::after {
  content: '';
  position: absolute;
  inset: -2px;
  border: 1px solid var(--stock-dot);
  border-radius: 50%;
  opacity: 0;
  animation: hotspot-ring 2.6s ease-out var(--ring-delay, 0s) infinite;
}
@keyframes hotspot-ring {
  0% {
    opacity: .5;
    transform: scale(1);
  }
  70%,100% {
    opacity: 0;
    transform: scale(2.6);
  }
}
.body-hotspots.is-still .hotspot-dot::after {
  animation: none;
}
.hotspot-label {
  position: absolute;
  left: 30px;
  top: 50%;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgb(250 250 248 / 90%);
  color: var(--ink);
  font-size: var(--text-meta);
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity .2s ease;
  pointer-events: none;
}
.hotspot.is-flip .hotspot-label {
  left: auto;
  right: 30px;
}
.hotspot:hover .hotspot-label,.hotspot.is-hover .hotspot-label,.hotspot.is-active .hotspot-label {
  opacity: 1;
}
html:lang(km) :is(.library-lead .intro-note,.pv-detail :is(.research-summary,.research-detail,.product-note),.pv-contact p) {
  line-height: 1.8;
}
html:lang(km) .hotspot-label {
  line-height: 1.5;
}
/* The phone strip is too narrow for a pill beside a centred dot, and the zone chip above it already names the zone. */
html.layout-phone .hotspot-label {
  display: none;
}
/* styles.css already drops every transition under reduced motion; the ring is an animation, so it stops here. */
@media (prefers-reduced-motion:reduce) {
  .hotspot-dot::after {
    animation: none;
  }
}

/* Shelf (Task 9): the only shelf mask. Its fade lengths equal MASK_TOP and MASK_BOTTOM in library-shelf.js. */
#shelf-canvas {
  -webkit-mask-image: linear-gradient(transparent, #000 32px, #000 calc(100% - 56px), transparent);
  mask-image: linear-gradient(transparent, #000 32px, #000 calc(100% - 56px), transparent);
}

/* Leader line (Task 9, desktop only): library.js draws one path from the active hotspot to its shelf heading.
   It passes under the sticky header (z-index 10) and the dialogs. */
.library-leader {
  position: fixed;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.library-leader path {
  fill: none;
  stroke: var(--stock-dot);
  stroke-width: 1;
  stroke-linecap: round;
  opacity: .8;
}

/* Product-view FLIP (Task 10). While .is-flipping, a fixed copy of .pv-panel travels from the shelf card, the dialog's
   own surface and backdrop stay clear, and its content waits; then both fade in over the copy, which is removed. */
.product-view.is-flipping {
  background-color: transparent;
  border-color: transparent;
  box-shadow: none;
}
@starting-style {
  .product-view.is-flipping[open] {
    opacity: 1;
  }
}
.product-view::backdrop {
  transition: opacity .18s ease-out;
}
.product-view.is-flipping::backdrop {
  opacity: 0;
}
.pv-scroll {
  position: relative;
  z-index: 1;
  transition: opacity .18s ease-out;
}
.product-view.is-flipping .pv-scroll {
  opacity: 0;
}
.product-view .pv-panel.pv-ghost {
  position: fixed;
  inset: auto;
  left: 0;
  top: 0;
  z-index: 0;
  margin: 0;
  transform-origin: 0 0;
  pointer-events: none;
}

/* Phone: the body leads; native cards below use normal document scrolling. */
@media (max-width:640px) {
  html.layout-phone .shelf-track { height: auto; }
  html.layout-phone .library-pin {
    position: static;
    height: auto;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-template-areas: "lead" "nav" "body" "controls" "pause" "products" "search" "key" "shelf" "empty";
    padding-bottom: max(24px, env(safe-area-inset-bottom));
  }
  .library-lead { padding: 20px 0 12px; }
  .library-lead h1 { font-size: 32px; letter-spacing: -1px; }
  .library-lead .intro-note { margin-top: 8px; }
  .zone-nav { gap: 8px; }
  html.layout-phone .body-slot {
    height: clamp(280px, 48vh, 420px);
    height: clamp(280px, 48svh, 420px);
  }
  .shelf-slot { display: none; }
  .view-products { display: block; grid-area: products; background: var(--ink); color: var(--paper); margin-bottom: 16px; }
  .search-box { padding-top: 8px; }
  .stock-key { padding: 12px 0; gap: 8px 16px; }
  .library-list { height: auto; }
  .library-list .list-zone { scroll-margin-top: 16px; }
  html.layout-phone .library-list ul { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  html.layout-phone .library-list a { overflow-wrap: anywhere; }
  html.layout-phone .library-list img { width: 100%; height: 140px; object-fit: contain; }
  .library-empty { grid-area: empty; margin-top: 16px; }
  .pause-toggle { justify-self: center; margin: 0 0 12px; }
}
