:root {
  --bg: #fffaf4;
  --panel: rgba(255, 255, 255, 0.92);
  --panel-soft: #fff4e5;
  --text: #183153;
  --muted: #5f6f84;
  --line: #e6dece;
  --shadow-soft: 0 14px 34px rgba(24, 49, 83, 0.07);
  --radius-xl: 34px;
  --radius-lg: 24px;
  --radius-md: 18px;
  --site-width: 1160px;
  --content-width: 760px;
  --topbar-height: 7rem;
  --cloud-sun: #f8d86b;
  --cloud-pink: #f3b4d5;
  --cloud-mint: #9ce1d5;
  --cloud-sky: #b6d6ff;
  /* Border that goes with a --cloud-sky fill. Every use of the pair sets both
     together (tertiary buttons, active facet toggles, party-hire chips), so the
     edge colour needs a name as much as the fill does. */
  --cloud-sky-line: #96c4ff;
  --cloud-peach: #ffcfa8;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

/* playfair-display-700 - latin */
@font-face {
  font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 700;
  src: url('/fonts/playfair-display-v40-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}

body {
  margin: 0;
  font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif;
  color: var(--text);
  background:
    radial-gradient(circle at top left, rgba(248, 216, 107, 0.2), transparent 28rem),
    linear-gradient(180deg, #fffaf4 0%, #fffdf9 55%, #fff8f1 100%);
  line-height: 1.65;
}

a {
  color: #1b4d9b;
}

img {
  display: block;
  max-width: 100%;
}

input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--cloud-pink);
}

/* Base fallback so any un-classed fieldset (e.g. register's children list)
   still picks up the site's rounded-card look instead of the browser's
   default double-inset chrome. Component-specific fieldsets like
   .catalogue-facet override these with their own values. */
fieldset {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0.75rem 0.85rem;
}

legend {
  padding: 0 0.3rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
}

.site-shell {
  min-height: 100vh;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(255, 250, 244, 0.94);
  border-bottom: 1px solid rgba(24, 49, 83, 0.08);
  backdrop-filter: blur(10px);
}

.topbar-inner,
.content-inner,
.footer-inner {
  width: min(calc(100% - 2rem), var(--site-width));
  margin: 0 auto;
}

.topbar-inner {
  padding: 1rem 0;
}

.brand {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  height: 5rem;
  padding: 0.55rem 0.85rem;
  overflow: hidden;
  background: #fff;
  border-radius: 2.5rem 2.2rem 2.8rem 2.1rem / 2rem 2.4rem 1.9rem 2.3rem;
  box-shadow: var(--shadow-soft);
  text-decoration: none;
  transition: transform 160ms ease;
}

.brand-logo {
  width: auto;
  max-width: none;
  height: 100%;
  object-fit: contain;
}

.brand:hover,
.brand:focus-visible {
  transform: translateY(-1px);
}

.nav,
.footer-links,
.home-actions {
  display: flex;
  flex-wrap: wrap;
}

.nav {
  gap: clamp(0.45rem, 1.1vw, 0.9rem);
  flex-wrap: nowrap;
  width: 100%;
}

.nav-links {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: inherit;
  min-width: 0;
}

.nav-link,
.button,
th {
  font-family: "Avenir Next", "Arial Rounded MT Bold", "Trebuchet MS", sans-serif;
}

h1,
h2,
h3,
h4,
h5,
h6,
.toc-label {
  font-family: "Playfair Display", "Times New Roman", serif;
}

.nav-link {
  flex: 1 1 0;
  display: inline-flex;
  white-space: normal;
  align-items: center;
  justify-content: center;
  min-width: 0;
  height: 5rem;
  padding: 0.95rem clamp(0.55rem, 1.4vw, 1.15rem);
  border-radius: 2.5rem 2.2rem 2.8rem 2.1rem / 2rem 2.4rem 1.9rem 2.3rem;
  text-decoration: none;
  color: var(--text);
  font-size: clamp(0.88rem, 1.45vw, 1.08rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: 0;
  text-align: center;
  opacity: 0.55;
  transform: translateY(0);
  transition: opacity 160ms ease, transform 160ms ease, filter 160ms ease;
}

.nav-link:hover,
.nav-link:focus-visible,
.nav-link.active {
  opacity: 1;
  filter: saturate(1.05);
  transform: translateY(-1px);
  outline: none;
}

.nav-link.active {
  box-shadow: var(--shadow-soft);
}

.nav-link-home {
  display: none;
}

.nav-auth-guest,
.nav-auth-member,
.nav-auth-admin {
  display: none;
}

.nav-auth-guest.is-hidden,
.nav-auth-member.is-hidden,
.nav-auth-admin.is-hidden {
  display: none;
}

.nav-auth-guest:not(.is-hidden),
.nav-auth-member:not(.is-hidden),
.nav-auth-admin:not(.is-hidden) {
  display: inline-flex;
}

.nav-sun {
  background: var(--cloud-sun);
}

.nav-pink {
  background: var(--cloud-pink);
}

.nav-mint {
  background: var(--cloud-mint);
}

.nav-sky {
  background: var(--cloud-sky);
}

.nav-peach {
  background: var(--cloud-peach);
}

.subbar {
  position: sticky;
  top: var(--topbar-height);
  z-index: 19;
  background: var(--panel-soft);
  border-bottom: 1px solid rgba(24, 49, 83, 0.08);
}

.subbar-inner {
  width: min(calc(100% - 2rem), var(--site-width));
  margin: 0 auto;
  padding: 0.65rem 0;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(0.4rem, 1vw, 0.75rem);
}

.subbar .nav-link {
  height: 3rem;
  flex: 0 1 auto;
}

.nav-toggle {
  display: none;
  flex: 0 0 auto;
  width: 3rem;
  height: 3rem;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-soft);
  cursor: pointer;
}

.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 1.4rem;
  height: 2px;
  background: var(--text);
  transition: transform 160ms ease, opacity 160ms ease;
}

.nav-toggle-bars {
  position: relative;
}

.nav-toggle-bars::before,
.nav-toggle-bars::after {
  content: "";
  position: absolute;
  left: 0;
}

.nav-toggle-bars::before {
  top: -0.45rem;
}

.nav-toggle-bars::after {
  top: 0.45rem;
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bars {
  background: transparent;
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before {
  top: 0;
  transform: rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after {
  top: 0;
  transform: rotate(-45deg);
}

body.nav-locked {
  overflow: hidden;
}

.content {
  padding: 2rem 0 3.5rem;
}

.home-stack {
  display: grid;
  gap: 1.5rem;
}

.home-hero-card,
.article-card,
.toc-panel,
.shot-card,
.table-wrap {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
}

.home-hero-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2rem;
  align-items: center;
  padding: clamp(1.5rem, 4vw, 2.8rem);
}

.home-hero-copy {
  text-align: center;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 0.75rem;
  line-height: 1.05;
  letter-spacing: -0.03em;
}

h1 {
  font-size: clamp(2.5rem, 5vw, 4.5rem);
}

h2 {
  font-size: clamp(1.8rem, 3vw, 2.5rem);
}

h3 {
  font-size: 1.35rem;
}

h4,
h5,
h6 {
  font-size: 1.35rem;
}

.lede {
  max-width: 28rem;
  margin: 1rem auto 0;
  font-size: 1.12rem;
  color: var(--muted);
}

.home-actions {
  gap: 0.85rem;
  justify-content: center;
  margin-top: 1.5rem;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 3.25rem;
  padding: 0.85rem 1.25rem;
  border: 2px solid transparent;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 400;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.button-primary {
  background: var(--cloud-pink);
  color: var(--text);
  border-color: #e39ac2;
}

.button-secondary {
  background: var(--panel-soft);
  color: var(--text);
  border-color: #f2dcc0;
}

.button-tertiary {
  background: var(--cloud-sky);
  color: var(--text);
  border-color: var(--cloud-sky-line);
}

.button-mint {
  background: var(--cloud-mint);
  color: var(--text);
  border-color: #6cc9b8;
}

.owner-portrait-wrap {
  display: grid;
  place-items: center;
}

.owner-portrait {
  width: clamp(11rem, 24vw, 15rem);
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
  border: 10px solid #fff;
  box-shadow: var(--shadow-soft);
}

.photo-strip {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 1rem;
  align-items: center;
}

.photo-strip .shot-card {
  grid-column: span 2;
}

.photo-strip-count-1 .shot-card {
  grid-column: 3 / span 2;
}

.photo-strip-count-2 .shot-card:first-child,
.photo-strip-count-4 .shot-card:nth-child(odd),
.photo-strip-count-5 .shot-card:nth-child(4) {
  grid-column: 2 / span 2;
}

.photo-strip-count-2 .shot-card:last-child,
.photo-strip-count-4 .shot-card:nth-child(even),
.photo-strip-count-5 .shot-card:nth-child(5) {
  grid-column: 4 / span 2;
}

.shot-card {
  overflow: hidden;
  margin: 0;
}

.shot-card img {
  width: 100%;
  height: auto;
}

.kit-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1rem;
}

.kit-card {
  display: grid;
  gap: 0.85rem;
  color: var(--text);
  text-decoration: none;
}

.kit-card-media,
.toy-product-photo {
  position: relative;
  overflow: hidden;
  margin: 0;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
}

.kit-card-media {
  aspect-ratio: 4 / 3;
}

.kit-card-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 180ms ease, transform 180ms ease;
}

.kit-card-photo-detail {
  opacity: 0;
}

.kit-card:hover .kit-card-photo-hero,
.kit-card:focus-visible .kit-card-photo-hero {
  opacity: 0;
}

.kit-card:hover .kit-card-photo-detail,
.kit-card:focus-visible .kit-card-photo-detail {
  opacity: 1;
  transform: scale(1.02);
}

.kit-card:focus-visible {
  border-radius: var(--radius-lg);
  outline: 3px solid #1b4d9b;
  outline-offset: 0.35rem;
}

.kit-card-title {
  font-weight: 700;
  text-align: center;
}

.toy-page {
  display: grid;
  gap: 1.5rem;
}

.toy-breadcrumb {
  justify-self: start;
  color: var(--text);
  font-weight: 500;
  text-decoration-color: rgba(24, 49, 83, 0.35);
  text-underline-offset: 0.22rem;
}

.toy-breadcrumb:hover,
.toy-breadcrumb:focus-visible {
  color: #1b4d9b;
  outline: none;
}

.toy-product {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.9fr);
  gap: clamp(1.2rem, 3vw, 2rem);
  align-items: start;
  padding: clamp(1.5rem, 2.8vw, 2.6rem);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
}

.toy-product-copy {
  display: grid;
  gap: 1rem;
}

.toy-product-copy p {
  max-width: var(--content-width);
  margin: 0;
  font-size: 1.04rem;
}

.toy-product-copy h1,
.toy-product-copy h2 {
  margin-bottom: 0;
}

.toy-product-copy h2 {
  font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif;
  font-size: 1.35rem;
  font-style: italic;
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: 0;
}

.toy-product-copy ul {
  display: grid;
  gap: 0rem;
  max-width: var(--content-width);
  margin: 0 0 0.5rem 0;
  padding: 0;
  list-style: none;
}

.toy-product-copy li {
  position: relative;
  padding-left: 1.75rem;
  font-size: 1.04rem;
}

.toy-product-copy li::before {
  content: "✔";
  position: absolute;
  left: 0;
  font-weight: 800;
}

.toy-product-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem;
}

