/* Quiet Shelf shared components: icons, button, list row, sheet, panel, the bar, tile, eyebrow,
   cover and book row. Tokens only (theme/tokens.css); sizes follow the framework floors
   (QUESTIONS Q0.4): primary 72, standard and ghost 56, rows 56 with a 48 hard floor, radii
   12 / 14 / 999, covers 4 to 7. QS-13: shapes match the regenerated reference's .btn, .list / .li,
   .now / .ev, .srch, .tile, .eb, .row / .bk and .cover. Views use these classes and add no
   competing rules of their own (fid-shell-notes.md lists the ones to remove). */

.ic {
  width: var(--icon);
  height: var(--icon);
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Button ---------- */
.qsBtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--target-std);
  min-width: var(--target-min);
  padding: 8px 20px;
  border: var(--line-w) solid transparent;
  border-radius: var(--r-card);
  font-family: var(--font-body);
  font-size: var(--fs-action);
  font-weight: var(--weight-head);
  line-height: var(--lh-head);
  text-decoration: none;
  cursor: pointer;
}

.qsBtn--primary {
  min-height: var(--target-primary);
  background: var(--primary);
  color: var(--on-primary);
}

.qsBtn--standard {
  background: var(--primary);
  color: var(--on-primary);
}

.qsBtn--secondary {
  background: var(--primary-soft);
  color: var(--primary);
}

.qsBtn--ghost {
  background: transparent;
  color: var(--fg);
  border: var(--line-w) solid var(--line-strong);
}

