/*
 * Lines & Veils (docs/lines_and_veils.md): the landing page, a deck's summary and
 * the questionnaire. Loaded by those three pages only, from a content_for(:head)
 * tag, on top of base.css and components/ui.css. They are public pages with no
 * Turbo, so a page-local stylesheet costs nothing in navigation.
 *
 * The look follows the Stitch "Sanctuary Tabletop" design reference closely (card
 * composition, palette, radii and shadows), scoped to these three pages only via
 * the custom properties below: `.lv-page` redefines the same --color-* tokens
 * base.css and ui.css already read, so every shared component these pages render
 * (buttons, inputs, the flash region, the landing header and footer) inherits the
 * new values automatically, and nothing outside `.lv-page` changes at all.
 *
 * The boundary colors (--lv-ok/--lv-veil/--lv-line, the reference's own hex
 * values) are a :root rule of their own below, not part of that scoping (Gary,
 * 2026-10-17): what Ok, Veil and Line mean is the same everywhere the
 * questionnaire's box and card markup renders, including a signed-in page
 * that keeps the app's own regular palette and never adds `.lv-page` at all
 * (My Lines & Veils' questionnaire, the signed-in edit-link dialog). Scoped to
 * `.lv-page` alone, those pile colors read as the surrounding text color
 * instead, every `var(--lv-ok)` and the rest resolving to nothing.
 *
 * Plus Jakarta Sans is self-hosted below, four static weights (400/600/700/800,
 * the ones DESIGN.md uses), the same way Fraunces and Literata are in base.css:
 * SIL Open Font License 1.1, see plus-jakarta-sans-OFL.txt beside the files in
 * app/assets/fonts/.
 */

@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/plus-jakarta-sans-400-96468787.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/plus-jakarta-sans-600-9288b21a.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/plus-jakarta-sans-700-1648affe.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/plus-jakarta-sans-800-98d61d09.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --lv-ok: #10b981;
  --lv-veil: #f59e0b;
  /* The reference's own boundary-line is #E11D48, which reads fine as a fill or a
     4px border accent but fails WCAG AA as small text on this background (axe
     caught it on the mini pile buttons). Lightened one step
     within the same rose-crimson family rather than widened to the old coral. */
  --lv-line: #f43f5e;
  --lv-ok-rgb: 16, 185, 129;
  --lv-veil-rgb: 245, 158, 11;
  --lv-line-rgb: 244, 63, 94;
}

.lv-page {
  --color-bg: #13131b;
  --color-surface: #1b1b23;
  --color-surface-raised: #1f1f27;
  --color-border: #35354f;
  --color-text: #e4e1ed;
  --color-text-muted: #c7c4d7;
  --color-primary: #5e5ce6;
  --color-primary-hover: #4f46e5;
  --color-primary-rgb: 94, 92, 230;
  --color-accent: #c2c1ff;
  --radius-lg: 1rem;
  --shadow: 0 4px 16px -2px rgba(0, 0, 0, 0.45);
}

/* The compound selector, not .lv-page alone: this page's own content_for(:head)
   tag puts lines_and_veils.css ahead of base.css and components/ui.css in the
   document (layouts/application.html.erb yields :head before its own
   stylesheet_link_tag call), so ui.css's .landing { font-family: "Inter" ... }
   would otherwise win this tie by load order alone, every class here being
   single and so equally specific. Requiring both classes (every page's root
   carries both) outranks it on specificity instead, regardless of order. */
.lv-page.landing {
  font-family: "Plus Jakarta Sans", "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* The column of the landing page and a deck's page: 936px, about a fifth wider than the
   site's own narrow column (768px, components/ui.css), which the other pages keep.
   (Gary, 2026-10-15. A phone is as it was: the container's own gutters.) */
.lv-page .container-narrow {
  max-width: 58.5rem;
}

/* There is no header on these pages (a Log in button would read as the tool's
   own), so nothing is pushed down to clear one. */
.lv-main {
  flex: 1;
  padding: 1.5rem 0 3rem;
}

/* A message flashed above the page already leaves its own gap at the top
   (components/ui.css), so the page need not leave the same one again. */
.flash-region:has(.flash) ~ .lv-page .lv-main {
  padding-top: 1rem;
}

/* ── Shared pieces ────────────────────────────────────────── */

.lv-eyebrow {
  color: var(--color-accent);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}

.lv-help,
.lv-empty {
  color: var(--color-text-muted);
  font-size: 0.9375rem;
}

.lv-empty {
  background: var(--color-surface);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius);
  padding: 1rem;
  margin: 1rem 0;
}

.lv-error {
  color: var(--lv-line);
  font-size: 0.9375rem;
  margin-top: 0.5rem;
}

.lv-privacy {
  color: var(--color-text-muted);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 0.875rem 1rem;
  margin: 2rem 0 1rem;
  font-size: 0.9375rem;
}

.lv-privacy strong {
  color: var(--color-text);
}

/* A link inside a line of text is told apart by more than its color. */
.lv-main p a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.lv-credit {
  color: var(--color-text-muted);
  font-size: 0.875rem;
  margin: 1rem 0 0;
}

/* Two credits in a row (Ron Edwards's, then Roll Call's) read as one block. */
.lv-credit + .lv-credit {
  margin-top: 0.5rem;
}

/* The credits at the foot of every page, set off from what is above them (the lists,
   the form) by a quiet rule: 1px in the page's own border colour, with room above and
   below. The first credit's own top margin is the rule's padding now. */
.lv-credits {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--color-border);
}

.lv-credits > .lv-credit:first-child {
  margin-top: 0;
}

/* Roll Call's own line is small print: the size of the footer's, in italics, in a
   quieter grey (7.1:1 on the page, over the 4.5:1 small text needs). Its link is
   still plainly one: underlined, in the accent colour. */
.lv-credit--site {
  color: #a09eb8;
  font-size: 0.75rem;
  font-style: italic;
}

/* ── Landing ──────────────────────────────────────────────── */

.lv-hero {
  padding: 1.5rem 0 1rem;
}

