/* Review cards: card chrome, graded reveals, the grading scale, choice and
   map exercises, and study session controls. */

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 3rem;
}

.card--row,
.fact--row,
.clip--row {
  overflow: hidden;
  border-color: var(--line);
  border-radius: 1rem;
}

.card--row:hover,
.card--row:focus-visible,
.fact--row:hover,
.fact--row:focus-visible,
.clip--row:hover,
.clip--row:focus-visible {
  background-color: var(--card);
  background-image: linear-gradient(105deg, color-mix(in srgb, var(--accent) 11%, var(--card)), color-mix(in srgb, var(--wash) 52%, var(--card)) 48%, var(--card));
}

.card--shown-on-revealed {
  display: none;
}

.card--revealed,
.card:has(.card--reveal:checked) {
  display: flex;

  .card--shown-on-revealed {
    display: flex;
    flex-direction: column;
  }

  .card--hidden-on-revealed {
    display: none;
  }
}

.card--front,
.card--back {
  min-height: 10rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem 4rem;
  font-size: 1.25rem;
}

.card--back .card--shown-on-revealed {
  align-items: center;
  text-align: center;
}

/* Graded free-type reveals (steelman, use-in-sentence, pairs): the back is
   reading matter, not a flashcard answer: left-aligned prose, the learner's
   words set off as a quote. */
.card--back-prose {
  font-size: 1.05rem;
  padding: 1.5rem 2.5rem;
}

.card--back-prose .card--shown-on-revealed {
  align-items: stretch;
  text-align: start;
  gap: 1rem;
  width: 100%;
}

.card--kicker {
  font-family: var(--display);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.35rem;
}

.card--given {
  margin: 0;
  padding: 0.75rem 1rem;
  background-color: color-mix(in srgb, var(--wash) 42%, var(--card));
  border-left: 3px solid var(--accent);
  border-radius: 0 0.5rem 0.5rem 0;
  font-style: italic;
}

.card--given .card--kicker {
  font-style: normal;
}

.card--model:not(:first-child) {
  border-top: 1px solid var(--line);
  padding-top: 1rem;
}

/* Quantity answers ride the stage (stage.css) at card scale. */
.card--stage {
  font-size: clamp(1.25rem, 3vw, 1.75rem);
}

/* A cloze reads as one line of prose with the blank set into it: the input
   underlines like a paper blank and inherits the sentence's own type. */
.card--cloze {
  display: block;
  text-align: start;
  line-height: 2;
}

.card--cloze-blank {
  border: none;
  border-bottom: 2px solid var(--muted);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: center;
  padding: 0 0.15em;
  /* One blank width for every card, growing to fit what's typed, so the
     blank never gives the answer's length away. Browsers without
     field-sizing hold the fixed starting width, which hints nothing either. */
  field-sizing: content;
  min-width: 8ch;
  max-width: 100%;
}

.card--cloze-blank:focus {
  outline: none;
  border-bottom-color: var(--accent);
}

.card--cloze-answer {
  border-bottom: 2px solid var(--accent);
  padding: 0 0.15em;
}

.card--cloze-slot {
  display: inline-block;
  width: 5em;
  border-bottom: 2px solid var(--muted);
}

/* A breakdown asks every part at once: one row per part, the label reading
   down the left and the answer sitting on the right, so the whole shape of
   the thing stays legible while it is being filled in. */
.card--breakdown {
  width: min(38rem, 100%);
  display: grid;
  gap: 0.5rem;
  text-align: start;
}

.card--breakdown-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--card);
}

.card--breakdown-label {
  font-family: var(--display);
  font-weight: 640;
  line-height: 1.25;
}

/* The tier ask is three pills sharing one row: hidden radios under labels,
   so the whole pill is the target and the selection is pure CSS. */
.card--breakdown-tiers {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.1875rem;
  border-radius: 999px;
  background: var(--wash);
}

