html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

html {
  position: relative;
  min-height: 100%;
  overflow-x: hidden;
}

body {
  margin-bottom: 60px;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}

.navbar-nav .nav-link.active {
  font-weight: 600;
}

/* Class/talent filter checkbox grids - keep the long lists of class toggles
   aligned in tidy columns instead of a ragged flex wrap. Used by the Class
   Editor, Set Editor, and the Settings "Collection Focus" panel. */
.class-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 0.25rem 0.75rem;
}

/* Collection Focus hero tree (CardFocus.renderTree in site.js) - Settings + Class Editor.
   Margin-left clears past the parent row's own chevron + checkbox so nested heroes read as
   clearly indented under the class label, not just barely right of the group's own checkbox. */
.focus-tree-list {
  margin: 0.15rem 0 0.35rem 2rem;
  padding-left: 0.75rem;
  border-left: 2px solid var(--bs-border-color);
}

.focus-tree-list .form-check {
  padding: 0.2rem 0.4rem 0.2rem 1.5rem;
  border-radius: 4px;
}

.focus-tree-list .form-check:nth-child(odd) {
  background-color: rgba(127, 127, 127, 0.1);
}

/* Binder Collection section list (BinderCollection.cshtml) - a class/talent tree, not a plain
   Bootstrap .list-group, since rows aren't its direct children (each root row wraps its own
   optional child list). Bootstrap's .list-group-item.active/hover colors are CSS custom properties
   that only ever get defined on an actual .list-group ancestor (see bootstrap.css ~5015) - this
   tree has none, so without these the active row's background silently fell back to transparent
   and the selected section was identifiable only by its badge. Custom properties inherit down the
   DOM regardless of class, so declaring them here reaches every .list-group-item nested below. */
.binder-section-tree {
  --bs-list-group-bg: transparent;
  --bs-list-group-border-color: transparent;
  --bs-list-group-action-hover-bg: var(--bs-tertiary-bg);
  --bs-list-group-action-hover-color: var(--bs-emphasis-color);
  --bs-list-group-action-active-bg: var(--bs-secondary-bg);
  --bs-list-group-active-bg: var(--bs-primary, #0d6efd);
  --bs-list-group-active-color: #fff;
  --bs-list-group-active-border-color: var(--bs-primary, #0d6efd);
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background-color: var(--bs-body-bg);
  padding: 0.5rem;
}

/* Every root - a class, standalone talent, or category - is its own bordered, rounded box, so the
   list reads as a stack of cards rather than a dense unbroken list; the boxes also give the active
   row's fill something to sit inside instead of bleeding edge-to-edge. */
.binder-root-row {
  border: 1px solid var(--bs-border-color);
  border-radius: 0.6rem;
  background-color: var(--bs-tertiary-bg, rgba(127, 127, 127, 0.05));
  overflow: hidden;
  /* .binder-section-tree is a column flex container with a JS-computed max-height + overflow-y:auto
     (see fitGrid). A flex item's automatic min-height is its min-content size UNLESS its own
     overflow isn't visible - ours is hidden (needed to clip children's corners to this box's own
     radius) - which drops that floor to 0. Without flex-shrink:0, once total content exceeds the
     container's max-height the browser shrinks flex items to fit rather than scrolling, and
     silently clips whichever boxes have the most content (an expanded root with many children)
     instead of respecting their real height. This is what made Illusionist/Assassin's later
     children vanish while short leaf rows looked fine - they had little height to shrink from. */
  flex-shrink: 0;
}

.binder-root-row > .d-flex {
  padding: 0.2rem 0.5rem;
  gap: 0.4rem;
}

.binder-root-row .list-group-item {
  border: none !important;
  border-radius: 0.4rem !important;
  /* A flex item's default min-width is its content's width, not 0 - without this override, a
     root row's button refuses to shrink narrower than "Lightning Illusionist" and forces the
     label to wrap instead of the ellipsis truncation below ever kicking in. */
  min-width: 0;
}

/* A root row's own label (leaf or expandable) reads bolder than its children, so the hierarchy is
   visible even before anything is expanded. Padding here is on top of the row's own 0.2rem, not
   stacked with an equal amount - collapsed rows were carrying double vertical padding (once on
   the flex row, once on the button inside it) and reading much taller than the boxed-child rows
   below them. */
.binder-root-row > .d-flex > .list-group-item {
  font-weight: 600;
  padding: 0.3rem 0.65rem;
}

/* The drag handle only reorders root rows (root classes/talents), never the talent variants
   nested under them - matches "root classes and talents should be drag & drop reorderable".
   Dim by default so the row reads primarily as a label, not a row of controls; a full-opacity
   hover/focus makes it discoverable without a hint. */
.binder-drag-handle {
  cursor: grab;
  width: 1.25rem;
  flex: 0 0 auto;
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bs-secondary-color);
  opacity: 0.5;
}

.binder-drag-handle:hover,
.binder-drag-handle:focus-visible {
  opacity: 1;
}

.binder-drag-handle:active {
  cursor: grabbing;
}

.binder-root-row.dragging {
  opacity: 0.5;
}

/* Talent + Hero/Equip variants nested under a root - each its own small box too (a shade lighter
   than the root's, on the root's own background rather than the page's), indented past the
   parent's drag handle + chevron so the nesting is obvious without needing a connecting rule. */
.binder-section-children {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin: 0 0.5rem 0.5rem 2.4rem;
}

.binder-section-children .list-group-item {
  border: 1px solid var(--bs-border-color) !important;
  border-radius: 0.5rem !important;
  font-size: 0.88rem;
  padding: 0.4rem 0.65rem;
}

/* :not(.active) matters here: this selector has the same specificity as Bootstrap's own
   .list-group-item.active (bootstrap.css ~5087) and site.css loads after bootstrap.css, so
   without it this background-color would win the tie by source order and silently overwrite the
   active row's highlight with the plain child-box background - which is exactly what happened. */
.binder-section-children .list-group-item:not(.active) {
  background-color: var(--bs-body-bg);
}

/* The root box containing the current selection gets a faint tint of its own - distinct from every
   other collapsed-looking root, but clearly a step below the selected row's own solid fill so the
   two read as "this group holds it" vs. "this is it". */
.binder-root-row.binder-root-active {
  background-color: rgba(13, 110, 253, 0.12);
  border-color: rgba(13, 110, 253, 0.35);
}

.binder-row-label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  /* Same min-width:0 story as the button above, one level in: this is the flex item that
     actually needs to give up width so binder-row-label-text can ellipsis instead of wrap. */
  flex: 1 1 auto;
  min-width: 0;
}