.lv-hero h1 {
  font-size: clamp(2.25rem, 7vw, 3.25rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin-bottom: 1rem;
}

/* What it is, for someone who has never heard of it: three short lines in a quiet
   panel under the title, before the lede that says how to start. */
.lv-intro {
  max-width: 46rem;
  margin-bottom: 1.5rem;
  padding: 1rem 1.25rem;
  border-left: 3px solid var(--color-accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: rgba(255, 255, 255, 0.04);
}

.lv-intro h2 {
  margin: 0 0 0.5rem;
  font-size: 1.0625rem;
}

.lv-intro p {
  margin: 0 0 0.5rem;
  color: var(--color-text);
  font-size: 1rem;
  line-height: 1.55;
}

.lv-intro p:last-child {
  margin-bottom: 0;
}

.lv-lede {
  color: var(--color-text-muted);
  font-size: 1.125rem;
  max-width: 46rem;
  margin-bottom: 1.75rem;
}

/* The GM line, below the signed-in landing's tiles (Design Lead, 2026-10-17):
   .lv-intro's own callout box, just a top margin to clear the tiles above it. */
.lv-intro--gm {
  margin-top: 1.5rem;
  margin-bottom: 0;
}

.lv-new {
  background: linear-gradient(165deg, rgba(var(--color-primary-rgb), 0.1) 0%, var(--color-surface) 70%);
  border: 1px solid rgba(var(--color-primary-rgb), 0.25);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  box-shadow: var(--shadow);
}

.lv-new__label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.lv-new__row {
  display: flex;
  gap: 0.75rem;
  align-items: stretch;
}

.lv-new__input {
  flex: 1;
  min-width: 0;
  font-size: 1.0625rem;
}

.lv-new .lv-help {
  margin-top: 0.625rem;
}

.lv-piles h2,
.lv-how h2 {
  font-size: 1.375rem;
  margin: 2.5rem 0 1rem;
}

.lv-pile-list {
  list-style: none;
  display: grid;
  gap: 0.75rem;
}

.lv-pile {
  display: grid;
  grid-template-columns: 8rem 1fr;
  gap: 0.75rem;
  align-items: center;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left-width: 4px;
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
}

.lv-pile--ok { border-left-color: var(--lv-ok); }
.lv-pile--veil { border-left-color: var(--lv-veil); }
.lv-pile--line { border-left-color: var(--lv-line); }

.lv-pile__name {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 700;
}

.lv-pile--ok .lv-pile__name { color: var(--lv-ok); }
.lv-pile--veil .lv-pile__name { color: var(--lv-veil); }
.lv-pile--line .lv-pile__name { color: var(--lv-line); }

.lv-pile__means {
  color: var(--color-text-muted);
}

.lv-steps {
  display: grid;
  gap: 0.75rem;
  padding-left: 1.25rem;
  color: var(--color-text-muted);
}

.lv-steps strong {
  color: var(--color-text);
}

/* ── A deck's summary ─────────────────────────────────────── */

.lv-deck-head {
  padding: 1rem 0 0.5rem;
}

.lv-deck-name {
  font-size: clamp(1.75rem, 5vw, 2.5rem);
  line-height: 1.15;
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
  margin-bottom: 0.5rem;
}

.lv-answered {
  color: var(--color-text-muted);
  font-weight: 500;
  margin-bottom: 1rem;
}

/* The row of buttons under the deck's name: Answer it yourself (or Change your
   answers) at the left, and Print, Share and, for someone who has answered, Your
   link together at the right. Each a thumb tall. Where the group does not fit beside
   the first it wraps under it, still at the right; on a phone the first has its own
   row, and from 36rem up it is one row. */
.lv-actions-wrap {
  position: relative;
}

.lv-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.lv-actions .btn {
  min-height: 2.75rem;
}

.lv-actions .lv-answer-link {
  flex: 1 1 100%;
}

.lv-actions__group {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-left: auto;
}

.lv-actions__group .btn {
  justify-content: center;
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  white-space: nowrap;
}

@media (min-width: 36rem) {
  .lv-actions .lv-answer-link {
    flex: 0 0 auto;
  }
}

/* Asked until the private link has really been copied (from here or from the Your
   link panel); no close button, copying it is the way out. A row on a wide page, and
   on a phone the words, then the button across the width. */
.lv-save-link {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  margin: 1rem 0 0.5rem;
  padding: 0.875rem 1rem;
  background: rgba(var(--color-primary-rgb), 0.1);
  border: 1px solid rgba(var(--color-primary-rgb), 0.5);
  border-radius: var(--radius-lg);
}

.lv-save-link__icon {
  display: inline-flex;
  flex-shrink: 0;
  align-self: flex-start;
  margin-top: 0.125rem;
  color: var(--color-primary);
}

.lv-save-link__text {
  flex: 1 1 14rem;
  margin: 0;
  color: var(--color-text);
}

.lv-save-link__copy {
  flex: 1 1 100%;
}

.lv-save-link__copy .btn {
  width: 100%;
  min-height: 2.75rem;
  justify-content: center;
  white-space: nowrap;
}

@media (min-width: 36rem) {
  .lv-save-link__copy {
    flex: 0 0 auto;
  }

  .lv-save-link__copy .btn {
    width: auto;
  }
}

/* The link in a field of its own, selected, where a browser would not copy it: only
   ever put there by the clipboard controller after a copy that did not work. */
.lv-manual-copy {
  margin-top: 0.5rem;
}

/* The private link's panel: under the row, at its right end where Your link is, over
   what follows it, as wide as the column up to 26rem. Opened and closed by the
   popover controller. */
.lv-popover {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.5rem);
  right: 0;
  width: min(26rem, 100%);
  padding: 0.875rem 1rem;
  background: var(--color-surface-raised);
  border: 1px solid rgba(var(--color-primary-rgb), 0.5);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.lv-popover:focus {
  outline: none;
}

.lv-popover p {
  margin: 0 0 0.75rem;
  color: var(--color-text);
}

.lv-popover p:last-child {
  margin-bottom: 0;
}

.lv-danger {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
  margin-top: 1.25rem;
}

.lv-share__title {
  font-size: 1rem;
  margin-bottom: 0.25rem;
}

.lv-delete-button {
  color: var(--lv-line);
  border-color: rgba(var(--lv-line-rgb), 0.5);
  background: transparent;
}

.lv-delete-button:hover {
  background: rgba(var(--lv-line-rgb), 0.12);
  border-color: var(--lv-line);
}

/* The deck's page: what the table marked as a Line or a Veil, a group for each,
   each card once. No number is drawn here. */
.lv-result {
  margin-top: 2rem;
}

.lv-result__title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.125rem;
  letter-spacing: 0.01em;
}

.lv-result--line .lv-result__title { color: var(--lv-line); }
.lv-result--veil .lv-result__title { color: var(--lv-veil); }

.lv-result__means {
  margin: 0.25rem 0 0.75rem;
  color: var(--color-text-muted);
  font-size: 0.875rem;
}

.lv-cards {
  list-style: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface);
}

.lv-card {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 1rem;
}

.lv-card + .lv-card {
  border-top: 1px solid var(--color-border);
}

.lv-card__icon {
  color: var(--color-text-muted);
  display: inline-flex;
}

.lv-card__title {
  font-weight: 500;
  overflow-wrap: anywhere;
}

.lv-result--line .lv-card { box-shadow: inset 3px 0 0 var(--lv-line); }
.lv-result--veil .lv-card { box-shadow: inset 3px 0 0 var(--lv-veil); }

/* .toast lives in components/ui.css: both this public bundle and the app's own
   load it, and a campaign's share card (clipboard_controller.js) uses the same
   toast the Share and Copy private link buttons do. */

/* ── The questionnaire ────────────────────────────────────── */

.lv-page--quiz {
  min-height: 100vh;
  min-height: 100dvh;
}

.lv-main--quiz {
  padding-bottom: 1.5rem;
  /* A card or box sliding in starts beside its place: clipped at the screen's
     edge rather than making the page wider (and, on a phone, shrinking it). */
  overflow-x: clip;
  background:
    radial-gradient(ellipse 60% 40% at 50% 0%, rgba(var(--color-primary-rgb), 0.14), transparent 70%),
    radial-gradient(ellipse 50% 30% at 50% 90%, rgba(var(--lv-veil-rgb), 0.05), transparent 70%);
}

.lvq {
  /* A quarter wider than it was (30rem; Gary, 2026-10-15): the tabs, the header, the
     end steps and the pages' forms have the room. The card and the box are not
     wider: --lvq-h below keeps the width they were sized from. */
  max-width: 37.5rem;
  margin: 0 auto;
  /* The 1.5rem under is where the end steps' scroll area runs on, to the rule under it. */
  padding: 0 1rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  /* A bounded height, so the cards step below (and the card within it) can
     size itself to what's actually left on screen instead of its own content
     setting the page's height and needing a scroll to reach the buttons.
     The top 1.5rem is .lv-main--quiz's own padding; the box is border-box, so the
     1.5rem of .lvq's own padding under is inside this height, which leaves what is
     inside it exactly as tall as it was (100dvh less the two 1.5rem), and the rule
     under it at the bottom edge of the screen. */
  height: calc(100dvh - 1.5rem);
}