.qsBtn:focus-visible,
.qsRow--action:focus-visible,
.qsSheet__close:focus-visible {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* ---------- List row ---------- */
.qsRow {
  display: flex;
  align-items: center;
  gap: 14px;
  box-sizing: border-box;
  width: 100%;
  min-height: var(--target-std);
  padding: var(--row-pad) 14px;
  margin: 0;
  background: var(--card);
  color: var(--fg);
  border: 0;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  text-align: left;
}

.qsRow--action {
  cursor: pointer;
}

.qsRow--action:hover {
  background: var(--primary-soft);
}

.qsRow__icon {
  display: flex;
  color: var(--primary);
}

.qsRow__text {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

.qsRow__title {
  font-weight: var(--weight-action);
}

.qsRow__sub {
  color: var(--muted);
}

/* The ", " between a row's title and subtitle: read by screen readers, not shown. */
.qsRow__sep {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.qsRow__chev {
  display: flex;
  color: var(--muted);
}

/* A list is one card: --card, a 1 px --line edge, radius 12; rows are divided by --line. */
.qsList {
  margin: 0;
  padding: 0;
  list-style: none;
  border: var(--line-w) solid var(--line);
  border-radius: var(--r-card);
  background: var(--card);
}

.qsList > li + li,
.qsList > .qsRow + .qsRow {
  border-top: var(--line-w) solid var(--line);
}

/* Rows follow the card's corners (no overflow clip on the list, so no focus ring is cut off). */
.qsList > li:first-child > .qsRow,
.qsList > .qsRow:first-child {
  border-top-left-radius: calc(var(--r-card) - var(--line-w));
  border-top-right-radius: calc(var(--r-card) - var(--line-w));
}

.qsList > li:last-child > .qsRow,
.qsList > .qsRow:last-child {
  border-bottom-left-radius: calc(var(--r-card) - var(--line-w));
  border-bottom-right-radius: calc(var(--r-card) - var(--line-w));
}

/* A selected row (a chosen branch, language or slot). */
.qsRow[aria-current="true"],
.qsRow[aria-selected="true"],
.qsRow--selected {
  background: var(--primary-soft);
}

/* In a sheet the list keeps the gutter. */
.qsSheet .qsList {
  margin: 0 var(--gutter);
}


/* ---------- Panel (the reference .now and .ev): a --card box with a --line edge ---------- */
.qsPanel {
  box-sizing: border-box;
  padding: 11px 14px;
  border: var(--line-w) solid var(--line);
  border-radius: var(--r-card);
  background: var(--card);
  color: var(--fg);
}

/* ---------- The bar (DESIGN.md 5; the reference .srch): 56 px, radius 14, --card, a
   --line-strong edge, the search icon in --primary at the start. The large Home bar (.srch.big)
   has a --primary edge and the mic at the end. ---------- */
.qsBar {
  display: flex;
  align-items: center;
  gap: 11px;
  box-sizing: border-box;
  min-height: var(--target-std);
  padding: 0 17px;
  border: var(--line-w) solid var(--line-strong);
  border-radius: var(--r-bar);
  background: var(--card);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--fs-action);
  line-height: var(--lh-head);
  text-decoration: none;
}

.qsBar--big {
  border-color: var(--primary);
}

.qsBar__icon,
.qsBar__mic {
  display: flex;
  flex: none;
  color: var(--primary);
}

.qsBar__icon .ic,
.qsBar__mic .ic {
  width: 29px;
  height: 29px;
}

.qsBar__mic {
  margin-inline-start: auto;
}

/* ---------- Tile (the reference .tile): exactly three on Home ---------- */
.qsTiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

:root[data-text="large"] .qsTiles,
:root[data-text="larger"] .qsTiles {
  grid-template-columns: minmax(0, 1fr);
}

.qsTile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  box-sizing: border-box;
  height: 100%;
  min-height: 74px;
  padding: 11px;
  border: var(--line-w) solid var(--line);
  border-radius: var(--r-card);
  background: var(--card);
  color: var(--fg);
  font-size: var(--fs-body);
  font-weight: var(--weight-action);
  line-height: 1.15;
  text-decoration: none;
}

.qsTile__icon {
  display: flex;
  padding: 4px;
  border-radius: var(--r-card);
  background: var(--primary-soft);
  color: var(--primary);
}

.qsTile__icon .ic {
  width: 32px;
  height: 32px;
}

.qsTile__icon--catalog { background: var(--acc-catalog-wash); color: var(--acc-catalog); }
.qsTile__icon--way { background: var(--acc-way-wash); color: var(--acc-way); }
.qsTile__icon--events { background: var(--acc-events-wash); color: var(--acc-events); }
.qsTile__icon--discover { background: var(--acc-discover-wash); color: var(--acc-discover); }
.qsTile__icon--now { background: var(--acc-now-wash); color: var(--acc-now); }

[data-surface="kiosk"] .qsTile {
  min-height: var(--target-tile);
}

/* ---------- Eyebrow (the reference .eb): a section label, uppercase +0.08em, system bold 18,
   colored by its category accent text, with the See all link at the end. ----------
   <div class="qsEyebrow qsEyebrow--way"><h2 class="qsEyebrow__title">Staff picks</h2>
   <a class="qsEyebrow__more">See all</a></div> */
.qsEyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 var(--gap);
  color: var(--muted);
}

.qsEyebrow__title {
  margin: 0;
  color: inherit;
  font-family: var(--font-head);
  font-size: var(--fs-eyebrow);
  font-weight: var(--weight-head);
  letter-spacing: var(--ls-eyebrow);
  line-height: var(--lh-head);
  text-transform: uppercase;
}

.qsEyebrow--catalog { color: var(--acc-catalog-text); }
.qsEyebrow--way { color: var(--acc-way-text); }
.qsEyebrow--events { color: var(--acc-events-text); }
.qsEyebrow--discover { color: var(--acc-discover-text); }
.qsEyebrow--now { color: var(--acc-now-text); }

.qsEyebrow__more {
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
  min-width: var(--target-min);
  color: var(--primary);
  font-family: var(--font-body);
  font-size: var(--fs-action);
  font-weight: var(--weight-action);
  letter-spacing: 0;
  text-decoration: underline;
  text-transform: none;
}