.card--breakdown-tier {
  padding: 0.3125rem 0.75rem;
  border-radius: 999px;
  color: var(--muted);
  font-family: var(--display);
  font-size: 0.8125rem;
  font-weight: 680;
  line-height: 1;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

.card--breakdown-tier-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.card--breakdown-tier:hover {
  color: var(--ink);
}

.card--breakdown-tier:has(.card--breakdown-tier-input:checked) {
  background: var(--accent);
  color: white;
}

.card--breakdown-tier:has(.card--breakdown-tier-input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.card--breakdown-field {
  display: flex;
  align-items: baseline;
  gap: 0.25rem;
}

.card--breakdown-field .form-control {
  width: 5.5rem;
  text-align: end;
}

.card--breakdown-suffix {
  color: var(--muted);
  font-weight: 600;
}

/* On the reveal each row grows two more columns: what was said, what was
   true, and the mark that settles it. */
.card--breakdown-row.is-hit,
.card--breakdown-row.is-miss {
  grid-template-columns: minmax(0, 1fr) auto auto 1.25rem;
}

.card--breakdown-given {
  font-variant-numeric: tabular-nums;
}

.card--breakdown-expected {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.card--breakdown-blank {
  color: var(--muted);
  font-style: italic;
}

.card--breakdown-mark {
  text-align: center;
  font-weight: 700;
}

/* Skipping is saying the words: a quiet text button, not an icon. */
.card--idk {
  background: none;
  border: none;
  padding: 0.45rem 0;
  font-family: var(--display);
  font-weight: 600;
  color: var(--muted);
  text-decoration: underline dotted;
  text-underline-offset: 0.25em;
}

.card--idk:hover {
  color: var(--ink);
}

/* The grading scale is a filling circle (○ ◐ ●): a material question, not a
   traffic light. Shape carries the meaning; the accent carries the brand. */
.card--review-buttons {
  form {
    flex-grow: 1;
  }

  .btn {
    --bs-btn-border-radius: 1.25rem;
    width: 100%;
    min-height: 5.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: var(--card);
    border: 1.5px solid var(--line);
    transition: border-color 0.2s, background-color 0.2s;
  }

  .btn:hover,
  .btn:focus-visible {
    background-color: var(--wash);
    border-color: var(--accent);
  }

  .grade-glyph {
    font-size: 2.8rem;
    line-height: 1;
    color: var(--accent);
    transition: transform 0.2s;
  }

  .btn:hover .grade-glyph,
  .btn:focus-visible .grade-glyph {
    transform: scale(1.12);
  }

}

/* Loading dots: the maturity dots take turns filling while something is
   worth waiting for (shared/_loading_dots). Drawn geometry rather than
   text glyphs, so the trio renders the same in every font stack. The
   Bootstrap .spinner-border remains the right wait for tight corners. */
.loading-dots span {
  display: inline-block;
  width: 1.2rem;
  height: 1.2rem;
  margin: 0 0.25rem;
  border: 2.5px solid var(--accent);
  border-radius: 50%;
  animation: grading-pulse 1.2s ease-in-out infinite;
}

.loading-dots .curing {
  background: linear-gradient(90deg, var(--accent) 50%, transparent 50%);
}

.loading-dots .stone {
  background: var(--accent);
}

.loading-dots span:nth-child(2) {
  animation-delay: 0.2s;
}

.loading-dots span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes grading-pulse {
  0%, 100% {
    opacity: 0.25;
    transform: translateY(0);
  }

  50% {
    opacity: 1;
    transform: translateY(-0.2rem);
  }
}

/* Grading wait: the dots plus a rotating message, centered in the card */
.card--grading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 1rem 0;
}

.card--grading-message {
  color: var(--accent);
  font-family: var(--display);
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  .card--review-buttons .grade-glyph {
    transition: none;
  }

  .card--review-buttons .btn:hover .grade-glyph,
  .card--review-buttons .btn:focus-visible .grade-glyph {
    transform: none;
  }

  .loading-dots span {
    animation: none;
  }

}

/* Maturity state rendered as a glyph (history lists, summaries); the
   grading-scale glyphs wear plain accent, with no data-maturity to read */
.maturity-glyph {
  color: var(--maturity, var(--accent));
}

.hand--hotkey-hints {
  color: var(--bs-secondary-color);
  font-size: 0.8rem;
}

.hand--hotkey-hints kbd {
  font-size: 0.8rem;
  color: var(--accent-deep);
}

/* A button wears its key, drawn from the attribute the hotkeys controller
   reads, so the legend cannot drift from the binding; a button Enter
   presses wears the return. */
button[data-hotkey],
button[data-hotkey-quick] {
  position: relative;
}

button[data-hotkey]::after,
button[data-hotkey-quick]::after {
  content: attr(data-hotkey);
  position: absolute;
  top: 0.5rem;
  right: 0.625rem;
  min-width: 1.25rem;
  padding: 0 0.3rem;
  border: 1px solid var(--line);
  border-radius: 0.375rem;
  background: var(--card);
  color: var(--muted);
  font-family: var(--bs-font-monospace);
  font-size: 0.6875rem;
  font-weight: 650;
  line-height: 1.25rem;
  text-align: center;
}

button[data-hotkey-quick]:not([data-hotkey])::after {
  content: "↵";
}

button[data-hotkey="backspace"]::after {
  content: "⌫";
}

/* A receipt's way back wears its key inline, after the word */
.toast-action[data-hotkey] {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.toast-action[data-hotkey]::after {
  position: static;
}

.choice-btn[data-hotkey]::after {
  top: -0.5rem;
  right: -0.625rem;
}

/* Choice card: stacked option rows, label on the left, confidence picks on
   the right. Picking an answer and rating sureness is one gesture. */
.choice-options {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.choice-option {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  border: 1px solid var(--line);
  border-radius: 0.8rem;
  padding: 0.7rem 0.9rem;
  transition: border-color 0.2s, background-color 0.2s;
}

.choice-option-label {
  font-family: var(--display);
  font-weight: 650;
  font-size: 1.05rem;
  flex: 1;
}

/* The row's letter, the key that picks it */
.choice-key {
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: 0.375rem;
  background: var(--wash);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 650;
}

.choice-sibling-reading {
  color: var(--muted);
  font-family: var(--display);
  font-weight: 600;
  font-size: 0.9rem;
}

.choice-mark {
  font-family: var(--display);
  font-weight: 700;
  /* As tall as the confidence buttons it stands in for, so a revealed row
     keeps the height of the row that was picked */
  height: 2.6rem;
  display: inline-grid;
  place-items: center;
}

.choice-confidence {
  display: flex;
  gap: 0.4rem;
}

.choice-btn {
  width: 2.6rem;
  height: 2.6rem;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: none;
  display: grid;
  place-items: center;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--accent);
  cursor: pointer;
  transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1),
              border-color 0.2s,
              background-color 0.2s;
}

.choice-btn:hover {
  border-color: var(--accent);
  background-color: var(--wash);
  transform: scale(1.12);
}

/* Revealed choice states: the verdict wears moss when correct and rose when
   wrong, stated once for option rows and map pins alike */
.choice-option--correct,
.choice-map-pin--correct {
  --verdict: var(--moss);
  --verdict-wash: var(--moss-wash);
}

.choice-option--wrong,
.choice-map-pin--wrong {
  --verdict: var(--rose);
  --verdict-wash: var(--rose-wash);
}

.choice-option--correct,
.choice-option--wrong {
  border-color: var(--verdict);
  background-color: var(--verdict-wash);
}

.choice-option--correct {
  animation: choiceCorrectIn 0.4s ease-out;
}

.choice-option--correct .choice-mark,
.choice-option--wrong .choice-mark {
  color: var(--verdict);
}

.choice-option--shake {
  animation: choiceShake 0.4s ease-out;
}

.choice-note {
  animation: choiceNoteIn 0.4s ease-out;
}

@keyframes choiceNoteIn {
  0% { opacity: 0; transform: translateY(0.4rem); }
  100% { opacity: 1; transform: translateY(0); }
}

@keyframes choiceCorrectIn {
  0% { transform: scale(1); }
  50% { transform: scale(1.03); }
  100% { transform: scale(1); }
}

@keyframes choiceShake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

/* Sequence boards: the vertical stack a puzzle deals for dragging into order */
.sequence-stack {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.sequence-row {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  border: 1px solid var(--line);
  border-radius: 0.8rem;
  padding: 0.7rem 0.9rem;
  background-color: var(--bs-body-bg);
  cursor: grab;
  touch-action: none;
  user-select: none;
  transition: transform 0.15s ease;
}

.sequence-row--dragging {
  cursor: grabbing;
  border-color: var(--bs-primary);
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  position: relative;
  z-index: 1;
  transition: none;
}

/* The drop's own frame: transforms come off as the DOM reorders, no sliding */
.sequence-stack--settling .sequence-row {
  transition: none;
}

.sequence-grip {
  color: var(--bs-secondary-color);
}

.sequence-label {
  font-family: var(--display);
  font-weight: 650;
  font-size: 1.05rem;
  flex: 1;
}

.sequence-arrows {
  display: flex;
  gap: 0.25rem;
}

.sequence-arrow {
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: none;
  color: var(--bs-body-color);
  width: 2rem;
  height: 2rem;
  line-height: 1;
}

.sequence-arrow:hover {
  border-color: var(--bs-primary);
}

.sequence-value {
  color: var(--bs-secondary-color);
  white-space: nowrap;
}

/* The daylight between two revealed rows: quiet and indented past the row
   borders, so it reads as the space between rather than a row of its own */
.sequence-gap {
  color: var(--bs-secondary-color);
  font-size: 0.8rem;
  padding-left: 0.9rem;
  margin: -0.15rem 0;
}

/* The reveal: the true order, rows the user misplaced marked in rose */
.sequence-stack--revealed .sequence-row {
  cursor: default;
  --verdict: var(--moss);
  --verdict-wash: var(--moss-wash);
  border-color: var(--verdict);
  background-color: var(--verdict-wash);
}

.sequence-stack--revealed .sequence-row--misplaced {
  --verdict: var(--rose);
  --verdict-wash: var(--rose-wash);
}

/* Choice map pins */
[data-controller="choice-map"] {
  width: 100%;
}

.choice-map-pin {
  cursor: pointer;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1rem;
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 2px solid color-mix(in srgb, var(--bs-emphasis-color) 60%, transparent);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.choice-map-pin--selected {
  border-color: var(--accent-deep);
  background-color: var(--accent);
  color: #fff;
}

.choice-option--selected {
  border-color: var(--accent);
  background-color: var(--wash);
}

.choice-map-pin--correct,
.choice-map-pin--wrong {
  border-color: var(--verdict);
  background-color: var(--verdict);
  color: #fff;
}

/* Map card */
.map-card:focus,
.map-card .leaflet-container:focus {
  outline: none;
}

.map-card .leaflet-container {
  z-index: 1;
}

/* Country cards: the tile-free world where countries are the interface.
   Everything derives from theme tokens, so light, dark, and every accent
   hue follow without map-specific overrides. */
.world-map-frame {
  position: relative;
}

.world-map {
  display: block;
  width: 100%;
  height: 100%;
  background: color-mix(in srgb, var(--sea) 14%, var(--paper));
  cursor: pointer;
  touch-action: none;
  outline: none;
}

.world-map--dragging {
  cursor: grabbing;
}

.world-map .country {
  fill: var(--card);
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transition: fill 0.15s ease;
}

@media (prefers-reduced-motion: reduce) {
  .world-map .country {
    transition: none;
  }
}

/* Which countries a choice card offers */
.world-map .country--option {
  fill: color-mix(in srgb, var(--accent) 12%, var(--card));
  stroke: var(--accent);
}

.world-map .country--hover {
  fill: color-mix(in srgb, var(--accent) 30%, var(--card));
}

.world-map .country--selected,
.world-map .country--subject {
  fill: var(--accent);
}

/* The neighbor halo an unsure claim would cover, previewed and settled */
.world-map .country--halo-preview {
  fill: color-mix(in srgb, var(--accent) 15%, var(--card));
  stroke: var(--accent);
  stroke-dasharray: 5 4;
}

.world-map .country--correct {
  fill: var(--moss);
}

.world-map .country--wrong {
  fill: var(--rose);
}

.world-map .country--held {
  fill: color-mix(in srgb, var(--moss) 35%, var(--card));
  stroke: var(--moss);
  stroke-dasharray: 5 4;
}

.world-map .country--halo-held {
  fill: color-mix(in srgb, var(--moss) 15%, var(--card));
  stroke: var(--moss);
  stroke-dasharray: 5 4;
}

.world-map .country--halo-missed {
  fill: color-mix(in srgb, var(--rose) 12%, var(--card));
  stroke: var(--rose);
  stroke-dasharray: 5 4;
}

.world-map-label,
.world-map-letter {
  font-family: var(--display);
  font-weight: 700;
  fill: var(--ink);
  paint-order: stroke;
  stroke: var(--card);
  stroke-width: 0.22em;
  stroke-linejoin: round;
  text-anchor: middle;
  pointer-events: none;
}

.world-map-zoom {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  z-index: 1;
}

.world-map-zoom button {
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--card);
  color: var(--ink);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
}

.world-map-zoom button:hover {
  background: var(--wash);
}

.world-map-zoom button:focus-visible {
  outline: 2px solid var(--accent);
}

/* Timeline card */
.timeline-card:focus {
  outline: none;
}

.timeline-card svg {
  display: block;
  width: 100%;
}

.timeline-area {
  height: 140px;
  cursor: crosshair;
  position: relative;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--bs-emphasis-color) 1%, transparent) 0%,
    color-mix(in srgb, var(--bs-emphasis-color) 2.5%, transparent) 100%
  );
  transition: background 0.3s;
}