/* The credit line under the questionnaire: out of sight until the page is
   scrolled (the questionnaire itself fills the screen), in the same column. */
.lvq-foot {
  max-width: 37.5rem;
  margin: 0 auto;
  padding: 0 1rem;
}

/* The rule above the credits is the foot of the questionnaire: it is where the end
   steps' scroll area ends (see .lvq-end), with no gap between. */
.lvq-foot .lv-credits {
  margin-top: 0;
}

/* The cards and box steps are the ones that hold a sized card or box; the end
   step (custom, the end of the deck) is plain content and scrolls normally, so
   this is scoped to these two. */
.lvq-step[data-step="cards"],
.lvq-step[data-step="box"] {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* Beats the browser's own [hidden] rule (see .lvq-navbtns[hidden] above): this
   selector's own display: flex otherwise outranks the UA's [hidden] on
   specificity alone, so the step it names stays visible, stacked on the step
   the controller switched to, once a person finishes the deck. */
.lvq-step[data-step="cards"][hidden],
.lvq-step[data-step="box"][hidden] {
  display: none;
}

/* The six boxes as a row of tabs, above everything else in the column. Six equal
   columns, so it is the column's width at any screen (no sideways scroll at 320),
   and each tab is a 2.75rem tap. The names show where each has the room (the
   strip itself is the container asked: 416px, about 69px a tab); on a phone a tab
   is its icon alone. The strip is not inside any frame, so it stays put while a
   card or a box moves. */
.lvq-tabs {
  container-type: inline-size;
  flex-shrink: 0;
}

/* Six boxes and Finish, always seven. A narrow gap and a Finish a tenth wider than a
   box keep a box's tab 44px wide from a 357px screen up. */
.lvq-tabs__list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr)) minmax(0, 1.1fr);
  gap: 0.125rem;
  margin: 0;
  padding: 0;
}

.lvq-tab {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.125rem;
  width: 100%;
  min-height: 2.75rem;
  padding: 0.25rem 0.125rem;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: var(--radius);
  color: var(--color-text-muted);
  font: inherit;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.1;
  cursor: pointer;
  touch-action: manipulation;
}

.lvq-tab:hover {
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.06);
}

.lvq-tab:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
}

/* The box on show, also while on one of its cards. */
.lvq-tab[aria-current="step"] {
  color: var(--color-text);
  background: rgba(var(--color-primary-rgb), 0.16);
  border-bottom-color: var(--color-accent);
}

.lvq-tab__icon {
  display: inline-flex;
}