/* The name itself - the only part of a row allowed to lose width. Truncates to "Lightning
   Illus…" rather than wrapping to a second line, which used to push the badge cluster below
   the label and stagger row heights on narrower screens. */
.binder-row-label-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* Signals pending (not-yet-synced) cards in the row without costing it real width - the "+N"
   badge this replaced pushed labels down to one or two letters at nearly every real sidebar
   width (see .binder-section-tree above). A plain 8px dot carries no count of its own; the exact
   number is in its title tooltip on hover/tap. */
.binder-row-pending-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background-color: var(--bs-success, #198754);
}

.list-group-item.active .binder-row-pending-dot {
  background-color: #fff;
}

/* Was a "Hero/Equip" text pill (~75px) - on a narrow child row that alone outweighed the label,
   forcing it to truncate to almost nothing. An icon carries the same information (the full word
   is still in its title tooltip) in a fixed ~22px circle, matching the row's other icon-only
   controls (drag handle, chevron) instead of competing with the label as more text. */
.binder-family-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  padding: 0;
  font-size: 0.7rem;
  color: var(--bs-secondary-color);
  background-color: var(--bs-secondary-bg, rgba(127, 127, 127, 0.18));
  /* Fixed-size tag, not text - never shrinks or wraps, even as the label next to it truncates. */
  flex-shrink: 0;
}

.list-group-item.active .binder-family-pill {
  color: inherit;
  background-color: rgba(255, 255, 255, 0.25);
}

/* Settings → Excluded sets. Over a hundred sets, so they're split into one panel per
   product type (SetGroups.groupSets); the header carries the group's excluded count and
   its All/None bulk actions. */
.excset-group {
  border: 1px solid var(--bs-border-color);
  border-radius: 0.375rem;
  margin-bottom: 0.75rem;
}

.excset-group:last-child {
  margin-bottom: 0;
}

.excset-group-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.75rem;
  background-color: rgba(127, 127, 127, 0.08);
  border-bottom: 1px solid var(--bs-border-color);
  border-radius: 0.375rem 0.375rem 0 0;
}

/* Count sits with the group name; the bulk actions are pushed to the far edge. */
.excset-bulk {
  flex: 0 0 auto;
  margin-left: auto;
  white-space: nowrap;
}

.excset-group-body {
  padding: 0.5rem 0.75rem;
}