/* ---------- Cover (DESIGN.md 5, REQ-COV-1, 2; components/cover.js) ----------
   30 / 76 / 84 px wide at 2:3 (a book row sizes its own), radius 4 / 7, a spine shadow and a 1 px
   drop. The fallback is the title and author in system bold, --on-cover on a two-stop gradient
   of one of the nine cover colors (QS-13): --cover-N at the top left, --cover-N under
   --cover-shade at the bottom right. Only darker, so the text keeps at least the cover color's
   own contrast (4.64:1 or more) over the whole tile. The image covers it once loaded. */
.qsCover {
  position: relative;
  display: block;
  flex: none;
  box-sizing: border-box;
  width: 76px;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: 4px 7px 7px 4px;
  background-color: var(--cover-1);
  background-image: linear-gradient(160deg, transparent, var(--cover-shade));
  color: var(--on-cover);
  box-shadow: var(--shadow-cover);
}

/* The spine, over the image too. */
.qsCover::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--shadow-spine);
  pointer-events: none;
}

.qsCover--30 { width: 30px; border-radius: 4px; }
.qsCover--76 { width: 76px; }
.qsCover--84 { width: 84px; }

.qsCover--c1 { background-color: var(--cover-1); }
.qsCover--c2 { background-color: var(--cover-2); }
.qsCover--c3 { background-color: var(--cover-3); }
.qsCover--c4 { background-color: var(--cover-4); }
.qsCover--c5 { background-color: var(--cover-5); }
.qsCover--c6 { background-color: var(--cover-6); }
.qsCover--c7 { background-color: var(--cover-7); }
.qsCover--c8 { background-color: var(--cover-8); }
.qsCover--c9 { background-color: var(--cover-9); }

.qsCover__fallback {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2px;
  padding: 8px 7px 7px 9px;
  overflow: hidden;
  font-family: var(--font-head);
  font-size: var(--fs-body);
  font-weight: var(--weight-head);
  line-height: 1.15;
  /* Break between words; a word too long for the tile is hyphenated, not cut at any letter. */
  overflow-wrap: break-word;
  -webkit-hyphens: auto;
  hyphens: auto;
}

.qsCover__t {
  display: -webkit-box;
  overflow: hidden;
  text-shadow: var(--shadow-cover-text);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}

.qsCover__a {
  display: -webkit-box;
  overflow: hidden;
  font-weight: var(--weight-body);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
}

.qsCover__i {
  display: none;
}

/* 30 px: no room for words at the body size, so the title's initial stands in. */
.qsCover--30 .qsCover__fallback {
  align-items: center;
  justify-content: center;
  padding: 0;
}

.qsCover--30 .qsCover__t,
.qsCover--30 .qsCover__a {
  display: none;
}

.qsCover--30 .qsCover__i {
  display: block;
}