.lvq-tab__name {
  display: none;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@container (min-width: 26rem) {
  .lvq-tab__name {
    display: block;
  }
}

/* Finish is the one tab that is an action and not a place, so it is filled (white on
   the primary violet, 5.1:1), a pill, and set apart from the six boxes by a small gap
   and a hairline. It names itself wherever "Finish" fits under its picture (a seventh
   of the 328px row of a 360px phone is about 45px), and is the picture alone where
   it does not. The picture is a flag, and a check once every card has an answer. */
.lvq-tabs__finish {
  position: relative;
  margin-left: 0.25rem;
}

.lvq-tabs__finish::before {
  content: "";
  position: absolute;
  left: -0.25rem;
  top: 18%;
  bottom: 18%;
  width: 1px;
  background: var(--color-border);
}

.lvq-tab--finish,
.lvq-tab--finish:hover,
.lvq-tab--finish[aria-current="step"] {
  background: var(--color-primary);
  border-bottom-color: transparent;
  border-radius: 9999px;
  color: #fff;
}

.lvq-tab--finish:hover {
  background: var(--color-primary-hover);
}

/* At the end of the deck: ringed, since the fill is already the tab's. */
.lvq-tab--finish[aria-current="step"] {
  box-shadow: 0 0 0 2px var(--color-accent);
}

.lvq-tab--finish:focus-visible {
  outline-color: var(--color-accent);
  outline-offset: 2px;
}

.lvq-tab__icon--done,
.lvq-tab--finish[data-state="complete"] .lvq-tab__icon--flag {
  display: none;
}

.lvq-tab--finish[data-state="complete"] .lvq-tab__icon--done {
  display: inline-flex;
}

@container (min-width: 20.5rem) {
  .lvq-tab--finish .lvq-tab__name {
    display: block;
  }
}

/* How far the box is answered: a check once every card has an answer, a half-filled
   dot once some have, nothing before any has. */
.lvq-tab__mark {
  position: absolute;
  top: 0.1875rem;
  right: 0.1875rem;
  display: none;
  line-height: 0;
}

.lvq-tab[data-state="complete"] .lvq-tab__mark,
.lvq-tab[data-state="partial"] .lvq-tab__mark {
  display: inline-flex;
}

.lvq-tab__check,
.lvq-tab__half {
  display: none;
}

.lvq-tab[data-state="complete"] .lvq-tab__check {
  display: inline-flex;
  color: var(--lv-ok);
}

.lvq-tab[data-state="partial"] .lvq-tab__half {
  display: block;
  color: var(--color-text-muted);
}

.lvq-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.lvq-deck {
  min-width: 0;
  /* Takes whatever the meta on the right leaves, and nothing at all if that is
     nothing, so it is the one to give way before the meta has to wrap. On a
     box the meta is only Back and Undo (the box says what it is, on its face),
     so the deck's name has all of the rest. A plain flex-shrink here would
     still take a share of the squeeze from the counter. */
  flex: 1 1 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin: 0;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.lvq-deck a {
  color: var(--color-text-muted);
}

.lvq-meta {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}

.lvq-counter {
  font-size: 0.875rem;
  line-height: 1.15;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.lvq-quiet {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.375rem 0.5rem;
  background: none;
  border: 0;
  border-radius: var(--radius);
  color: var(--color-text-muted);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
}

/* On a phone Back and Undo are their icons alone (the words stay for a screen
   reader), which leaves the deck's name and the card's counter the room. */
@media (max-width: 420px) {
  .lvq-navbtns .lvq-quiet {
    min-width: 2.5rem;
    justify-content: center;
  }

  .lvq-quiet__text {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

.lvq-quiet:hover:not(:disabled) {
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.06);
}

.lvq-quiet:disabled {
  opacity: 0.4;
  cursor: default;
}

.lvq-navbtns {
  display: inline-flex;
  align-items: center;
  gap: 0.125rem;
  flex-shrink: 0;
}

/* Beats the browser's own [hidden] rule: an attribute selector and a class
   selector are equal specificity, and an author rule wins that tie over the
   user-agent one, so the plain display:inline-flex above would otherwise keep
   this visible even with the hidden attribute set. */
.lvq-navbtns[hidden] {
  display: none;
}

/* One element: the track, with the fill painted into the same box, so the two can
   never differ in height, top or radius (they did, by a device pixel, wherever the
   bar landed on a fraction of a pixel, and a separate rounded fill left a notch of
   track beside its end and collapsed to a dot with the whole gradient squashed into
   it when it was short). The gradient is laid across the whole track, and the track's
   own colour is laid over the part not yet reached (--lvq-progress: nothing until the
   controller sets it on the element), so a short bar is the first of the colours, and the leading edge is a
   clean cut. Registered as a percentage so that it can be animated. */
@property --lvq-progress {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

.lvq-progress {
  --lvq-progress: 0%;
  flex-shrink: 0;
  height: 0.375rem;
  border-radius: 9999px;
  background:
    linear-gradient(90deg, transparent var(--lvq-progress), var(--color-surface-raised) var(--lvq-progress)),
    linear-gradient(90deg, var(--color-primary), var(--lv-veil), var(--lv-ok));
  transition: --lvq-progress 0.3s ease;
}

/* The three piles: a quiet strip saying how many you have put where, not a
   scoreboard - the counter above already says how far through the deck you
   are. Where a card visibly lands is the single card itself (below), not a
   set of boxes; the source design has no running tally here at all. */
.lvq-piles {
  list-style: none;
  display: flex;
  justify-content: center;
  gap: 1.5rem;
}

.lvq-pile {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  font-weight: 600;
}

.lvq-pile__stack {
  display: none;
}

.lvq-pile__name {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}

.lvq-pile__count {
  font-variant-numeric: tabular-nums;
}

.lvq-pile--ok .lvq-pile__name svg { color: var(--lv-ok); }
.lvq-pile--veil .lvq-pile__name svg { color: var(--lv-veil); }
.lvq-pile--line .lvq-pile__name svg { color: var(--lv-line); }

/* The tactile card chrome: the card shell in hand and its flying copies wear
   it, so a future change to the look of "a card" needs only this one rule to
   stay in step between them. (The box is not a card: it draws itself, below,
   in the same frame.) */
.lvq-cardshell,
.lvq-flying {
  padding: 1.125rem 1.25rem 1.25rem;
  border-radius: 1rem;
  border: 1px solid var(--color-border);
  background: linear-gradient(180deg, #28283c 0%, var(--color-surface-raised) 42%, var(--color-surface) 100%);
  box-shadow: 0 12px 28px -4px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

/* What a card and a box are drawn in: all the height .lvq-step has (neither has a
   key hint under it now), measured here (container-type: size) so the shells below
   can be sized from it. Centred, so a tall narrow phone puts any
   spare height above and below the slot rather than stretching it.

   The slot is the 5:7 place the card or the box stands in, and three layers
   share it: the stack of cards behind (which stays still), the frame, which
   holds the card or the box and is the one thing that leans toward the pointer
   and slides, and the arrows beside it (which stay still too). Only the frame
   moves, so what is behind and beside the card is where it was when the card
   has gone and the next one has come. */
.lvq-board {
  /* 39.2rem is 140% of the 28rem the column's inside was, so a tall window gives the
     card and the box no more than it did when the column was narrower. */
  --lvq-h: max(22rem, min(100cqh, 140cqw, 39.2rem));
  /* How far the arrows' near edge is from the slot's: out beside it where there
     is room, and a phone's gutter is only the page's own 1rem, so there they
     stand in it and reach a little over the card's padding (never its words). */
  --lvq-chev-gap: -0.625rem;
  /* Half the contrast of the text colour (5.5:1 on the page, against its 10.8:1;
     a picture needs 3:1), brightening to the text colour on hover or focus. */
  --lvq-chev-color: #8b89a1;
  flex: 1 1 0;
  min-height: 0;
  container-type: size;
  display: grid;
  place-items: center;
}

.lvq-slot {
  position: relative;
  aspect-ratio: 5 / 7;
  height: var(--lvq-h);
  min-width: min(17.5rem, 100%);
}

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

/* One frame, exactly 5:7 (a poker card, 63 x 88mm), whatever the screen: the
   biggest that fits both the height the frame has and the width it has
   (140cqw is the height a full-width 5:7 would have), never below 22rem so a
   very short screen scrolls a little rather than shrinking it past legibility
   ("within reason" - Gary, round 2). Width follows from the ratio. Giving the
   card and the box the same rule, not two rules that agree today, is what
   keeps them the same size in the same place. A flex-grown height with a
   max-width clamp (which this replaces) left the ratio wrong on any phone
   taller than it is wide. */
.lvq-cardshell,
.lvq-box-shell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  box-sizing: border-box;
  aspect-ratio: 5 / 7;
  height: var(--lvq-h);
  /* The narrowest the card's own contents (the three answer buttons) let it be.
     On a short screen, where the height is at its floor and 5:7 would be narrower
     than that, the card was already the wider of the two, so the box is made as
     wide as it: they swap in the same place at every size. */
  min-width: min(17.5rem, 100%);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* A ghost stack of cards behind the one in hand: the deck is many deep, not one.
   Its own layer in the slot, under the frame and not in it, so it stays where it
   is while the card leans, is answered, or slides out and the next slides in. */
.lvq-stack {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.lvq-stack i {
  position: absolute;
  inset: 1px;
  border-radius: 1rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}

.lvq-stack i:nth-child(1) { transform: rotate(2deg) translateY(0.5rem); opacity: 0.5; }
.lvq-stack i:nth-child(2) { transform: rotate(-1.2deg) translateY(0.25rem); opacity: 0.75; }

/* The arrows beside a card (lvq-chev in the view; a box has none): Back on the
   left, and Next card on the right (on to the next card, this one left as it was;
   on a touch screen the one way to), exactly what the arrow keys do. Still, in
   the slot beside the frame, so they do not lean or slide with the card. 2.75rem
   tall, a thumb's size; as wide where there is room beside the card, and on a
   phone as wide as its gutter and a little of the card's padding make (its
   picture is centred in the gutter, so it never touches the card). Above the
   frame, so the part that reaches over the card's padding is theirs. */
.lvq-chev {
  position: absolute;
  top: 50%;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.75rem;
  margin-top: -1.375rem;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 9999px;
  color: var(--lvq-chev-color);
  cursor: pointer;
  touch-action: manipulation;
}

.lvq-chev--prev { right: calc(100% + var(--lvq-chev-gap)); }
.lvq-chev--next { left: calc(100% + var(--lvq-chev-gap)); }

.lvq-chev:hover,
.lvq-chev:focus-visible {
  color: var(--color-text);
}

.lvq-chev:hover {
  background: rgba(255, 255, 255, 0.06);
}

.lvq-chev:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

/* The picture is only as wide as a phone's gutter (1rem), so it never touches the
   card; beside a card on a bigger screen it is larger. */
.lvq-chev svg {
  width: 1rem;
  height: 1rem;
  /* A little thinner than the icons' 2. */
  stroke-width: 1.5;
}

/* Hidden for real: the display above would otherwise win over [hidden]. */
.lvq-chev[hidden] {
  display: none;
}

@media (min-width: 36rem) {
  /* Out beside the card with room to spare: 1.625rem from its edge (the picture,
     centred in a 2.75rem arrow, then sits about 36px from it), less where the
     window is only just wide enough, so the arrow is never nearer the window's
     edge than 0.75rem, and never nearer the card than 0.5rem. */
  .lvq-board { --lvq-chev-gap: clamp(0.5rem, calc((100vw - var(--lvq-h) * 5 / 7) / 2 - 3.5rem), 1.625rem); }
  .lvq-chev { width: 2.75rem; }
  .lvq-chev svg { width: 1.5rem; height: 1.5rem; }
}

/* Where the per-card content swaps. No chrome of its own: the shell supplies
   the card look, so the buttons below read as part of the same object. */
.lvq-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

.lvq-card {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: center;
}

.lvq-card[hidden] {
  display: none;
}

/* The deck box. It fills the same 5:7 frame a card is drawn in (the shell shares
   the card shell's size, place and padding, and draws nothing of its own): a lid
   band across the top (15% of the height) with its thumb notch, and under it
   the front face, which holds the category's icon and name, how many cards,
   every title in two columns, and at its foot, where a card's buttons sit,
   Mark all Ok and Review cards (the shell's own row, laid over the face). It is
   built in two layers with room between them: the BACK layer is what is behind
   the front face (the dark inside, the card edges, the hinged lid, the shadow
   on the table) and the FRONT layer is the face (so a card that rises out of
   the box, lv_motion.js, goes between the two and comes up from behind it).
   Every piece that moves is its own element, so the motion only ever has to
   change transform and opacity. */
.lvq-box-shell {
  /* The card's own padding and (invisible) border at the sides and the top. The
     foot is the box's own: its buttons and the Reset line directly under them stand
     lower on the face than a card's buttons do (Gary, 2026-10-15), with 0.75rem under
     the Reset line and 0.375rem between it and the buttons, and the title list has
     the room that frees. */
  padding: 1.125rem 1.25rem 0.75rem;
  border: 1px solid transparent;
  border-radius: 1rem;
  gap: 0.375rem;
  justify-content: flex-end;
}

.lvq-box-stage {
  position: absolute;
  inset: -1px;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Mark all Ok and Review cards: about three fifths the height of a card's buttons,
   standing on the same line they do, over the face. (Named through the shell,
   so they win over the answer row's own min-height, which is written further
   down.) */
.lvq-box-shell .lvq-actions--box,
.lvq-flying--box .lvq-actions--box {
  position: relative;
  z-index: 4;
  min-height: 0;
  height: 3rem;
}

.lvq-box-shell .lvq-secondary--box,
.lvq-flying--box .lvq-secondary--box {
  position: relative;
  z-index: 4;
}

.lvq-box {
  --lid: 15%;
  --seam: 0.25rem;
  --edge: rgba(255, 255, 255, 0.14);
  --inside: #0d0d15;
  position: absolute;
  inset: 0;
}

.lvq-box__layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.lvq-box__layer--back { z-index: 1; }
.lvq-box__layer--front { z-index: 3; }

/* What the box is made of, stepped back (scaled from its foot) while it opens:
   see openBox in lv_motion.js. */
.lvq-box__body {
  position: absolute;
  inset: 0;
  transform-origin: 50% 100%;
}

/* The shadow it stands in, just under the foot of the frame. */
.lvq-box__shadow {
  position: absolute;
  left: 6%;
  right: 6%;
  bottom: -0.625rem;
  height: 0.875rem;
  background: radial-gradient(closest-side, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.26) 62%, transparent);
}

.lvq-box__interior {
  position: absolute;
  inset: 0;
  border-radius: 1rem;
  background: linear-gradient(180deg, #08080f, var(--inside));
  box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.8);
}

/* The cards in the box, seen from the front: each one further back shows a
   little higher and a little narrower. Only the nearest two show through the
   seam while the lid is shut; the rest come into view when it lifts. */
.lvq-box__stack {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--lid) + var(--seam));
  height: 0;
}

.lvq-box__stack i {
  --n: 0;
  position: absolute;
  left: calc(4% + var(--n) * 2.2%);
  right: calc(4% + var(--n) * 2.2%);
  bottom: calc(var(--n) * 3px);
  height: 2px;
  border-radius: 1px;
  background: linear-gradient(90deg, rgba(222, 219, 240, 0.18), rgba(238, 236, 250, 0.92) 50%, rgba(222, 219, 240, 0.18));
  opacity: calc(1 - var(--n) * 0.13);
}

.lvq-box__stack i:nth-child(2) { --n: 1; }
.lvq-box__stack i:nth-child(3) { --n: 2; }
.lvq-box__stack i:nth-child(4) { --n: 3; }
.lvq-box__stack i:nth-child(5) { --n: 4; }
.lvq-box__stack i:nth-child(6) { --n: 5; }
.lvq-box__stack i:nth-child(7) { --n: 6; }

/* The lid is hinged on its top edge and has two faces: the outside (a plain flap
   with a thumb notch) and, once it has swung past upright, the inside. */
.lvq-box__lid {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--lid);
  transform-origin: 50% 0;
  transform: perspective(720px) rotateX(0deg);
  transform-style: preserve-3d;
}

.lvq-box__lid-out,
.lvq-box__lid-in {
  position: absolute;
  inset: 0;
  border-radius: 1rem 1rem 0.25rem 0.25rem;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.lvq-box__lid-out {
  border: 1px solid var(--edge);
  border-bottom-color: rgba(0, 0, 0, 0.55);
  background:
    linear-gradient(115deg, rgba(255, 255, 255, 0.13) 0%, rgba(255, 255, 255, 0) 44%),
    linear-gradient(180deg, #44445f 0%, #31314b 58%, #272740 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 8px 12px -6px rgba(0, 0, 0, 0.75);
}

/* A thumb notch cut into the lid's lower edge, showing the dark inside. */
.lvq-box__lid-out::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -1px;
  width: 2.25rem;
  height: 0.6rem;
  transform: translateX(-50%);
  border: 1px solid var(--edge);
  border-bottom: 0;
  border-radius: 1.1rem 1.1rem 0 0;
  background: var(--inside);
}

.lvq-box__lid-in {
  transform: rotateX(180deg);
  border: 1px solid rgba(0, 0, 0, 0.4);
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.04) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, #5d5d86, #444469);
  box-shadow: inset 0 -6px 10px -4px rgba(0, 0, 0, 0.5);
}

/* The front face: the label side of the box, and everything on it. Its foot is
   left clear for the shell's row of buttons (their 3rem, the Reset row's 1.25rem,
   the 0.375rem between them, the shell's 0.75rem of padding and its 1px border:
   5.4375rem, less the 1px each of the face's own border and the last row's divider,
   which the list already has: 5.3125rem) and then for the air between the last
   title and the top of the buttons, --lvq-box-air (Gary, 2026-10-15). That air is
   the face's own, so it holds in every box and state, however full the list and
   whether or not Reset is showing: 4.4% of the frame's height, from 1rem to 1.5rem
   (about 22px in a desktop window, never under 16px on a phone), plus whatever
   spare height the last row has. If a box is tight, it is the rows' spare height
   that gives way, never this; and on a window too short for every title the list
   scrolls inside this gap's edge, not through it. */
.lvq-box__panel {
  --lvq-box-air: clamp(1rem, calc(var(--lvq-h) * 0.044), 1.5rem);
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--lid) + var(--seam));
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding: 0.5rem 0.75rem calc(5.3125rem + var(--lvq-box-air));
  border: 1px solid var(--edge);
  border-radius: 0.25rem 0.25rem 1rem 1rem;
  background:
    linear-gradient(115deg, rgba(255, 255, 255, 0.09) 0%, rgba(255, 255, 255, 0.02) 38%, rgba(255, 255, 255, 0) 56%),
    linear-gradient(180deg, #2b2b46 0%, #1d1d30 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.13), 0 10px 18px -8px rgba(0, 0, 0, 0.7);
}

.lvq-box__head {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 0;
}

.lvq-box__icon {
  display: inline-flex;
  color: var(--color-accent);
}

.lvq-box__name {
  font-size: 1rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.005em;
}

.lvq-box__count {
  flex: none;
  margin: 0;
  color: var(--color-text-muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-align: center;
}

/* The shade the lid throws on the face as it lifts (lv_motion.js moves it down
   the face and fades it out as the lid goes over). */
.lvq-box__lidshade {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--lid) + var(--seam));
  height: 2.5rem;
  border-radius: 0.25rem 0.25rem 0 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0));
  opacity: 0;
}

/* Every title is on show: the room is the face's, in two columns, in a size that
   follows the frame (smaller on a short screen), with the rows' spacing giving way
   before the size does. Only a screen too short for all of them in that size, with
   the least spacing, scrolls, here. */
.lvq-box__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  pointer-events: auto;
  /* A finger over the list scrolls it, but a sideways one is left to the box's
     swipe (which opens it or goes back) rather than being claimed by the browser. */
  touch-action: pan-y;
}