.timeline-area:hover {
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--bs-emphasis-color) 1.5%, transparent) 0%,
    color-mix(in srgb, var(--bs-emphasis-color) 3.5%, transparent) 100%
  );
}

.card--revealed .timeline-area {
  cursor: default;
}

/* Marker entrance animation */
.timeline-marker-enter {
  animation: timelineMarkerIn 0.3s ease-out;
}

@keyframes timelineMarkerIn {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Marker glow pulse on placement */
.timeline-marker-glow {
  animation: timelineGlowPulse 0.6s ease-out;
}

@keyframes timelineGlowPulse {
  0% {
    r: 5;
    opacity: 1;
  }
  100% {
    r: 14;
    opacity: 0;
  }
}

/* Error zone fade in */
.timeline-error-enter {
  animation: timelineErrorIn 0.4s ease-out;
}

@keyframes timelineErrorIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Range band entrance */
.timeline-band-enter {
  animation: timelineBandIn 0.35s ease-out;
}

@keyframes timelineBandIn {
  from {
    opacity: 0;
    transform: scaleX(0.8);
  }
  to {
    opacity: 1;
    transform: scaleX(1);
  }
}

/* Result feedback styling */
.timeline-result-exact {
  color: #16a34a;
  font-weight: 700;
}

.timeline-result-good {
  color: #16a34a;
}

.timeline-result-ok {
  color: #d97706;
}

.timeline-result-miss {
  color: #dc2626;
}

.form-floating {
  /* move ts-control input element below the floating label */
  .ts-wrapper.form-control,
  .ts-wrapper.form-select {
    height: 0 !important;
  }

  .ts-wrapper.form-control .ts-control,
  .ts-wrapper.form-select .ts-control {
    padding-top: 1.625rem;
    padding-bottom: 0.625rem;
  }

  /* if field is styled as a text field (.form-control instead of .form-select)
   * make sure we apply the same animation as Bootstrap 5 */
  .ts-wrapper.form-control ~ label,
  .ts-wrapper.form-select ~ label {
    transform: scale(1) translateY(0) translateX(0);
    color: var(--ink);
  }

  .ts-wrapper.form-control.focus ~ label,
  .ts-wrapper.form-control.full ~ label,
  .ts-wrapper.form-select.focus ~ label,
  .ts-wrapper.form-select.full ~ label,
  .ts-wrapper.form-select.has-items ~ label {
    transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
    color: color-mix(in srgb, var(--ink) 65%, transparent);
  }

  /* hides empty value which tomselect uses as placeholder */
  .ts-wrapper.form-select:not(.focus) .ts-control ::placeholder {
    opacity: 0;
  }
}

/* The dropdown's search input autofocuses on open, so Bootstrap's blue focus
   ring flashes every time a select is opened; keep the resting border instead */
.plugin-dropdown_input.focus .ts-dropdown .dropdown-input {
  border-color: #d0d0d0;
  box-shadow: none;
}

/* Filled floating textareas: Bootstrap tucks the first line right under the
   floated label and the last line against the bottom edge; give both some air. */
.form-floating > textarea.form-control:focus,
.form-floating > textarea.form-control:not(:placeholder-shown) {
  padding-top: 1.875rem;
  padding-bottom: 0.875rem;
}

/* Opt-in: textareas that grow to fit their content. Inside .form-floating,
   Bootstrap's fixed height must yield; its min-height still sets the floor. */
textarea.form-control.autogrow {
  field-sizing: content;
  overflow: hidden;
  resize: none;
}

.form-floating > textarea.form-control.autogrow {
  height: auto;
}

/* The sift: the rest of the bucket peeks out beneath the top card.
   The pseudo-layers escape behind the card (no stacking context on it),
   so only their bottom edges show. */
.sift--stack::before,
.sift--stack::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--bs-card-bg);
  border: var(--bs-card-border-width) solid var(--bs-card-border-color);
  border-radius: var(--bs-card-border-radius);
}