.qsCover__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* An event in a book row (Home's For you): the events wash with its day and time, the same colours
   as Discover's event tile (views/discover-view.css .qsDsc__evCover). */
.qsCover--event {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px;
  text-align: center;
  background: var(--acc-events-wash);
  color: var(--acc-events-text);
  font-weight: 700;
  line-height: 1.2;
}
.qsCover__evDay,
.qsCover__evTime {
  overflow-wrap: anywhere;
}
@media (forced-colors: active) {
  .qsCover--event {
    border: var(--line-w) solid CanvasText;
  }
}

/* Until it loads the image is invisible (no broken-image box over the fallback). */
.qsCover[data-cover="pending"] .qsCover__img {
  opacity: 0;
}

.qsCover[data-cover="loaded"] .qsCover__fallback {
  visibility: hidden;
}

/* ---------- Book row (DESIGN.md 5 "Row with reason"; the reference .row / .bk) ----------
   A side-swipe row (QS-13, Chris 2026-10-01): three covers fit and about 0.4 of a fourth shows,
   so it reads as scrollable, at any width (3.4 item widths and 3 gaps per row). The patron
   scrolls it (swipe, wheel, or Tab to the next item, which the browser scrolls into view clear
   of the edge); it snaps to a cover and never moves on its own: no timer, no paging dots, never
   a carousel (REQ-DSC-1). Per item the cover, the title on one line and the muted reason on one
   line. No chevron: the whole item opens the book. */
.qsBookRow {
  display: flex;
  gap: var(--gap);
  /* 4 px of room on every side (pulled back by the margin) so a focus ring is never clipped by
     the scroller (2.4.7). */
  margin: -4px;
  padding: 4px;
  overflow-x: auto;
  scroll-padding-inline: 4px;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  list-style: none;
  /* The containing block of the items' visually hidden text, so it scrolls and clips with them
     instead of widening the page (1.4.10). */
  position: relative;
}

/* The same room again, above a view's plain list reset (home.css .qsScreen ul). */
.qsBookRow[role="list"] {
  margin: -4px;
  padding: 4px;
}

/* Never narrower than the text needs: 4.2em grows with Larger text and OS text scaling (fewer
   covers fit, the row still scrolls). */
.qsBookRow__item {
  flex: 0 0 auto;
  box-sizing: border-box;
  width: max(calc((100% - 3 * var(--gap)) / 3.4), 4.2em);
  scroll-snap-align: start;
}

/* No scrollbar chrome on a touch screen (the swipe and the cut fourth cover say it scrolls);
   with a mouse the thin bar stays. */
@media (hover: none) and (pointer: coarse) {
  .qsBookRow {
    scrollbar-width: none;
  }

  .qsBookRow::-webkit-scrollbar {
    display: none;
  }
}

.qsBookCard {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  /* The item sets the width (.qsBookRow__item). */
  width: 100%;
  min-height: var(--target-min);
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.2;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.qsBookCard .qsCover {
  width: 100%;
}

.qsBookCard__title,
.qsBookCard__reason {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.qsBookCard__title {
  margin-top: 6px;
  font-weight: var(--weight-action);
}

.qsBookCard__reason {
  color: var(--muted);
}

/* Larger text (REQ-SET-4, 1.4.4): the whole title and reason wrap instead of being cut off. */
:root[data-text] .qsBookCard__title,
:root[data-text] .qsBookCard__reason {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: break-word;
  -webkit-hyphens: auto;
  hyphens: auto;
}

.qsBookCard > .qsChev,
.qsBookCard__title > .qsChev,
.qsBookCard__title > .ic {
  display: none;
}

.qsBookCard:focus-visible,
.qsTile:focus-visible,
.qsBar:focus-visible,
.qsEyebrow__more:focus-visible {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* Cover credits (QS-13; components/cover.js rowCreditHtml, bookCreditHtml): one small muted line
   under a row, section or book head that shows Google Books or Open Library covers. The links are
   underlined (not told apart by color alone) and 48 px tall. */
.qsCredit {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--gap);
  margin: 0;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--fs-body);
}

.qsCredit__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.qsCredit__link:focus-visible {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* ---------- Sheet ---------- */
.qsSheet {
  box-sizing: border-box;
  width: 100%;
  max-width: 480px;
  max-height: 90dvh;
  margin: auto auto 0;
  padding: 0;
  border: 0;
  border-radius: var(--r-bar) var(--r-bar) 0 0;
  background: var(--card);
  color: var(--fg);
  box-shadow: var(--shadow-sheet);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  animation: qsSheetIn var(--motion-sheet) ease-out;
}

.qsSheet::backdrop {
  background: var(--scrim);
}

@keyframes qsSheetIn {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

.qsSheet__panel {
  padding: 8px 0 var(--gutter);
}

.qsSheet__grab {
  width: 40px;
  height: 4px;
  margin: 0 auto 4px;
  border-radius: var(--r-pill);
  background: var(--line-strong);
}

.qsSheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  padding: 0 var(--gutter) 8px;
}

.qsSheet__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-family: var(--font-head);
  font-size: var(--fs-section);
  font-weight: var(--weight-head);
  line-height: var(--lh-head);
}

.qsSheet__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: var(--target-min);
  min-height: var(--target-min);
  padding: 0 12px;
  border: var(--line-w) solid var(--line-strong);
  border-radius: var(--r-card);
  background: var(--card);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--weight-action);
  cursor: pointer;
}