.toy-product-media {
  display: grid;
  gap: 1rem;
}

.toy-product-photo img {
  width: 100%;
  height: auto;
}

.article-card {
  padding: clamp(1.5rem, 2.8vw, 2.6rem);
}

.article-card-home {
  max-width: 860px;
  margin: 0 auto;
  text-align: center;
}

.article-card-centered {
  max-width: 980px;
  margin: 0 auto;
}

.article-card-offset {
  margin-left: auto;
}

.article-card p,
.article-card li {
  font-size: 1.04rem;
}

.article-card p,
.article-card ul,
.article-card ol {
  max-width: var(--content-width);
}

.article-card .markdown-image {
  float: right;
  width: min(38%, 16rem);
  height: auto;
  margin: 0 0 1rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
}

.article-card .has-markdown-image :is(p, ul, ol) {
  max-width: none;
}

.page-header::after,
.section-block::after {
  content: "";
  display: block;
  clear: both;
}

.article-card-home p,
.article-card-home ul,
.article-card-home ol {
  margin-left: auto;
  margin-right: auto;
}

.page-header {
  margin-bottom: 1.8rem;
}

.page-header h1 {
  margin-bottom: 1rem;
}

.page-header p {
  margin: 0 0 1rem;
}

.page-header p:last-child {
  margin-bottom: 0;
}