.sift--stack::before {
  transform: translateY(2.25rem) scale(0.85);
}

.sift--stack::after {
  transform: translateY(1rem) scale(0.95);
}

.sift-progress {
  height: 0.4rem;
}

/* The sift's way back, above the card: previous and next at the ends, the
   answer on record for a card already sorted between them. The row holds
   its height so the card never jumps as the ends come and go. */
.sift-flip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  min-height: 2.75rem;
  margin-bottom: 0.25rem;
}

.sift-flip > .btn {
  flex: none;
  padding-inline: 0;
  text-decoration: none;
}

.sift-flip-filed {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
  text-align: center;
}

.sift-flip-filed .btn {
  padding-block: 0;
}

/* A settled card's buttons rest inside a row that takes the hover, the
   focus, and the tap for them, so the tooltip has something to hang on. */
.sift-settled {
  cursor: not-allowed;
}

.sift-settled .btn:disabled {
  pointer-events: none;
}

/* The answer on record, pressed on a card the reader flipped back to */
.sift--stack .btn-outline-secondary.active {
  background: var(--bs-primary-bg-subtle);
  border-color: var(--bs-primary);
  color: var(--bs-primary-text-emphasis);
}

/* The sift's keyboard hints, for the desk. The markup hides the row below the
   md breakpoint, where there is no keyboard to hint at. */
.sift-deck-keyhint {
  margin-top: 3rem;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
  flex-wrap: wrap;
  gap: 0.15rem 0.7rem;
}