/* A sheet that cannot close yet (canClose, e.g. a payment being sent): Close looks unavailable
   too, not only to assistive technology. The pay sheet says why in a visible line. */
.qsSheet__close[aria-disabled="true"] {
  color: var(--muted);
  cursor: not-allowed;
}

.qsSheet__body {
  padding: 0 0 var(--gutter);
}

.qsSheet__text {
  margin: 0 var(--gutter) var(--gap);
}

.qsSheet__actions {
  display: grid;
  gap: var(--gap);
  padding: 0 var(--gutter);
}

.qsSheet__title:focus-visible {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* The sheet's own status region (visually hidden). */
.qsSheet__live {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Rows fill the scrolling dialog: draw their ring inside so no edge is clipped. */
.qsRow--action:focus-visible {
  outline-offset: calc(-1 * (var(--focus-w) + 1px));
}

@media (forced-colors: active) {
  .qsSheet {
    border: var(--line-w) solid CanvasText;
    border-bottom: 0;
  }

  .qsCover,
  .qsBar,
  .qsTile,
  .qsPanel {
    border: var(--line-w) solid CanvasText;
  }

  /* No gradient behind the fallback text: the system colors only. */
  .qsCover {
    background-image: none;
  }

  .qsCover__fallback {
    background: Canvas;
    color: CanvasText;
  }

  .qsBookCard:focus-visible,
  .qsTile:focus-visible,
  .qsBar:focus-visible,
  .qsEyebrow__more:focus-visible,
  .qsCredit__link:focus-visible {
    outline: 3px solid Highlight;
  }

  .qsCredit__link {
    color: LinkText;
  }

  /* The 3 px ring with a 1 px offset fits the row's 4 px of room, so no edge is clipped. */
  .qsBookRow .qsBookCard:focus-visible {
    outline-offset: 1px;
  }

  /* Unavailable Close: the system's disabled color for its text and edge, not ButtonText. */
  .qsSheet__close[aria-disabled="true"] {
    color: GrayText;
    border-color: GrayText;
  }
}

@media (forced-colors: active) {
  .qsBtn:focus-visible,
  .qsSheet__close:focus-visible,
  .qsSheet__title:focus-visible {
    outline: 3px solid Highlight;
  }
}

/* ---------- Text scale, bold text and motion (QS-10a; REQ-SET-4, REQ-SET-5, DESIGN.md 3 and 4) ----------
   Shell.applyTheme puts the M7 settings on the root: data-text="large" (the type tokens in
   tokens.css scale by 1.25 over the OS text size), data-weight="bold" (the weight tokens step up)
   and data-motion="reduce" (also set from the OS preference, which the media block covers too). */
:root[data-weight="bold"] body {
  font-weight: var(--weight-body);
}

/* 200% text at 320 px (REQ-SET-4, WCAG 1.4.10): a screen or card grid never grows past the
   viewport for one long word; a word wider than its line breaks (break-word leaves min-content
   alone, so segments still wrap whole options). At the default size nothing changes. */
.qsScreen > *,
.qsCard > * {
  min-width: 0;
}

.qsScreen,
.qsSheet {
  overflow-wrap: break-word;
}

/* The kiosk attract CTA: scale 1.0 to 1.03 only, never opacity, at most 3 cycles
   (Idle.attractMotion; --motion-pulse and --pulse-cycles in tokens.css). */
.qsPulse {
  animation: qsPulse var(--motion-pulse) ease-in-out var(--pulse-cycles);
}

@keyframes qsPulse {
  0%, 100% { scale: 1; }
  50% { scale: 1.03; }
}

/* Reduce motion: nothing animates or transitions at all, the attract pulse included. */
:root[data-motion="reduce"] *,
:root[data-motion="reduce"] *::before,
:root[data-motion="reduce"] *::after {
  animation: none !important;
  transition: none !important;
  scroll-behavior: auto !important;
}

:root[data-motion="reduce"] {
  scroll-behavior: auto !important;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }

  :root {
    scroll-behavior: auto !important;
  }
}