/* On a screen too short for every title, the list fades at its foot while it has
   more to scroll to (lv_questionnaire_controller.js sets data-more). */
.lvq-box__scroll[data-more="true"] {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5rem), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5rem), transparent);
}

.lvq-box__scroll:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: 0.375rem;
}

/* The titles, in two columns that read down the left and then down the right. Each
   column is its own list, and a row is as tall as its text needs at the least; the
   face's spare height is shared out among the rows of each column, so the titles
   breathe where there is room and the spacing, not the type, gives way where there
   is not (the font size is the same either way). A column grows only up to a cap of
   its own text and 0.875rem for each of its rows (--n, set by the view): 7px above
   and below each, so a short list is not stretched down the whole face. (Where a
   browser has no calc-size, the cap before it is a line and a half of text and the
   same 0.875rem for each row, which comes to nearly the same.) */
.lvq-box__lists {
  display: flex;
  gap: clamp(1rem, calc(var(--lvq-h) * 0.03), 1.5rem);
  height: 100%;
  font-size: clamp(0.6875rem, calc(var(--lvq-h) * 0.026), 0.8125rem);
}

.lvq-box__contents {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  min-width: 0;
  height: 100%;
  max-height: calc(var(--n, 8) * (1.6em + 0.875rem + 1px));
  max-height: calc-size(max-content, size + var(--n, 8) * 0.875rem);
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: left;
}