.section-block,
h3 {
  scroll-margin-top: 8rem;
}

.section-block:not(:first-child) {
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  margin-top: 1.5rem;
}

.section-block h2 {
  margin-bottom: 0.9rem;
}

.article-card h3,
.article-card h4,
.article-card h5,
.article-card h6 {
  margin-top: 1.4rem;
}

.section-subtitle {
  margin-top: -0.4rem;
  margin-bottom: 0.7rem;
  color: var(--muted);
}

.eyebrow,
.toc-label {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #1b4d9b;
}

.section-heading {
  display: grid;
  gap: 0.3rem;
  margin-bottom: 1rem;
}

.table-wrap {
  margin: 1.2rem 0 1.5rem;
  overflow-x: auto;
}

.content-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.content-table th,
.content-table td {
  padding: 1rem 1.05rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
  overflow-wrap: anywhere;
}

.content-table thead th {
  background: #fff8ef;
}

.content-table tbody tr:last-child th,
.content-table tbody tr:last-child td {
  border-bottom: 0;
}

.content-table th.align-center,
.content-table td.align-center {
  text-align: center;
}

.content-table th.align-right,
.content-table td.align-right {
  text-align: right;
}

.content-table.cols-3 {
  min-width: 32rem;
}

.content-table.cols-4 {
  min-width: 40rem;
}

.content-table.cols-5 {
  min-width: 48rem;
}

.content-table.cols-6 {
  min-width: 56rem;
}

.memberships-table th:first-child,
.memberships-table td:first-child {
  width: 22%;
  background: #fff8ef;
}

.memberships-table :is(th, td):nth-child(2) {
  background: #fff5cc;
}

.memberships-table :is(th, td):nth-child(3) {
  background: #ffe4f0;
}

.memberships-table :is(th, td):nth-child(4) {
  background: #e4faf4;
}

.memberships-table :is(th, td):nth-child(5) {
  background: #e6f1ff;
}

.memberships-table :is(th, td):nth-child(6) {
  background: #fff1e2;
}

.memberships-table thead th:not(:first-child) {
  font-size: 1.2rem;
}

.toc-layout {
  display: grid;
  grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}

.toc-panel {
  position: sticky;
  top: 6.5rem;
  padding: 1.2rem 1.1rem;
}

.toc-list {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.toc-list a {
  display: block;
  padding: 0.55rem 0.75rem;
  border-radius: 14px;
  text-decoration: none;
  color: var(--text);
  background: #fff8ef;
}

.toc-list a:hover,
.toc-list a:focus-visible {
  background: #ffecc7;
  outline: none;
}

.footer {
  padding: 0 0 2.5rem;
}

.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.3rem 1.5rem;
  border-radius: var(--radius-lg);
  background: #fff4e9;
  border: 1px solid var(--line);
}

.footer-inner p {
  margin: 0;
}

.footer-links {
  gap: 1rem;
}

@media (max-width: 980px) {
  .home-hero-card,
  .toc-layout,
  .toy-product,
  .footer-inner {
    grid-template-columns: 1fr;
  }

  .home-hero-copy {
    order: 2;
  }

  .owner-portrait-wrap {
    order: 1;
  }

  .toc-panel {
    position: static;
  }

  .footer-inner {
    display: grid;
  }
}

@media (max-width: 780px) {
  .topbar,
  .subbar {
    position: static;
  }

  .topbar-inner {
    grid-template-columns: 1fr;
  }

  .brand {
    justify-content: center;
  }

  .content {
    padding-top: 1.4rem;
  }

}