/* Settings panels fold from their header (see js/settings-panels.js). The whole header is the
   hit target, so it takes a pointer and stops text selection dragging into a toggle; the
   chevron turns to point at the panel it would open. Buttons living in the header (info
   popovers, "New family") opt out of the toggle in JS. */
.settings-panel-header {
  cursor: pointer;
  user-select: none;
}

.settings-panel-chevron {
  flex: 0 0 auto;
  transition: transform 0.15s ease-in-out;
}

.settings-panel-header.collapsed .settings-panel-chevron {
  transform: rotate(-90deg);
}

/* A boxed block of related switches inside a settings panel - used where a toggle only makes
   sense read alongside its neighbours (Binder → Grouping). Purely visual; unlike
   .settings-panel these don't fold. */
.settings-subgroup {
  border: 1px solid var(--bs-border-color);
  border-radius: 0.375rem;
}

.settings-subgroup-head {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.4rem 0.75rem;
  background-color: rgba(127, 127, 127, 0.08);
  border-bottom: 1px solid var(--bs-border-color);
  border-radius: 0.375rem 0.375rem 0 0;
}

.settings-subgroup-body {
  padding: 0.75rem;
}

/* Mobile (< sm) expanded nav menu.
   The container uses flex-nowrap to keep the top row (brand + search + theme +
   hamburger) on a single line, but that also stops Bootstrap from wrapping the
   expanded .navbar-collapse onto its own line, so it gets squeezed onto the top
   row and overlaps the header. Pull it out of the flex flow and render it as a
   full-width panel anchored below the navbar instead. */
@media (max-width: 575.98px) {
  .navbar .container-fluid {
    position: relative;
  }

  .navbar .navbar-collapse {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1050;
    padding: 0.5rem 1rem 0.75rem;
    background-color: var(--bs-body-bg);
    border-bottom: 1px solid var(--bs-border-color);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175);
  }

  .navbar .navbar-collapse .navbar-nav .nav-link {
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
  }

  /* Keep the user (Settings/Logout) dropdown within the panel rather than
     floating off to the side. */
  .navbar .navbar-collapse .navbar-nav .dropdown-menu {
    position: static;
    float: none;
    width: 100%;
    margin-top: 0.25rem;
    box-shadow: none;
  }
}
/* "What's new" popup - the changelog markdown rendered into a modal. The Changelog page's own
   heading scale is set by Bootstrap's h2/h3 and is far too heavy inside a dialog, where the
   version heading sits directly under the modal title, so the whole block is stepped down and
   the version headings get a rule to separate one release from the next. */
.release-notes h2 {
  font-size: 1.05rem;
  font-weight: 600;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--bs-border-color);
}

.release-notes h2:not(:first-child) {
  margin-top: 1.75rem;
}

.release-notes h3 {
  font-size: 0.9rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--bs-secondary-color);
  margin-top: 1rem;
}

.release-notes ul {
  padding-left: 1.1rem;
  margin-bottom: 0;
}

.release-notes li {
  margin-bottom: 0.4rem;
}

/* Orienting a card played horizontally. CardImages.orient picks one of these off the loaded image's
   own shape - see the comment there for the rule they serve. Each frame lays its image out in its
   transposed box (width = the frame's height, height = its width) and turns it counter-clockwise
   into it, so the rotated result is exactly frame-sized; the two are the same trick inverted. The
   binder pocket has its own copy of `.card-as-filed`, since a pocket is sized by fitGrid. */

/* Read a portrait file - a Landmark, which arrives as the card turned clockwise. */
.card-as-read {
  position: relative;
  aspect-ratio: 88 / 63;
  overflow: hidden;
}

.card-as-read > img {
  width: calc(100% * 63 / 88);
  height: calc(100% * 88 / 63);
}

/* File a landscape file - a meld card, which arrives as the card is read. */
.card-as-filed {
  position: relative;
  aspect-ratio: 63 / 88;
  overflow: hidden;
}

.card-as-filed > img {
  width: calc(100% * 88 / 63);
  height: calc(100% * 63 / 88);
}

.card-as-read > img,
.card-as-filed > img {
  position: absolute;
  top: 50%;
  left: 50%;
  max-width: none;    /* the frame governs the size, not the image's own constraints */
  max-height: none;
  transform: translate(-50%, -50%) rotate(-90deg);
}

/* Filing a portrait file needs no frame: the file and the slot are already the same shape, so it is
   a half turn in place - which leaves it counter-clockwise, like a turned meld card beside it. */
.card-turned {
  transform: rotate(180deg);
}