/* A row: its mark and its title, set in the middle of the row's height (so the room
   a row is given is above and below it) and in from the divider's ends. */
.lvq-box__contents li {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: auto 1fr;
  align-content: center;
  align-items: start;
  column-gap: 0.375rem;
  padding: 0 clamp(0.25rem, calc(var(--lvq-h) * 0.0075), 0.5rem);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  line-height: 1.35;
  color: var(--color-text-muted);
}

/* The mark's own box, so its line does not make the row taller than its text. */
.lvq-box__pile {
  display: flex;
}

/* All three marks are in every row; only the one for the row's own pile is
   shown (display: none also keeps the other two out of a screen reader). */
.lvq-box__mark {
  display: none;
  flex-shrink: 0;
  padding-top: 0.0625rem;
}

.lvq-box__contents li[data-pile="ok"] .lvq-box__mark--ok,
.lvq-box__contents li[data-pile="veil"] .lvq-box__mark--veil,
.lvq-box__contents li[data-pile="line"] .lvq-box__mark--line {
  display: inline-flex;
}

.lvq-box__contents li[data-pile="ok"] .lvq-box__contents-title { color: var(--lv-ok); }
.lvq-box__contents li[data-pile="veil"] .lvq-box__contents-title { color: var(--lv-veil); }
.lvq-box__contents li[data-pile="line"] .lvq-box__contents-title { color: var(--lv-line); }

/* What the box shows below its buttons: a quiet way to clear the box (and nothing
   else, no count), once anything in it is answered. */
.lvq-box__reset {
  position: relative;
  padding: 0 0.5rem;
  min-height: 1.25rem;
  font-size: 0.75rem;
  line-height: 1.25rem;
}

/* Hidden for real: .lvq-quiet's own display would otherwise win over [hidden]. */
.lvq-box__reset[hidden] {
  display: none;
}

/* A bigger place to press than it looks: the button is a line of small print, and
   a thumb is not. */
.lvq-box__reset::after {
  content: "";
  position: absolute;
  inset: -0.375rem -0.75rem;
}

/* The card's face leans toward the pointer and a soft glare follows it
   (lv_tilt.js). The lean is put on the frame round the card. The stack of cards
   behind it and the arrows beside it are not in the frame, so they stay still. */
.lvq-frame.is-tilting {
  transition: transform 110ms ease-out;
  will-change: transform;
}

.lvq-frame.is-settling {
  transition: transform 420ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.lvq-glare {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 260ms ease-out;
}

/* A broad, soft sheen, not a hotspot: a spot of light 2.3 cards wide, centred on
   the point it is moved to, at most a tenth as strong as white, and fading by a
   raised cosine (smooth where it starts and where it ends, so there is no edge
   and no ring) to nothing at its rim. Soft white with a breath of the accent
   violet in it, never a colour shift. */
.lvq-glare__spot {
  position: absolute;
  left: 0;
  top: 0;
  width: 230%;
  aspect-ratio: 1;
  margin: -115% 0 0 -115%;
  border-radius: 50%;
  background: radial-gradient(circle closest-side, rgba(238, 232, 255, 0.1) 0%, rgba(238, 232, 255, 0.0976) 10%, rgba(238, 232, 255, 0.0905) 20%, rgba(238, 232, 255, 0.0794) 30%, rgba(238, 232, 255, 0.0655) 40%, rgba(238, 232, 255, 0.05) 50%, rgba(238, 232, 255, 0.0345) 60%, rgba(238, 232, 255, 0.0206) 70%, rgba(238, 232, 255, 0.0095) 80%, rgba(238, 232, 255, 0.0024) 90%, rgba(238, 232, 255, 0) 100%);
  will-change: transform;
}

/* What is written on the card, and its buttons, lie over the glare, never under
   it, so the glare lights the card's surface and leaves the words exactly as
   they are without it. */
.lvq-cardshell > .lvq-stage,
.lvq-cardshell > .lvq-actions,
.lvq-flying-shell > .lvq-stage,
.lvq-flying-shell > .lvq-actions:not(.lvq-actions--box) {
  position: relative;
  z-index: 2;
}

/* A card coming out of a box (_openBox in lv_motion.js): the card itself, the
   shadow it casts as it lifts, and the glint that crosses its face. */
.lvq-rise {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  transform-origin: 50% 50%;
  pointer-events: none;
  will-change: transform, opacity;
}

/* The copy that is over the box's front wall (the other is under it). */
.lvq-rise--ahead { z-index: 5; }

.lvq-rise__shadow {
  position: absolute;
  inset: 0;
  border-radius: 1rem;
  box-shadow: 0 24px 38px -8px rgba(0, 0, 0, 0.72);
  opacity: 0;
}

/* A rim of light round the card as it comes up out of the dark, the colour of
   the rest of the app's accent. */
.lvq-rise__glow {
  position: absolute;
  inset: 0;
  border-radius: 1rem;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.18), 0 0 26px 2px rgba(var(--color-primary-rgb), 0.55), 0 0 60px 8px rgba(var(--color-primary-rgb), 0.25);
  opacity: 0;
}

.lvq-rise__face {
  position: absolute;
  inset: 0;
}

.lvq-rise__gloss {
  position: absolute;
  inset: 0;
  border-radius: 1rem;
  overflow: hidden;
}

.lvq-rise__band {
  position: absolute;
  top: -10%;
  bottom: -10%;
  left: 0;
  width: 34%;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.32) 50%, rgba(255, 255, 255, 0) 100%);
  transform: translateX(-130%) skewX(-16deg);
}

/* One card of the handful that spill out of a box on Mark all Ok. */
.lvq-petal {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 3.5rem;
  padding: 0;
  border-radius: 0.5rem;
  color: var(--color-accent);
  box-shadow: 0 6px 10px -3px rgba(0, 0, 0, 0.65), inset 0 1px 0 rgba(255, 255, 255, 0.14);
  pointer-events: none;
}