@media (max-width: 640px) {
  .photo-strip.photo-strip .shot-card {
    grid-column: 1 / -1;
  }

  .section-block,
  h3 {
    scroll-margin-top: 1rem;
  }

  .nav-toggle {
    display: inline-flex;
    position: relative;
    z-index: 31;
  }

  .nav-links {
    display: none;
  }

  .nav-links.is-open {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    position: fixed;
    inset: 0;
    z-index: 30;
    background: var(--bg);
    padding: 2rem;
  }

  .topbar:has(.nav-links.is-open) {
    backdrop-filter: none;
  }

  .nav-links.is-open .nav-link {
    flex: none;
    width: 100%;
    max-width: 20rem;
    height: auto;
    padding: 1.1rem;
    font-size: 1.15rem;
  }

  .nav-links.is-open .nav-link-home {
    display: inline-flex;
  }

  .subbar-inner {
    justify-content: center;
  }

  .article-card,
  .home-hero-card {
    padding: 1.3rem;
  }

  .article-card .markdown-image {
    float: none;
    width: 100%;
    max-width: 22rem;
    margin: 0 auto 1rem;
  }

  .section-block.has-markdown-image {
    display: flex;
    flex-direction: column;
  }

  .section-block.has-markdown-image h2 {
    order: 1;
  }

  .section-block.has-markdown-image .markdown-image {
    order: 2;
  }

  .section-block.has-markdown-image > :not(h2, .markdown-image) {
    order: 3;
  }

  .content-table.cols-3 {
    min-width: 30rem;
  }

  .content-table.cols-4 {
    min-width: 36rem;
  }

  .content-table.cols-5 {
    min-width: 42rem;
  }

  .content-table.cols-6 {
    min-width: 48rem;
  }
}

/* Catalogue — see public/js/catalogue/*.js */

button.button {
  cursor: pointer;
  font: inherit;
}

/* Disabled buttons had no visual treatment, so a functionally-disabled
   control (e.g. Submit order before a pickup slot is chosen) looked fully
   active. Dim it and swap the cursor so the disabled state reads clearly. */
.button:disabled,
.button[disabled],
.button[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.catalogue-summary {
  color: var(--muted);
  margin-bottom: 1.5rem;
}

.catalogue-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1.5rem;
}

.featured-carousel {
  margin-bottom: 2rem;
}

.featured-carousel-heading {
  margin: 0 0 0.75rem;
}

.featured-carousel-viewport {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.featured-carousel-track {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  flex: 1;
}

/* No scroll-snap / scroll-behavior here on purpose: the carousel paging is
   driven entirely by animateScrollTo() in browse.js, which writes scrollLeft
   per-frame. scroll-snap-type (mandatory OR proximity) makes Chromium re-snap
   each of those writes to the nearest snap point, collapsing the 400ms ease
   into a few instant jumps; scroll-behavior: smooth adds its own competing
   native scroll. Both silently break the hand-rolled animation. */

.featured-carousel-item {
  flex: 0 0 calc((100% - 2rem) / 3);
}

.featured-carousel-arrow {
  flex: 0 0 auto;
  border: 1px solid var(--line);
  border-radius: 50%;
  width: 2.4rem;
  height: 2.4rem;
  background: var(--panel);
  font-size: 1.2rem;
  cursor: pointer;
}

.featured-carousel-pause {
  display: block;
  margin: 0.5rem auto 0;
  border: none;
  background: none;
  color: var(--muted);
  font-size: 0.9rem;
  cursor: pointer;
}

@media (max-width: 640px) {
  .featured-carousel-item {
    flex-basis: 100%;
  }
}

.toy-card {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  color: var(--text);
  box-shadow: var(--shadow-soft);
}

.toy-card-link {
  display: flex;
  flex-direction: column;
  flex: 1;
  text-decoration: none;
  color: inherit;
}

.toy-card-actions {
  padding: 0 1rem 1rem;
  text-align: center;
}

.toy-card-in-order {
  color: var(--muted);
  font-size: 0.9rem;
}

.toy-card img,
.toy-card-placeholder {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--panel-soft);
}

.toy-card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 1rem;
}

.toy-card-meta {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0.25rem 0 0.75rem;
}

.toy-card-body h3 {
  margin: 0 0 0.25rem;
  line-height: 1.3;
  min-height: 2.6em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Pins the status pill(s) to the bottom of the card body regardless of how
   many lines the name/category meta wrap to, so the action button below
   (a sibling of .toy-card-link, not .toy-card-body) lines up consistently
   across cards in the same grid row. A wrapper is needed (rather than
   margin-top: auto directly on .status-pill) because .toy-card-body's
   flex column would otherwise stretch each pill span to the card's full
   width — align-self keeps the wrapper (and the pills' natural row layout
   inside it) sized to its content; center then centres it on the card. */
.toy-card-pills {
  display: flex;
  align-items: center;
  align-self: center;
  margin-top: auto;
}

.checkout-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.checkout-item-thumb,
.checkout-item-thumb-placeholder {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--radius-md);
  object-fit: cover;
  background: var(--panel-soft);
  flex-shrink: 0;
}

.checkout-item-name {
  flex: 1 1 auto;
}

/* A return/renewal line stacks its row above the misalignment note, which
   would otherwise be laid out as another column of .checkout-item's flex row. */
.checkout-item-stacked {
  flex-direction: column;
  align-items: stretch;
  gap: 0.35rem;
}

.checkout-item-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.checkout-item-due {
  display: block;
  color: var(--muted);
  font-size: 0.9rem;
}

/* One section per kind of line in the visit: taking home, bringing back,
   renewing. Empty ones aren't rendered at all, so this only spaces real ones. */
.checkout-section + .checkout-section {
  margin-top: 1.5rem;
}

.checkout-section h3 {
  margin: 0 0 0.5rem;
}

.toy-product-photo.is-placeholder {
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  padding: 1rem;
  color: var(--muted);
  font-size: 0.95rem;
  text-align: center;
  background: var(--panel-soft);
}

.warning-note {
  color: #8a4b12;
  background: var(--cloud-peach);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.85rem;
  font-size: 0.9rem;
}

/* Generic hide utility (lower specificity than nav-specific .nav-auth-*.is-hidden rules; used by confirmInline and other non-nav uses) */
.is-hidden {
  display: none;
}

.confirm-inline {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-left: 0.5rem;
  max-width: 100%;
}

.confirm-inline-message {
  flex: 1 1 auto;
  min-width: 12rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.confirm-inline-reason {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0.35rem 0.5rem;
  font-size: 0.9rem;
}

.account-list {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 1rem;
}

.account-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 1rem;
}

.account-list-info {
  flex: 1 1 auto;
  min-width: 0;
}

/* Keeps the action button pinned to the right edge even when it wraps
   onto its own line below a long/multi-line .account-list-info. */
.account-list li > button {
  margin-left: auto;
}

/* My Loans is one section per order rather than one flat list, so each group
   needs a header of its own and its rows need to read as belonging to it. */
.loan-group + .loan-group {
  margin-top: 1.5rem;
}

.loan-group-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.loan-group-header h3 {
  margin: 0;
}

.loan-group-actions,
/* A loan row carries up to two actions, so they sit in their own box —
   .account-list li > button's margin-left would otherwise split the pair
   across the row's free space instead of keeping them together. */
.loan-row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
}

.loan-row-note {
  font-size: 0.9rem;
  color: var(--muted);
}

#login-form,
#add-toy-form {
  display: grid;
  gap: 0.75rem;
  max-width: 24rem;
  margin-bottom: 1.5rem;
}

#login-form input,
#add-toy-form input,
.admin-table select,
.reorder-input {
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
}

.reorder-input {
  width: 4rem;
}

.admin-section {
  margin-bottom: 2.5rem;
}

.admin-section h2 {
  margin-bottom: 1rem;
}

.admin-enquiry-list {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: 1rem;
}

.admin-enquiry {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 1rem;
}

.admin-enquiry-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.admin-enquiry-details {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 0.75rem;
  margin: 0 0 0.75rem;
}

.admin-enquiry-details dt {
  color: var(--muted);
}

.admin-enquiry-details dd {
  margin: 0;
}

.admin-enquiry-toys {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Reuses .warning-note's colours (see comment above .party-toy-error) rather
   than inventing new ones — same "needs the admin's attention" meaning. */
.admin-enquiry-conflict {
  display: inline-block;
  margin-left: 0.5rem;
  color: #8a4b12;
  background: var(--cloud-peach);
  border-radius: var(--radius-md);
  padding: 0.1rem 0.5rem;
  font-size: 0.85rem;
}

.admin-enquiry-message {
  margin: 0 0 0.75rem;
  color: var(--muted);
  font-style: italic;
}

.admin-enquiry-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.hire-form-toys {
  list-style: none;
  margin: 0.5rem 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.hire-form-toys li {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--panel-soft);
  border-radius: var(--radius-md);
  padding: 0.25rem 0.6rem;
}

.toys-panel-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

.print-select-cell {
  width: 2.25rem;
  text-align: center;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1rem;
}

.admin-table th,
.admin-table td {
  text-align: left;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--line);
}

.admin-table th.align-right,
.admin-table td.align-right {
  text-align: right;
}

.admin-table select[multiple] {
  min-width: 9rem;
  vertical-align: top;
}

.admin-table td input[type="text"] {
  display: block;
  margin-top: 0.35rem;
  width: 100%;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 0.85rem;
}

.form-grid select[multiple] {
  min-height: 6rem;
}

.order-items-list {
  margin: 0;
  padding-left: 1.1rem;
}

/* A declared return arrives pre-ticked, so it has to be tellable apart from
   the member's other open loans at a glance — unticking one of these is the
   librarian saying "they didn't bring it", and unticking any other row means
   nothing at all. */
.return-declared-flag {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--panel-soft);
  border: 1px solid var(--line);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.toy-components {
  padding: clamp(1.5rem, 2.8vw, 2.6rem);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
}

.toy-components h2 {
  margin-bottom: 0.9rem;
}