.lvq-card__top {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

.lvq-card__category {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--lv-veil);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.lvq-card__category::before {
  content: "";
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 8px currentColor;
}

.lvq-card__number {
  color: var(--color-text-muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
}

.lvq-card__body {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin: auto 0;
}

.lvq-card__sigil {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 4.25rem;
  height: 4.25rem;
  margin-bottom: 0.25rem;
  border-radius: 1rem;
  background: var(--color-bg);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
  color: var(--color-accent);
}

.lvq-card__title {
  font-size: clamp(1.375rem, 5.5vw, 1.75rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.lvq-card__description {
  max-width: 20rem;
  color: var(--color-text-muted);
  font-size: 1rem;
  line-height: 1.5;
}

/* While a finger drags the card, the whole shell tints toward the pile it is heading for. */
.lvq-cardshell[data-swipe='ok'] { border-color: var(--lv-ok); box-shadow: 0 0 0 2px rgba(var(--lv-ok-rgb), 0.35), 0 12px 28px -4px rgba(0, 0, 0, 0.6); }
.lvq-cardshell[data-swipe='veil'] { border-color: var(--lv-veil); box-shadow: 0 0 0 2px rgba(var(--lv-veil-rgb), 0.35), 0 12px 28px -4px rgba(0, 0, 0, 0.6); }
.lvq-cardshell[data-swipe='line'] { border-color: var(--lv-line); box-shadow: 0 0 0 2px rgba(var(--lv-line-rgb), 0.35), 0 12px 28px -4px rgba(0, 0, 0, 0.6); }

/* The copy of a card that flies to its pile. It carries the shell's own chrome
   (above) rather than the live card's, since the clone is appended outside the
   shell for the moment it is in flight. */
.lvq-flying {
  inset: auto;
  overflow: hidden;
  transform-origin: center;
  will-change: transform, opacity;
}

/* The flex layout .lvq-cardshell gives its stage/actions/secondary children,
   reapplied to the whole-card clone (_cloneShell in the controller) built
   from those same three pieces. Not .lvq-cardshell itself: its other rules
   (aspect-ratio, flex, align-self) are
   about sizing the real shell in the page, not a fixed-position
   clone already sized to match it from the outside. */
.lvq-flying-shell {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

/* The copy of a box is the box's own padding and nothing of a card's: no panel
   behind it, and its shadow is let out. */
.lvq-flying--box {
  justify-content: flex-end;
  overflow: visible;
  border-color: transparent;
  background: none;
  box-shadow: none;
}

.lvq-actions {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
  /* The tallest the card's own buttons ever are (their hints wrap to two
     lines in a narrow frame, 83px at the most), shared with the box's row below
     so the two sit at the same height whichever you are looking at. */
  min-height: 5.25rem;
}

/* Two buttons at a time - overrides the grid above rather than repeating the
   rest of .lvq-actions. Flex, so when Mark all Ok is hidden (nothing left to mark)
   Next box takes its place, or, on the last box, Review cards takes the whole row.
   The height is the answer row's, so the buttons sit where the card's Ok, Veil
   and Line sit. */
.lvq-actions--box {
  display: flex;
  gap: 0.5rem;
}

.lvq-actions--box .btn {
  flex: 1 1 0;
  padding: 0.5rem;
  border-radius: 0.75rem;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
}

.lvq-actions--box .lvq-boxbtn--ok {
  border-color: rgba(var(--lv-ok-rgb), 0.45);
  background: rgba(0, 0, 0, 0.22);
  color: var(--color-text);
}

.lvq-actions--box .lvq-boxbtn--ok svg { color: var(--lv-ok); }

/* Next box takes Mark all Ok's place once there is nothing left to mark. */
.lvq-actions--box .lvq-boxbtn--next {
  border-color: rgba(var(--color-primary-rgb), 0.55);
  background: rgba(0, 0, 0, 0.22);
  color: var(--color-text);
}

.lvq-actions--box .lvq-boxbtn--next:hover {
  border-color: var(--color-accent);
  background: rgba(var(--color-primary-rgb), 0.16);
}

.lvq-actions--box .lvq-boxbtn--ok:hover {
  border-color: var(--lv-ok);
  background: rgba(var(--lv-ok-rgb), 0.14);
}

/* Beats the browser's own [hidden] rule (see .lvq-navbtns[hidden] above):
   .btn's own display: inline-flex (base.css) is an equal-specificity author
   rule that otherwise outranks the UA's [hidden] the same way, so Mark all
   Ok stayed on screen, labelled "Mark the other 0 Ok", once nothing was left
   for it to mark. Scoped to this one button rather than a blanket .btn
   [hidden] rule in base.css, which nothing else needs yet. */
.lvq-actions--box [data-lv-questionnaire-target="markAllOk"][hidden],
.lvq-actions--box [data-lv-questionnaire-target="nextBox"][hidden] {
  display: none;
}

.lvq-answer {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.125rem;
  padding: 0.625rem 0.375rem 0.5rem;
  background: rgba(0, 0, 0, 0.22);
  border: 1px solid var(--color-border);
  border-radius: 0.75rem;
  color: var(--color-text);
  font: inherit;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}

.lvq-answer:active {
  transform: scale(0.97);
}

.lvq-answer__label {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-weight: 700;
  font-size: 1rem;
}

.lvq-answer__hint {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.lvq-answer__key {
  position: absolute;
  right: 0.375rem;
  top: 0.3rem;
  padding: 0 0.3rem;
  background: var(--color-surface-raised);
  border-radius: 0.25rem;
  color: var(--color-text-muted);
  font: 0.625rem ui-monospace, SFMono-Regular, Menlo, monospace;
}

.lvq-answer--ok { border-color: rgba(var(--lv-ok-rgb), 0.4); }
.lvq-answer--ok .lvq-answer__label svg,
.lvq-answer--ok .lvq-answer__hint { color: var(--lv-ok); }
.lvq-answer--ok:hover,
.lvq-answer--ok[aria-pressed='true'] { background: rgba(var(--lv-ok-rgb), 0.14); border-color: var(--lv-ok); }

.lvq-answer--veil { border-color: rgba(var(--lv-veil-rgb), 0.4); }
.lvq-answer--veil .lvq-answer__label svg,
.lvq-answer--veil .lvq-answer__hint { color: var(--lv-veil); }
.lvq-answer--veil:hover,
.lvq-answer--veil[aria-pressed='true'] { background: rgba(var(--lv-veil-rgb), 0.14); border-color: var(--lv-veil); }

.lvq-answer--line { border-color: rgba(var(--lv-line-rgb), 0.4); }
.lvq-answer--line .lvq-answer__label svg,
.lvq-answer--line .lvq-answer__hint { color: var(--lv-line); }
.lvq-answer--line:hover,
.lvq-answer--line[aria-pressed='true'] { background: rgba(var(--lv-line-rgb), 0.14); border-color: var(--lv-line); }

/* The row under the box's buttons, which holds Reset this box (a card has no such
   row: its Ok, Veil and Line are the last thing in it, and stand as far from its
   bottom as from its sides). It is the one line of text Reset is, and keeps that
   height when Reset is not showing, so the buttons above do not move when it comes. */
.lvq-secondary--box {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 1.25rem;
}

/* The 1 2 3 badges on Ok, Veil and Line are for a keyboard: a touch screen has none.
   A touch screen is a coarse pointer that cannot hover; no hover alone is also a
   machine with no pointer at all, which is driven from the keyboard. */
@media (hover: none) and (pointer: coarse) {
  .lvq-answer__key { display: none; }
}

/* The end of the deck: a person's own cards. .lvq's
   height is fixed (see above), so without min-height: 0 this step's default
   flex min-height of auto would hold it to its content size and a short
   viewport, or five custom-card rows, would spill past .lvq's box into the
   footer below instead of scrolling. */
.lvq-form {
  /* The custom step's own form tag, otherwise unstyled: without this it is
     the actual flex item .lvq sizes (its .lvq-end child is not), so it needs
     the same treatment this rule gives .lvq-end. display: contents lifts
     that child in its place instead of repeating the rule on both. */
  display: contents;
}

/* The scroll area of the end steps runs on, through .lvq's own 1.5rem of padding, to
   the rule under it (the credits'), so the rule is its bottom edge and what scrolls
   goes up under the line, not off above it with a gap. A short fade just above the
   line says there is more; 1.5rem of padding keeps the last of the content clear of
   the fade once it is scrolled to the end. */
.lvq-end {
  flex: 1 1 auto;
  padding: 0.5rem 0 1.5rem;
  margin-bottom: -1.5rem;
  min-height: 0;
  overflow-y: auto;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5rem), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5rem), transparent);
}

.lvq-end h2 {
  font-size: 1.375rem;
  margin-bottom: 0.5rem;
}

.lvq-end h2:focus {
  outline: none;
}

.lvq-end p {
  color: var(--color-text-muted);
}

.lvq-custom-warning {
  background: rgba(var(--lv-veil-rgb), 0.08);
  border: 1px solid rgba(var(--lv-veil-rgb), 0.3);
  border-radius: var(--radius);
  padding: 0.625rem 0.875rem;
  margin: 0.75rem 0;
  color: var(--color-text) !important;
  font-size: 0.9375rem;
}

.lvq-custom-rows {
  display: grid;
  gap: 0.75rem;
  margin: 1rem 0;
}

.lvq-custom-row {
  display: grid;
  gap: 0.5rem;
  padding: 0.75rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

/* The field, and the way to take the card out in the row's top-right corner: quiet
   (a small trash bin in a muted grey, 5.5:1 on the row) until the pointer or the
   keyboard is on it, and a 44px target, drawn out toward the corner into the row's
   padding. */
.lvq-custom-row__top {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.lvq-custom-row__text {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.lvq-custom-row__remove {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin: 0 -0.5rem 0 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius);
  color: #8b89a1;
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease;
}

.lvq-custom-row__remove:hover,
.lvq-custom-row__remove:focus-visible {
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.08);
}

.lvq-custom-row__remove:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 0;
}

.lvq-custom-row__text .form-input {
  padding-right: 4.5rem;
}

.lvq-custom-count {
  position: absolute;
  right: 0.625rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.lvq-custom-row__piles {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.375rem;
}

.lvq-choice {
  position: relative;
  display: block;
  cursor: pointer;
}

/* The radio is the whole pill, invisible, so the label's own look is what shows.
   Written as input[type="radio"] to outrank ui.css's own radio, which this page's
   stylesheet loads ahead of. */
.lvq-choice input[type="radio"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
}

.lvq-choice span {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-size: 0.875rem;
  font-weight: 600;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.lvq-choice--veil span { color: var(--lv-veil); }
.lvq-choice--line span { color: var(--lv-line); }
.lvq-choice--veil input:checked + span { background: rgba(var(--lv-veil-rgb), 0.16); border-color: var(--lv-veil); }
.lvq-choice--line input:checked + span { background: rgba(var(--lv-line-rgb), 0.16); border-color: var(--lv-line); }
.lvq-choice input:focus-visible + span { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.lvq-answered-note {
  margin: 1rem 0 0.25rem;
}

/* At the top of the end step while any card is unanswered: which boxes still have
   some, each name a link back to its box. Submit waits. */
.lvq-notfinished {
  margin: 0 0 0.75rem;
  padding: 0.625rem 0.875rem;
  background: rgba(var(--lv-veil-rgb), 0.1);
  border: 1px solid rgba(var(--lv-veil-rgb), 0.45);
  border-radius: var(--radius);
  color: var(--color-text);
  font-size: 0.9375rem;
}

.lvq-notfinished p {
  margin: 0;
}

.lvq-notfinished__link {
  padding: 0;
  background: none;
  border: 0;
  color: var(--color-accent);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.lvq-notfinished__link:hover {
  color: var(--color-text);
}

.lvq-notfinished__link:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: 0.125rem;
}

.lvq-end-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
}

/* The honeypot (lines_and_veils/_honeypot): off the page, not display:none, which a
   script skips. */
.lv-hp {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.lvq-full {
  max-width: 39rem;
  margin: 2rem auto;
  text-align: center;
}

.lvq-full h1 {
  margin-bottom: 0.75rem;
}

.lvq-full p {
  color: var(--color-text-muted);
  margin-bottom: 1.25rem;
}

.lvq-full__actions {
  justify-content: center;
}

/* ── The dialog card (Gary, 2026-10-17) ──────────────────────
   The sign-out notice and the private edit link's own confirm step (every
   variant: import, same, overwrite, and signed out, Continue/Start my own):
   a small centered card instead of a bare two-line heading floating on the
   page, so a real choice reads as one. Icon badge, a short heading, a body
   line, buttons stacked full width (primary, then outline, Cancel last as
   a quiet text link so it never reads as a third real choice), and room for
   one line of muted helper text under a destructive button. */
.lv-dialog {
  max-width: 30rem;
  margin: 3rem auto;
  padding: 2rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  text-align: center;
}

.lv-dialog__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
  margin-bottom: 1.25rem;
}

/* The sign-out notice's own badge: access, not action. */
.lv-dialog__badge--lock {
  background: color-mix(in srgb, var(--color-amber) 16%, transparent);
  color: var(--color-amber);
}

.lv-dialog__heading {
  font-size: 1.375rem;
  line-height: 1.3;
  margin: 0 0 0.5rem;
}

.lv-dialog__body {
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  line-height: 1.5;
  margin: 0 0 1.5rem;
}

.lv-dialog__actions {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.lv-dialog__actions .btn {
  width: 100%;
}

.lv-dialog__helper {
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  margin: -0.25rem 0 0;
}

.lv-dialog__cancel {
  background: none;
  border: none;
  color: var(--color-text-muted);
  font-size: 0.875rem;
  font-family: inherit;
  padding: 0.5rem;
  margin-top: 0.25rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.lv-dialog__cancel:hover,
.lv-dialog__cancel:focus-visible {
  color: var(--color-text);
}

@media (max-width: 480px) {
  .lv-dialog {
    margin: 1.5rem auto;
    padding: 1.5rem;
  }
}

/* ── Phones ───────────────────────────────────────────────── */

@media (max-width: 640px) {
  .lv-new__row {
    flex-direction: column;
  }

  .lv-pile {
    grid-template-columns: 1fr;
    gap: 0.25rem;
  }

  .lvq-piles {
    gap: 1rem;
    font-size: 0.75rem;
  }

  .lvq-answer__hint {
    font-size: 0.625rem;
  }
}

/* ── Print: a plain sheet of the Lines and Veils ──────────── */

@media print {
  .landing-footer,
  .dice-bg,
  .flash-region,
  .lv-actions-wrap,
  .lv-save-link,
  .lv-danger,
  .lv-eyebrow,
  .skip-link {
    display: none !important;
  }

  body,
  .lv-main {
    background: #fff !important;
    color: #000 !important;
  }

  .lv-main {
    padding: 0;
  }

  .lv-main *,
  .lv-page * {
    color: #000 !important;
    background: transparent !important;
    box-shadow: none !important;
  }

  .lv-result {
    break-inside: avoid;
    margin-top: 1rem;
  }

  .lv-cards,
  .lv-card + .lv-card {
    border-color: #999 !important;
  }

  .lv-card {
    padding: 0.2rem 0.5rem;
  }

  .lv-deck-name {
    font-size: 1.5rem;
  }
}

/* ── Reduced motion ───────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .lvq-progress,
  .lvq-answer,
  .lvq-choice span {
    transition: none;
  }
}