.toy-components-list {
  display: grid;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.toy-components-list li {
  position: relative;
  padding-left: 1.75rem;
}

.toy-components-list li::before {
  content: "✔";
  position: absolute;
  left: 0;
  font-weight: 800;
}

/* Admin toy-pieces and member-children lists — mirrors .account-list's row
   treatment so the same "list of records with a trailing action" pattern
   looks the same in Admin as it does on My Account. */
.toy-components-admin-list {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.toy-components-admin-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  background: var(--panel-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.75rem;
}

.toy-components-admin-list li > button {
  margin-left: auto;
}

/* Overrides .admin-table td input[type="text"]'s stacked/full-width layout
   (meant for the type/age tag inputs) so name + quantity sit inline like
   the rest of this row instead of one being unstyled and the other
   dropping to its own line. data-piece-* (not data-component-*): the pieces
   editor moved into the toy edit modal (Task 11) and its inputs carry the
   new attribute names — a selector still targeting the old ones would match
   nothing and silently drop this sizing. */
.toy-components-admin-list li input[data-piece-name],
.toy-components-admin-list li input[data-piece-quantity] {
  display: inline-block;
  width: auto;
  margin-top: 0;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 0.85rem;
}

.toy-components-admin-list li input[data-piece-name] {
  flex: 1 1 12rem;
}

.toy-components-admin-list li input[data-piece-quantity] {
  flex: 0 0 auto;
  width: 4rem;
}

.toy-components-missing-note {
  color: var(--muted);
  font-size: 0.85rem;
}

/* The add-piece row below the list: same inline name+quantity+button layout
   as each existing piece row above, so adding one doesn't look like a
   different control. */
.toy-piece-add {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.toy-piece-add input[type="text"] {
  flex: 1 1 12rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 0.85rem;
}

.toy-piece-add input[type="number"] {
  flex: 0 0 auto;
  width: 4rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 0.85rem;
}

/* Toy status in the admin table: a readout rather than a control, since
   status is derived from loans, orders, hires and holds. Only the withdraw /
   return-to-circulation pair beside it actually writes it. */
.toy-status-readout {
  display: inline-block;
  margin-right: 0.4rem;
  white-space: nowrap;
}

.toy-status-withdrawn {
  color: var(--muted);
  font-style: italic;
}

/* A party hire wanting a toy that's reserved for a member's hold. Needs a
   human to ring the holder, so it's called out rather than folded into the
   status pill beside it. */
.hire-hold-conflict {
  margin-top: 0.35rem;
  font-size: 0.85rem;
  line-height: 1.3;
  color: var(--warning-text, #8a5300);
}

/* Shared form styling — used by login, register, add-toy, hire-request, and
   add-opening-hours forms instead of one-off ID-scoped rules. */
.form-grid {
  display: grid;
  gap: 0.75rem;
  max-width: 20rem;
  margin-bottom: 1.5rem;
}

.form-grid label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
}

.form-grid input,
.form-grid select {
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
}

/* Checkout — pickup time slots. Stacked, left-aligned radio options rather
   than the inline flow they'd otherwise fall into, and undoing the boxed
   .form-grid input treatment on the radios themselves. */
#pickup-slots {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.pickup-slot-option {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: inherit;
  font-weight: 400;
  color: var(--text);
}

/* Each part of the time range gets its own column so the hours, dashes and
   am/pms line up down the list, while still reading as ordinary text. The
   flex row collapses the markup's whitespace, so "5:30" and "pm" sit flush
   together; only the dash carries any spacing of its own. */
.pickup-slot-range {
  display: flex;
  align-items: baseline;
  font-variant-numeric: tabular-nums;
}

/* Right-aligned so single- and double-digit hours share a colon position
   without padding the hour out to a leading zero. */
.pickup-slot-time {
  min-width: 4.5ch;
  text-align: right;
}

/* Wide enough to hold either period outright, so the differing widths of
   "am" and "pm" can never nudge the dash out of column. */
.pickup-slot-period {
  min-width: 2.6ch;
  text-align: left;
}

.pickup-slot-dash {
  margin: 0 0.3rem;
}

.pickup-slot-option input[type="radio"] {
  flex: 0 0 auto;
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
}

.pickup-slots-empty {
  color: var(--muted);
}

/* Checkout — inline pickup date calendar. Only days that have slots are
   pickable; they carry a rounded-square outline, the selected day fills pink,
   and every other day is a plain, non-interactive number. */
.pickup-calendar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.6rem;
}

.pickup-calendar-title {
  font-weight: 600;
  font-size: 0.98rem;
}

.pickup-calendar-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.pickup-calendar-nav:hover:not(:disabled) {
  background: var(--panel-soft);
}

.pickup-calendar-nav:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.pickup-calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.pickup-calendar-dow {
  text-align: center;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  padding-bottom: 0.35rem;
}

.pickup-day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  padding: 0;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-family: inherit;
}

.pickup-day-empty {
  visibility: hidden;
}

.pickup-day-bookable {
  color: var(--text);
  border-color: var(--line);
  font-weight: 600;
  cursor: pointer;
}

.pickup-day-bookable:hover {
  background: var(--panel-soft);
}

.pickup-day-selected,
.pickup-day-selected:hover {
  background: var(--cloud-pink);
  border-color: #e39ac2;
  color: var(--text);
}

.pickup-day-bookable:focus-visible,
.pickup-calendar-nav:focus-visible {
  outline: 3px solid var(--cloud-sky);
  outline-offset: 2px;
}

/* Status pills — generalized from the old .toy-status rules so the same
   badge styling covers toy, order, hold, and hire statuses. */
.status-pill {
  display: inline-block;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  font-size: 0.8rem;
}

.status-pill-available,
.status-pill-ready,
.status-pill-approved,
.status-pill-confirmed,
.status-pill-active,
.status-pill-collected {
  background: var(--cloud-mint);
}

.status-pill-on_loan,
.status-pill-pending {
  background: var(--cloud-peach);
}

.status-pill-unavailable,
.status-pill-rejected,
.status-pill-cancelled {
  background: var(--line);
}

.status-pill-waiting,
.status-pill-returned {
  background: var(--cloud-sky);
}

.status-pill-grace {
  background: var(--cloud-peach);
}

.status-pill-missing {
  background: var(--cloud-peach);
}

.status-pill-large {
  background: var(--cloud-sky);
  margin-left: 0.4rem;
}

.status-pill-new {
  background: var(--cloud-peach);
}

.status-pill-contacted {
  background: var(--cloud-sky);
}

.status-pill-converted {
  background: var(--cloud-mint);
}

.status-pill-closed {
  background: var(--line);
}

.catalogue-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  margin-bottom: 1.5rem;
}

.catalogue-toolbar #catalogue-search {
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
  min-width: 14rem;
  flex: 1 1 auto;
}

.catalogue-toggle {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.9rem;
}

.catalogue-facet-dropdown {
  position: relative;
}

.catalogue-facet-toggle {
  font: inherit;
  font-size: 0.9rem;
  width: 7.5rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
}

.catalogue-facet-toggle-active {
  background: var(--cloud-sky);
  border-color: var(--cloud-sky-line);
  font-weight: 600;
}

.catalogue-facet-popover {
  position: absolute;
  top: calc(100% + 0.3rem);
  left: 0;
  z-index: 20;
  min-width: 11rem;
  max-height: 22rem;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.85rem;
  box-shadow: var(--shadow-soft);
}

.catalogue-facet-popover label {
  display: block;
  font-size: 0.85rem;
  padding: 0.15rem 0;
}

.catalogue-toolbar #catalogue-clear-filters {
  font: inherit;
  font-size: 0.9rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
}

.catalogue-toolbar #catalogue-clear-filters:hover {
  background: var(--panel-soft);
}

@media (max-width: 640px) {
  .catalogue-toolbar #catalogue-search {
    flex: 1 1 100%;
  }
}

/* Checkout page — two-column layout mirroring .toy-product */
.checkout-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.9fr);
  gap: clamp(1.2rem, 3vw, 2rem);
  align-items: start;
}

.checkout-summary,
.checkout-booking {
  padding: clamp(1.5rem, 2.8vw, 2.6rem);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
}

@media (max-width: 980px) {
  .checkout-layout {
    grid-template-columns: 1fr;
  }
}

.plan-guidance {
  border-left: 3px solid var(--line);
  padding-left: 0.75rem;
  font-size: 0.9rem;
}
.plan-guidance-exceeded {
  border-left-color: var(--color-warning, #b45309);
}

/* Admin panel tabs */
.admin-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.admin-tab {
  padding: 0.6rem 1.1rem;
  border: 2px solid var(--line);
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  font-size: 0.92rem;
  cursor: pointer;
  opacity: 0.65;
}

.admin-tab[aria-selected="true"] {
  background: var(--cloud-pink);
  border-color: #e39ac2;
  opacity: 1;
}

.admin-tab-panel[hidden] {
  display: none;
}

/* My account header row */
.account-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.account-header p {
  margin: 0;
}

/* Spinner: in-flight request indicator (buttons, selects/inputs, confirmInline) */
.spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-right: 0.4em;
  vertical-align: -0.15em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

/* Positioned absolutely inside .with-pending-wrap so a control gaining a
   spinner never grows its own row/cell — see withPending() in
   ui-feedback.js. */
.with-pending-wrap {
  position: relative;
  display: inline-block;
  vertical-align: middle;
}

.spinner-after {
  position: absolute;
  left: 100%;
  top: 50%;
  margin: 0 0 0 0.4em;
  transform: translateY(-50%);
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Shimmer: skeleton loading placeholders shown before a page's first real render */
.shimmer {
  position: relative;
  overflow: hidden;
  background: var(--panel-soft);
}

.shimmer::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
  animation: shimmer-sweep 1.4s infinite;
}

.shimmer-line {
  display: block;
  height: 0.85em;
  border-radius: 999px;
  margin-bottom: 0.6em;
}

.shimmer-line:last-child {
  margin-bottom: 0;
}

.shimmer-block {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
}

.shimmer-row {
  display: flex;
  gap: 1rem;
  align-items: center;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}

@keyframes shimmer-sweep {
  to {
    transform: translateX(100%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .spinner,
  .shimmer::after {
    animation: none;
  }
}

/* Party hire — toy picker and enquiry form. Reuses .catalogue-grid's
   auto-fill sizing and .toy-card's panel/line/radius/shadow so curated
   party toys read as the same kind of card as the browse grid. */
.party-toy-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: 1rem;
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
}

.party-toy-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}

.party-toy-card label {
  display: flex;
  flex-direction: column;
  cursor: pointer;
}

.party-toy-card input[type="checkbox"] {
  align-self: flex-start;
  margin: 0.6rem 0 0.4rem 0.6rem;
}

.party-toy-thumb,
.party-toy-thumb-placeholder {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--panel-soft);
}

.party-toy-name {
  display: block;
  padding: 0.75rem 0.85rem 0.25rem;
  font-weight: 600;
}

.party-toy-large {
  display: inline-block;
  margin: 0 0.85rem 0.75rem;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  background: var(--cloud-sky);
  font-size: 0.8rem;
}

.party-toy-empty,
.party-selected-empty,
.party-picker-hint,
.party-search-empty {
  color: var(--muted);
}

/* Empty until a cap is hit, so it must not reserve vertical space. */
#party-picker-message:empty {
  display: none;
}

/* Same colours as .party-toy-error below — hitting a cap is the same
   "needs your attention" register, not a new one. */
#party-picker-message {
  color: #8a4b12;
  background: var(--cloud-peach);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.85rem;
}

/* Distinguishes "we couldn't load the data" from the plain empty states
   above (which reuse .warning-note's colours rather than inventing new
   ones) — a fetch failure shouldn't read the same as "nothing here yet". */
.party-toy-error,
.party-search-error {
  color: #8a4b12;
  background: var(--cloud-peach);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.85rem;
}

.party-selected-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
}

.party-selected-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.5rem 0.4rem 0.9rem;
  border: 1px solid var(--cloud-sky-line);
  border-radius: 999px;
  background: var(--cloud-sky);
  color: var(--text);
  font-size: 0.9rem;
}

.party-selected-chip button {
  border: none;
  background: none;
  color: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.15rem;
}

.party-search-results {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
}

/* Printable toy shelf cards (admin).
   These rules deliberately live OUTSIDE @media print. The Finalise modal
   renders one real card as a live preview, so preview and printout are the
   same declarations rather than a screen-only lookalike that drifts. Only
   page-level concerns stay in the @media print block below. */
#print-cards-root {
  display: none;
}

.print-card {
  display: flex;
  flex-direction: column;
  gap: 2mm;
  width: 90mm;
  height: 130mm;
  padding: 4mm;
  /* Extra room at the bottom reserves the site URL's line, which is
     positioned out of flow below — see .print-card-site. */
  padding-bottom: 9mm;
  position: relative;
  border: 1px dashed #999;
  overflow: hidden;
  background: #fff;
  color: #000;
  break-inside: avoid;
}

/* height alone stretched the logo to the card's full width, since a column
   flex container defaults to align-items: stretch. */
.print-card-logo {
  height: 8mm;
  width: auto;
  object-fit: contain;
  align-self: flex-start;
}

.print-card-photo {
  max-width: 100%;
  max-height: 40mm;
  object-fit: contain;
}

.print-card-name {
  margin: 0;
  font-size: 14pt;
}

.print-card-id,
.print-card-site {
  margin: 0;
  font-size: 8pt;
  color: #555;
}

/* Taken out of the flex flow and pinned absolutely: as the last flex item,
   margin-top: auto only pushed it down when there was free space, so once
   the fields above filled the card it became the first thing overflow:
   hidden clipped. The site URL must always render, however much content
   sits above it, so it lives outside that flow entirely. */
.print-card-site {
  position: absolute;
  left: 4mm;
  right: 4mm;
  bottom: 3mm;
}

.print-card-tags,
.print-card-description {
  margin: 0;
  font-size: 9pt;
}

/* A fixed-height card cannot grow to fit a long description, so clamp it.
   The card's own overflow: hidden is the backstop. Kept short (4 lines, not
   6) to leave the pieces list below it room to breathe. */
.print-card-description {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}

.print-card-components {
  margin: 0;
  padding-left: 4mm;
  font-size: 9pt;
  max-height: 18mm;
  overflow: hidden;
}

@media print {
  body > *:not(#print-cards-root) {
    display: none;
  }

  /* 2 x 90mm + 10mm gap = 190mm across; 2 x 130mm + 10mm = 270mm down.
     Both inside A4's 190 x 277mm printable area at 10mm margins, so four
     cards to a sheet with their cut lines aligned. */
  #print-cards-root {
    display: flex;
    flex-wrap: wrap;
    gap: 10mm;
  }

  @page {
    size: A4;
    margin: 10mm;
  }
}

/* Screen-reader-only text. Used by the Toys tab's image readout, where a bare
   ✓/✗ is ambiguous without a word beside it. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.toy-image-cell {
  display: flex;
  gap: 0.55rem;
  align-items: center;
}

.toy-image-thumb {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 4px;
  flex: none;
}

.toy-image-missing-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed currentColor;
  opacity: 0.55;
}

.toy-image-readout {
  display: flex;
  flex-direction: column;
  font-size: 0.78rem;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
}

.toy-image-state-missing {
  color: #b4690e;
}

.toy-id {
  opacity: 0.5;
}

.admin-toys-actions {
  text-align: right;
}

.toy-edit-dialog {
  width: min(48rem, 92vw);
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 8px;
  padding: 1.1rem;
}

.toy-edit-dialog::backdrop {
  background: rgba(0, 0, 0, 0.45);
}

.toy-edit-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

.toy-edit-body {
  display: flex;
  gap: 1.4rem;
  flex-wrap: wrap;
}

.toy-edit-column {
  flex: 1;
  min-width: 15rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.toy-edit-footer {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  border-top: 1px solid rgba(0, 0, 0, 0.15);
  padding-top: 0.8rem;
  margin-top: 0.8rem;
}

.toy-photo-slot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  margin-bottom: 0.7rem;
}

.toy-photo-preview {
  max-width: 8rem;
  border-radius: 4px;
}

.toy-photo-strip {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.toy-photo-action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
}

.toy-photo-action .toy-photo-preview {
  max-width: 4.5rem;
}

.toy-photo-note {
  font-weight: 400;
  text-transform: none;
  opacity: 0.7;
}

/* Sticky rather than fixed, and rendered inside the Toys panel, so
   switchTab()'s existing panel.hidden takes it off screen with the rest of
   the tab and the mode survives a tab switch untouched. */
.print-select-bar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
  padding: 0.7rem 0.9rem;
  border-top: 1px solid rgba(0, 0, 0, 0.15);
  background: #fff;
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
}

.print-select-summary {
  font-weight: 600;
}

.print-select-hidden {
  font-weight: 400;
  opacity: 0.75;
}

.print-select-buttons {
  display: flex;
  gap: 0.5rem;
  margin-left: auto;
}

.print-cards-dialog {
  width: min(46rem, 92vw);
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 8px;
  padding: 1.1rem;
}

.print-cards-dialog::backdrop {
  background: rgba(0, 0, 0, 0.45);
}

.print-cards-body {
  display: flex;
  gap: 1.4rem;
  flex-wrap: wrap;
  align-items: flex-start;
}

.print-cards-fields {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 6px;
  padding: 0.7rem 0.9rem;
}

.print-cards-preview-label {
  margin: 0 0 0.4rem;
  font-weight: 600;
}

/* The preview holds a real .print-card at its true printed size, so it can
   outgrow a short viewport. */
.print-cards-preview {
  max-height: 60vh;
  overflow: auto;
}

.print-cards-status {
  min-height: 1.2em;
  margin: 0.6rem 0 0;
}

.print-cards-footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 0.8rem;
}
