/* Site-wide design system derived from duel/duel.css.
   Tokens are set in :root (Gunfighter parchment) and overridden per CONTEXT
   by an inline <style id="site-theme-vars"> emitted from context.php.
   Live duel screens (.duel-page) keep their own scoped rules. */

:root {
  --site-ink: #2a1a0a;
  --site-muted: #4a3218;
  --site-panel: rgba(221, 207, 188, 0.88);
  --site-panel-solid: #e8dcc8;
  --site-border: #6b3e1f;
  --site-gold: #8b5a2b;
  --site-rust: #7a3b2e;
  --site-shadow: 0 4px 18px rgba(60, 30, 10, 0.35);
  --site-inset: inset 0 0 0 1px rgba(120, 70, 30, 0.35);
  --site-btn-top: #f0e0c0;
  --site-btn-bot: #d4b88a;
  --site-btn-hover-top: #ffe8b8;
  --site-btn-hover-bot: #e0c090;
  --site-th: rgba(90, 55, 25, 0.12);
  --site-row: rgba(255, 250, 235, 0.4);
  --site-input-bg: rgba(255, 250, 235, 0.65);
  --site-highlight: rgba(255, 248, 230, 0.55);
  --site-radius: 6px;
}

/* ---------- Page chrome ---------- */

body.site-page {
  box-sizing: border-box;
  color: var(--site-ink);
}

/* gf.css makes html and body at least one viewport tall (a column), and
   the shared footer pins itself with margin-top: auto plus 16px under
   the copyright table. That 16px is inside the flex box. The user-agent
   body margin (8px, top and bottom) is not, so a short page is 16px
   taller than the window. Zeroing the margin lets a short page end at
   the viewport. Duel pages are not selected; they clip this footer
   themselves. Narrow screens keep an inset as padding in the 45rem rule. */
body:not(.duel-page) {
  box-sizing: border-box;
  margin: 0;
}

/* MatchMaker must not be the gf.css flex column. That column plus
   footer margin-top:auto collapses the panels to an empty border
   inside the Discord Activity iframe and pins the footer underneath. */
body.mm-page,
body.pbem-page,
body.challenge-search {
  min-height: 0;
  height: auto;
  display: block;
  overflow: visible;
}

body.challenge-search .mm-stack,
body.challenge-search table.site-panel,
body.challenge-search table.private-opponents {
  width: min(100%, max(80%, 720px));
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
}

body.challenge-search table.private-opponents {
  table-layout: fixed;
  margin-top: 8px;
}

body.challenge-search table.private-opponents th,
body.challenge-search table.private-opponents td {
  min-width: 0;
  white-space: normal;
  overflow-wrap: normal;
  word-break: normal;
}

body.challenge-search table.private-opponents col.private-opponents-name { width: 36%; }
body.challenge-search table.private-opponents col.private-opponents-level { width: 16%; }
body.challenge-search table.private-opponents col.private-opponents-path { width: 26%; }
body.challenge-search table.private-opponents col.private-opponents-act { width: 22%; }

body.challenge-search #livesearch {
  display: block;
  width: 100%;
  max-width: 100%;
  max-height: 240px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  margin-top: 4px;
  background: var(--site-panel-solid);
  position: relative;
  z-index: 4;
}

body.challenge-search #livesearch:empty {
  display: none;
  margin: 0;
  border: 0;
}

body.challenge-search #livesearch a {
  display: block;
  padding: 8px 6px;
  text-decoration: none;
}

body.challenge-search #livesearch a.gf-search-on,
body.challenge-search #livesearch a:hover {
  background: var(--site-highlight);
}

body.challenge-search #livesearch .gf-search-more {
  margin: 0;
  padding: 8px 6px;
  font-style: italic;
}

body.site-page *,
body.site-page *::before,
body.site-page *::after {
  box-sizing: border-box;
}

body:not(.duel-page) a {
  color: var(--site-gold);
}

body:not(.duel-page) a:hover {
  color: var(--site-rust);
}

/* ---------- Panels / tables ---------- */

body:not(.duel-page) table {
  border-collapse: separate;
  border-spacing: 0;
  border: 2px solid var(--site-border);
  border-radius: var(--site-radius);
  background-color: var(--site-panel);
  box-shadow: var(--site-shadow), var(--site-inset);
}

body:not(.duel-page) table table {
  box-shadow: none;
  background-color: transparent;
  border-radius: 4px;
}

body:not(.duel-page) th,
body:not(.duel-page) td {
  border-color: var(--site-border);
  color: inherit;
}

body:not(.duel-page) th {
  background: var(--site-th);
  font-weight: bold;
  text-shadow: 0 1px 0 rgba(255, 245, 220, 0.45);
}

body:not(.duel-page) tr.spacerRow td,
body:not(.duel-page) .spacerRow td {
  background: transparent;
  border-left: none;
  border-right: none;
  box-shadow: none;
}

body:not(.duel-page) .table-responsive {
  max-width: 100%;
  overflow-x: auto;
}

/* Explicit card hook for pages that want a named panel */
.site-panel {
  margin: 12px auto;
  padding: 10px 12px 14px;
  border: 2px solid var(--site-border);
  border-radius: var(--site-radius);
  background-color: var(--site-panel);
  box-shadow: var(--site-shadow), var(--site-inset);
  overflow: hidden;
}

/* Accepted-duel panel on create.php and join.php. Stays the small
   upper-left box. A flex column would otherwise stretch a table whose
   percentage width cannot resolve, and the phone path rules below
   would stretch it across the Activity iframe. */
body:not(.duel-page) table.duel-accepted {
  align-self: flex-start;
  flex: 0 0 auto;
  width: 10%;
  margin-left: 20px;
  margin-right: auto;
}

.site-header {
  padding: 6px 8px;
  background: var(--site-th);
  border-bottom: 1px solid rgba(107, 62, 31, 0.3);
}

.site-callout {
  padding: 6px 10px;
  background: var(--site-highlight);
  border: 1px solid rgba(107, 62, 31, 0.25);
  border-radius: 3px;
}

/* ---------- Buttons ---------- */

body:not(.duel-page) button,
body:not(.duel-page) input[type="submit"],
body:not(.duel-page) input[type="button"],
body:not(.duel-page) a.site-btn,
body:not(.duel-page) a.site-btn-nav,
.site-btn,
.site-btn-nav,
.gf-home .gf-home-button,
.gf-tutorial .gf-tutorial-button,
.gf-home .gf-home-nav-links a,
.gf-home .gf-home-actions a,
.gf-tutorial .gf-tutorial-nav a,
.gf-tutorial .gf-tutorial-actions a,
.gf-tutorial .gf-tutorial-video-links a {
  display: inline-block;
  padding: 6px 14px;
  border: 2px solid var(--site-border);
  border-radius: 4px;
  background: linear-gradient(var(--site-btn-top), var(--site-btn-bot));
  color: var(--site-ink);
  font-weight: bold;
  text-decoration: none;
  cursor: pointer;
  line-height: 1.25;
  vertical-align: middle;
  text-align: center;
}

/* Compact parchment buttons for header return links and stacked table-cell actions. */
.site-btn-nav,
body:not(.duel-page) a.site-btn-nav {
  padding: 4px 10px;
  font-size: 0.95em;
}

body:not(.duel-page) button:hover,
body:not(.duel-page) input[type="submit"]:hover,
body:not(.duel-page) input[type="button"]:hover,
body:not(.duel-page) a.site-btn:hover,
body:not(.duel-page) a.site-btn-nav:hover,
.site-btn:hover,
.site-btn-nav:hover,
.gf-home .gf-home-button:hover,
.gf-tutorial .gf-tutorial-button:hover,
.gf-home .gf-home-nav-links a:hover,
.gf-home .gf-home-actions a:hover,
.gf-tutorial .gf-tutorial-nav a:hover,
.gf-tutorial .gf-tutorial-actions a:hover,
.gf-tutorial .gf-tutorial-video-links a:hover {
  background: linear-gradient(var(--site-btn-hover-top), var(--site-btn-hover-bot));
  text-decoration: none;
  color: var(--site-ink);
}

body:not(.duel-page) button:disabled,
body:not(.duel-page) button[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Jeb's Store price amounts. Every amount is bold. A price above the
   gunfighter's scrip is dark red (#8b0000); an affordable price keeps
   the normal ink color via inherit. These rules sit outside the phone
   and Activity width queries, so the wide web table and the narrow
   Discord Activity layout share them. Light-ink themes use a lighter
   red so the number still reads on a dark panel. */
body.hub-page b.price-amount {
  /* Copperplate has no bold face, so the amount uses Arial, which does. */
  font-family: Arial, Helvetica, sans-serif;
  font-weight: bold;
  color: inherit;
}

body.hub-page td.price-unaffordable {
  color: #8b0000;
}

/* Gray Buy reasons. The wrapper carries the tip because a disabled
   button does not fire hover. Tap and keyboard focus use the same tip.
   pointer-events: none lets the hover land on the wrapper. Right-aligned
   so the last column (Buy) grows the tip leftward inside a narrow
   Activity iframe. Card layout at 800px realigns it. */
body.hub-page .buy-why {
  position: relative;
  display: inline-block;
  max-width: 100%;
  vertical-align: middle;
  cursor: not-allowed;
}

body.hub-page .buy-why button:disabled,
body.hub-page .buy-why button[disabled] {
  pointer-events: none;
}

body.hub-page .buy-why-tip {
  display: none;
  position: absolute;
  z-index: 40;
  right: 0;
  bottom: calc(100% + 6px);
  width: max-content;
  max-width: min(16rem, 70vw);
  padding: 6px 8px;
  background: #2a1a0a;
  color: #fff8e8;
  border: 1px solid #6b3e1f;
  border-radius: 4px;
  font-size: 0.8rem;
  font-weight: normal;
  line-height: 1.35;
  text-align: left;
  white-space: normal;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(40, 20, 8, 0.35);
}

body.hub-page .buy-why-line {
  display: block;
}

body.hub-page .buy-why:hover,
body.hub-page .buy-why:focus {
  z-index: 40;
}

body.hub-page .buy-why:hover > .buy-why-tip,
body.hub-page .buy-why:focus > .buy-why-tip,
body.hub-page .buy-why.is-on > .buy-why-tip {
  display: block;
}

body.hub-page .buy-why:focus {
  outline: 2px solid var(--site-border);
  outline-offset: 2px;
}

body.hub-page.ctx-glitchwar td.price-unaffordable,
body.hub-page.ctx-twitwar td.price-unaffordable {
  color: #ff8a80;
}

/* Dashboard Min/Max chip. Must beat the parchment button rules, including
   the narrow-screen min-height, without becoming a full-width action. */
body.dash-page button.ui-mode-toggle,
body.dash-page button.ui-mode-toggle:hover,
body.dash-page button.ui-mode-toggle:focus {
  display: inline-block;
  width: auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: gray;
  color: white;
  font: inherit;
  font-weight: bold;
  line-height: inherit;
  vertical-align: baseline;
  box-shadow: none;
  cursor: pointer;
  white-space: nowrap;
}

body.dash-page button.ui-mode-toggle:focus {
  outline: 1px solid white;
  outline-offset: 1px;
}

form.ui-mode-form {
  margin: 0;
  padding: 0;
  border: 0;
  height: 0;
  overflow: hidden;
}

body:not(.duel-page) button:focus,
body:not(.duel-page) input[type="submit"]:focus,
body:not(.duel-page) a.site-btn:focus,
body:not(.duel-page) a.site-btn-nav:focus,
body:not(.duel-page) .site-btn:focus,
.gf-home .gf-home-button:focus,
.gf-tutorial .gf-tutorial-button:focus,
.gf-home .gf-home-nav-links a:focus,
.gf-home .gf-home-actions a:focus,
.gf-tutorial .gf-tutorial-nav a:focus,
.gf-tutorial .gf-tutorial-actions a:focus,
.gf-tutorial .gf-tutorial-video-links a:focus {
  outline: 2px solid var(--site-gold);
  outline-offset: 2px;
}

.site-btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
}

/* Sibling parchment controls in one row share height and type metrics.
   Mixed .site-btn / .site-btn-nav rows adopt the standard .site-btn size
   (padding, font-size, line-height). Nav-only rows stay compact. */
.site-btn-row:has(.site-btn) > .site-btn,
.site-btn-row:has(.site-btn) > .site-btn-nav,
.site-btn-row:has(.site-btn) > a,
.site-btn-row:has(.site-btn) > button,
.site-btn-row:has(.site-btn) > input[type="submit"],
.site-btn-row:has(.site-btn) > input[type="button"] {
  padding: 6px 14px;
  font-size: 1em;
  line-height: 1.25;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Sign in with Google, dark theme, per Google's branding guidelines:
   fill #131314, 1px inside stroke #8E918F, label #E3E3E3 in Roboto Medium
   14px/20px (Arial, then a generic sans-serif, if Roboto has not loaded).
   Web padding is 12px before the logo, 10px between the logo and the
   label, and 12px after the label. The color G stays unmodified.
   Rectangular corners are 4px, the same radius as Sign in with Discord
   and Sign in with X. Height stays the standard 40px, which is also the
   narrow-layout minimum on those neighboring buttons.
   Hover and press use the guidelines HTML button's white state overlay
   (8% and 12%). Keyboard focus keeps a visible outline.
   https://developers.google.com/identity/branding-guidelines */
body.site-page a.site-btn.gf-google-btn,
body.site-page .site-btn-row:has(.site-btn) > a.site-btn.gf-google-btn,
body.site-page .site-btn-row:has(.site-btn) > a.gf-google-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: #131314;
  background-image: none;
  color: #e3e3e3;
  border: 1px solid #8e918f;
  border-radius: 4px;
  box-shadow: none;
  font-family: Roboto, Arial, sans-serif;
  font-weight: 500;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.25px;
  padding: 0 12px;
  gap: 10px;
  height: 40px;
  min-height: 40px;
  text-decoration: none;
  transition: background-color .218s, box-shadow .218s;
}

body.site-page a.site-btn.gf-google-btn:hover,
body.site-page .site-btn-row:has(.site-btn) > a.site-btn.gf-google-btn:hover,
body.site-page .site-btn-row:has(.site-btn) > a.gf-google-btn:hover {
  background-color: #131314;
  background-image: linear-gradient(rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.08));
  color: #e3e3e3;
  border-color: #8e918f;
  box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.30), 0 1px 3px 1px rgba(60, 64, 67, 0.15);
  text-decoration: none;
}

body.site-page a.site-btn.gf-google-btn:focus,
body.site-page a.site-btn.gf-google-btn:active,
body.site-page .site-btn-row:has(.site-btn) > a.site-btn.gf-google-btn:focus,
body.site-page .site-btn-row:has(.site-btn) > a.site-btn.gf-google-btn:active,
body.site-page .site-btn-row:has(.site-btn) > a.gf-google-btn:focus,
body.site-page .site-btn-row:has(.site-btn) > a.gf-google-btn:active {
  background-color: #131314;
  background-image: linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12));
  color: #e3e3e3;
  border-color: #8e918f;
  text-decoration: none;
  outline: 2px solid #1a73e8;
  outline-offset: 2px;
}

body.site-page a.site-btn.gf-google-btn:visited,
body.site-page .site-btn-row:has(.site-btn) > a.site-btn.gf-google-btn:visited,
body.site-page .site-btn-row:has(.site-btn) > a.gf-google-btn:visited {
  color: #e3e3e3;
}

body.site-page a.gf-google-btn .gf-google-mark {
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  display: block;
}

/* Last in the service row. Discord and X stay at order 0, so a wrap
   still puts Google after them. */
body.site-page .site-btn-row > a.gf-google-btn {
  order: 1;
  flex: 0 0 auto;
  white-space: nowrap;
}

body.site-page .login-account-row {
  margin-top: 0.75rem;
}

body.site-page .login-account-row > b {
  flex: 0 1 auto;
  font-size: 1em;
  line-height: 1.35;
}

body.site-page .login-account-row > .login-return {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  flex: 0 1 auto;
  max-width: 100%;
}

body.site-page .login-account-row > a.site-btn-nav,
body.site-page .login-account-row .login-return > a.site-btn-nav {
  padding: 6px 14px;
  font-size: 1em;
  line-height: 1.25;
}

body.site-page .login-service-prompt {
  margin: 0.85rem 0 0.4rem;
  max-width: 100%;
}

html.gf-activity-no-lobby a.gf-google-btn {
  display: none !important;
}

.site-btn-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.45rem;
  max-width: 100%;
  vertical-align: top;
}

body:not(.duel-page) .site-btn-stack a.site-btn,
body:not(.duel-page) .site-btn-stack a.site-btn-nav,
.site-btn-stack .site-btn,
.site-btn-stack .site-btn-nav,
.site-btn-stack > a,
.site-btn-stack > button,
.site-btn-stack > form {
  display: block;
  width: 100%;
  box-sizing: border-box;
  align-self: stretch;
  text-align: center;
  white-space: nowrap;
}

/* Dashboard name-column actions. The buttons live inside <form>s, so the
   .site-btn-stack > button rule never applied. A fixed 90px width plus
   inherited td text-align:left clipped "Matchmaker" and shoved the label. */
.site-dash-actions {
  margin-top: 6px;
}

.site-dash-actions form {
  margin: 0;
  padding: 0;
}

body:not(.duel-page) .site-dash-actions button {
  /* Must beat body:not(.duel-page) button display:inline-block.
     Native buttons keep an inner flex item; without display:flex,
     justify-content is ignored and the label sits off-center. */
  display: flex;
  width: 100%;
  box-sizing: border-box;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 4px 10px;
  white-space: nowrap;
}

/* Equal-width parchment buttons in table-cell category grids (gear store). */
.site-btn-grid > td a.site-btn,
.site-btn-grid > td a.site-btn-nav {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: center;
}

/* Equal-width / equal-height parchment buttons in a CSS grid (leaderboard
   toggles, home header clusters). Column tracks share 1fr so every button
   matches the longest label in that row. */
nav.site-btn-grid,
div.site-btn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  grid-auto-rows: 1fr;
  gap: 0.45rem;
  align-items: stretch;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

nav.site-btn-grid > a,
nav.site-btn-grid > .site-btn,
nav.site-btn-grid > .site-btn-nav,
div.site-btn-grid > a,
div.site-btn-grid > .site-btn,
div.site-btn-grid > .site-btn-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  text-align: center;
  white-space: normal;
}

/* Current / selected toggle: pressed parchment, not a link. */
.site-btn-current,
span.site-btn-current {
  cursor: default;
  border-color: var(--site-ink);
  box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.28);
  background: linear-gradient(var(--site-btn-bot), var(--site-gold));
}

.site-btn-current:hover,
span.site-btn-current:hover {
  cursor: default;
  border-color: var(--site-ink);
  background: linear-gradient(var(--site-btn-bot), var(--site-gold));
  color: var(--site-ink);
}

/* Five leaderboard-type toggles: equal columns matching table width. */
nav.site-btn-grid-boards {
  width: 80%;
  margin: 12px auto;
  grid-template-columns: repeat(5, 1fr);
}

/* Compact parchment +/- controls (equip / quantity). Replaces plus.png / minus.png. */
.site-qty-btn,
a.site-qty-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 44px;
  min-height: 44px;
  padding: 0 0.35em;
  border: 2px solid var(--site-border);
  border-radius: 4px;
  background: linear-gradient(var(--site-btn-top), var(--site-btn-bot));
  color: var(--site-ink);
  font-weight: bold;
  font-size: 1.35em;
  line-height: 1;
  text-decoration: none;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: 0 2px 4px rgba(60, 30, 10, 0.22), inset 0 1px 0 rgba(255, 245, 220, 0.45);
}

.site-qty-btn:hover,
a.site-qty-btn:hover {
  background: linear-gradient(var(--site-btn-hover-top), var(--site-btn-hover-bot));
  color: var(--site-ink);
  text-decoration: none;
}

.site-qty-btn:focus,
a.site-qty-btn:focus {
  outline: 2px solid var(--site-gold);
  outline-offset: 2px;
  color: var(--site-ink);
}

/* ---------- Forms ---------- */

body:not(.duel-page) input[type="text"],
body:not(.duel-page) input[type="password"],
body:not(.duel-page) input[type="email"],
body:not(.duel-page) input[type="number"],
body:not(.duel-page) input[type="search"],
body:not(.duel-page) select,
body:not(.duel-page) textarea {
  padding: 5px 8px;
  border: 1px solid var(--site-border);
  border-radius: 4px;
  background: var(--site-input-bg);
  color: var(--site-ink);
  max-width: 100%;
}

body:not(.duel-page) input:focus,
body:not(.duel-page) select:focus,
body:not(.duel-page) textarea:focus {
  outline: 2px solid var(--site-gold);
  outline-offset: 1px;
}

/* ---------- Portraits / framed images ---------- */

.site-portrait,
body:not(.duel-page) img.site-portrait,
.gf-home .gf-home-portrait {
  border: 3px solid var(--site-border);
  border-radius: 4px;
  box-shadow: 0 3px 10px rgba(40, 20, 5, 0.45);
  background: rgba(255, 250, 235, 0.35);
  object-fit: contain;
}

/* ---------- Footer ---------- */

/* Same width as the gf.css / gg.css / tw.css content tables (80%).
   min(1165px, 96%) sat inside that band on a wide screen and outside it
   on a narrower one, so the copyright line did not meet the table above.
   80% keeps the same side margin, and that margin compresses with the
   viewport. Sheets with their own ladder override this. No min-width:
   a pixel floor scrolls inside a Discord Activity. */
body:not(.duel-page) table.footer,
body:not(.duel-page) table.site-footer {
  width: 80%;
  max-width: 100%;
  margin-top: auto;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 16px;
  border: 2px solid var(--site-border);
  border-radius: var(--site-radius);
  background-color: var(--site-panel);
  box-shadow: var(--site-shadow), var(--site-inset);
  color: var(--site-ink);
}

body:not(.duel-page) table.footer td,
body:not(.duel-page) table.site-footer td {
  padding: 8px 12px;
  text-align: center;
}

/* Post-duel review loads Bootstrap. Bootstrap's .table is width 100%,
   which beats the 80% element rule. The copyright table is not .table,
   so without this it stays at 80%. Hub, dashboard, path, create, and
   MatchMaker sheets are a different width and are not selected here. */
body.site-page:not(.hub-page):not(.dash-page):not(.path-page):not(.create-page):not(.mm-page) > table.table ~ table.footer,
body.site-page:not(.hub-page):not(.dash-page):not(.path-page):not(.create-page):not(.mm-page) > table.table ~ table.site-footer {
  width: 100%;
  max-width: 100%;
}

/* ---------- Page titles / nav ---------- */

.site-title {
  font-size: 1.35em;
  font-weight: bold;
  text-shadow: 0 1px 0 rgba(255, 245, 220, 0.6);
  color: var(--site-ink);
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1rem;
}

.site-nav a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

.site-nav a.site-btn,
.site-nav a.site-btn-nav {
  min-height: 0;
}

/* Gold-vs-rust hierarchy cues (player / opponent, primary / danger) */
.site-accent-gold {
  border-left: 3px solid var(--site-gold);
  background: linear-gradient(90deg, rgba(180, 140, 80, 0.28), transparent);
  padding-left: 8px;
}

.site-accent-rust {
  border-right: 3px solid var(--site-rust);
  background: linear-gradient(270deg, rgba(140, 70, 50, 0.28), transparent);
  padding-right: 8px;
}

/* ---------- Home + tutorial: align local shells with the system ---------- */

.gf-home .gf-home-panel,
.gf-tutorial .gf-tutorial-panel,
.gf-home .gf-home-footer-table {
  border: 2px solid var(--site-border);
  border-radius: var(--site-radius);
  background-color: var(--site-panel);
  box-shadow: var(--site-shadow), var(--site-inset);
  overflow: hidden;
}

.gf-home .gf-home-section h2,
.gf-tutorial .gf-tutorial-main h2 {
  text-shadow: 0 1px 0 rgba(255, 245, 220, 0.55);
}

.gf-home .gf-home-nav-links a,
.gf-tutorial .gf-tutorial-nav a {
  font-weight: bold;
}

/* ---------- Dashboard / inventory portraits ---------- */

img.site-portrait-sm {
  width: 80px;
  height: 80px;
  max-width: 100%;
  object-fit: contain;
  border: 3px solid var(--site-border);
  border-radius: 4px;
  box-shadow: 0 3px 10px rgba(40, 20, 5, 0.45);
  background: rgba(255, 250, 235, 0.35);
}

/* ---------- Mobile ---------- */

@media (max-width: 45rem) {
  body:not(.duel-page) table {
    width: 100%;
    max-width: 100%;
  }

  body.site-page {
    margin: 0;
    padding: 0.5rem;
    width: auto;
    min-width: 0;
  }

  /* The rule above stretches every content table to the padded body.
     The shared footer rule is more specific, so name it here too.
     Otherwise the copyright line stays at 80% beside a full-width table. */
  body:not(.duel-page) table.footer,
  body:not(.duel-page) table.site-footer {
    width: 100%;
    max-width: 100%;
  }

  body:not(.duel-page) button,
  body:not(.duel-page) input[type="submit"],
  .site-btn,
  .site-btn-nav,
  .site-qty-btn {
    min-height: 40px;
  }

  body:not(.duel-page) [style*="white-space: nowrap"],
  body:not(.duel-page) span[style*="nowrap"] {
    white-space: normal !important;
  }

  body:not(.duel-page) .site-btn-stack a.site-btn,
  body:not(.duel-page) .site-btn-stack a.site-btn-nav,
  .site-btn-stack .site-btn,
  .site-btn-stack .site-btn-nav,
  .site-btn-stack > a,
  .site-btn-stack > button {
    white-space: normal;
  }

  nav.site-btn-grid-boards {
    width: 100%;
  }
}

@media (max-width: 56rem) {
  nav.site-btn-grid-boards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 30rem) {
  .gf-home .gf-home-actions .gf-home-button,
  .gf-tutorial .gf-tutorial-actions .gf-tutorial-button {
    flex: 1 1 100%;
    text-align: center;
  }

  nav.site-btn-grid-boards {
    grid-template-columns: 1fr;
  }
}

/* ---------- MatchMaker stack + equal-height create buttons ---------- */

/* One cohesive column (same idea as main.php's single dashboard table):
   same width, no vertical parchment gaps, shared abutting borders. */
body.mm-page .mm-stack,
body.pbem-page .mm-stack {
  width: min(1165px, 96%);
  margin: 8px auto 0;
  display: block;
  overflow-x: auto;
}

body.mm-page .mm-stack > table,
body.mm-page .mm-stack > table.site-panel,
body.mm-page .mm-stack > table.lobby-panel,
body.pbem-page .mm-stack > table,
body.pbem-page .mm-stack > table.site-panel {
  width: 100%;
  max-width: 100%;
  margin: 0;
  border-radius: 0;
  box-shadow: none;
}

body.mm-page .mm-stack > table + table,
body.pbem-page .mm-stack > table + table {
  border-top-width: 0;
}

body.mm-page .mm-stack > table:first-child,
body.pbem-page .mm-stack > table:first-child {
  border-top-left-radius: var(--site-radius);
  border-top-right-radius: var(--site-radius);
}

body.mm-page .mm-stack > table:last-child,
body.pbem-page .mm-stack > table:last-child {
  border-bottom-left-radius: var(--site-radius);
  border-bottom-right-radius: var(--site-radius);
}

/* Open-challenge title. Same hub-banner table as Current Events Board,
   with the site-title used by Match Creation and Saloon Lobby. Fixed
   layout so the title cannot become a min-content floor and scroll the
   Activity iframe or a viewport under 800px. The challenge rows stay a
   real table. */
body.mm-page .mm-stack > table.mm-challenges-banner,
body.pbem-page .mm-stack > table.mm-challenges-banner {
  min-width: 0;
  table-layout: fixed;
}

body.mm-page .mm-stack > table.mm-challenges-banner th,
body.pbem-page .mm-stack > table.mm-challenges-banner th {
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}

body.mm-page table.footer,
body.mm-page table.site-footer,
body.pbem-page table.footer,
body.pbem-page table.site-footer {
  width: min(1165px, 96%);
  margin-top: 16px;
  color: var(--site-ink);
}

body.mm-page table.footer td,
body.mm-page table.site-footer td,
body.mm-page table.footer p,
body.mm-page table.site-footer p,
body.pbem-page table.footer td,
body.pbem-page table.site-footer td,
body.pbem-page table.footer p,
body.pbem-page table.site-footer p {
  color: var(--site-ink);
}

/* Two rows (PBEM / Timed). Stretch so the short "Any Level" labels
   match the taller level-range buttons. Extra gap after Any Level. */
.mm-create-row {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 6px;
  vertical-align: top;
  margin: 4px 0;
}

.mm-create-row .mm-create-btn {
  width: 155px;
  min-height: 3.4em;
  white-space: normal;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  box-sizing: border-box;
}

.mm-create-row .mm-create-any {
  margin-right: 12px;
}

/* One step under the page header (site-title is 1.35em bold). */
.mm-gf-level {
  display: block;
  margin: 0 0 0.2rem;
  max-width: 100%;
  font-size: 1.15em;
  font-weight: 600;
  line-height: 1.3;
  color: var(--site-ink);
}

.mm-create-note {
  display: block;
  margin: 0 0 0.6rem;
  max-width: 100%;
  font-size: 1em;
  font-weight: bold;
  line-height: 1.4;
  color: var(--site-ink);
}

/* The sentence under TIMED DUELS. Same size and wrap as the note above
   it, but regular weight. */
.mm-create-note.mm-timed-next {
  font-weight: normal;
}

/* Empty slot takes no space. A real message is only as tall as its
   text, in bold #8b0000, so the create buttons are not held down by a
   reserved gap. [hidden] wins over the block display below. */
.mm-pbem-email {
  display: block;
  box-sizing: border-box;
  margin: 0 0 0.2rem;
  padding: 0;
  min-height: 0;
  font-size: 1em;
  font-weight: bold;
  line-height: 1.35;
  color: #8b0000;
}

.mm-pbem-email[hidden] {
  display: none !important;
  margin: 0;
  padding: 0;
  min-height: 0;
}

.mm-pbem-email a {
  color: #8b0000;
  font-weight: bold;
}

/* Join copy used to be near-white, which disappears on the parchment panel. */
.mm-ink,
body.mm-page .mm-ink {
  color: var(--site-ink);
}

/* ---------- MatchMaker lobby (Pusher presence / chat / challenge) ---------- */

.lobby-panel {
  margin: 0;
  max-width: none;
}

/* Activity document only. display:none takes the Saloon Lobby out of
   flow so the challenges table sits in its place. No min-width. The
   phone rules below still keep MatchMaker as a real table. */
html.gf-activity-no-lobby #lobby-root {
  display: none !important;
}

/* Direct PvP list. Hidden on the website. The Activity class (set by
   PHP or by the discordsays head script) shows it. No min-width: the
   phone rules must not be able to force this panel back on. */
#activity-room {
  display: none !important;
}

html.gf-activity-no-lobby #activity-room {
  display: table !important;
  width: 100% !important;
}

.activity-room-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.activity-room-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(107, 62, 31, 0.25);
}

.activity-room-list li.lobby-empty {
  display: block;
  border-bottom: 0;
}

.activity-room-name {
  flex: 1 1 8rem;
  min-width: 0;
}

.activity-room-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.accept-comers {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-left: 0.75rem;
  font-weight: bold;
  font-size: 0.95em;
  line-height: 1.2;
  vertical-align: middle;
  cursor: pointer;
}

.accept-comers input {
  width: 1.1em;
  height: 1.1em;
  margin: 0;
}

.comers-quiet {
  color: var(--site-muted);
  font-style: italic;
  font-weight: normal;
}

@media (max-width: 800px) {
  body.mm-page .accept-comers {
    display: flex;
    margin: 8px 0 0;
    min-height: 44px;
  }
}

html.gf-activity-no-lobby body.path-page #activity-room .activity-room-actions button,
html.gf-activity-no-lobby body.path-page #activity-room .lobby-incoming-actions button {
  display: inline-block;
  width: auto !important;
  min-width: 7.5em;
  margin-top: 0;
}

/* Activity document only. Unlink Discord calls confirm(), which the
   Activity iframe does not run. The website still shows the button. */
html.gf-activity-no-lobby #discord-unlink,
html.gf-activity-no-lobby #google-unlink,
html.gf-activity-no-lobby #google-link {
  display: none !important;
}

/* Activity document only. Logout ends the partitioned session and the
   website login form then leaves the mapped host. Exit asks the shell
   to close the Activity. The website still shows Logout. No min-width. */
body.dash-page button.gf-activity-exit {
  display: none;
  font-family: inherit;
  padding: 4px 10px;
  font-size: 0.95em;
}

html.gf-activity-no-lobby body.dash-page button.gf-activity-exit {
  display: inline-block;
}

html.gf-activity-no-lobby a.gf-website-logout {
  display: none !important;
}

/* Activity document only. Shopify checkout does not run in the Activity
   iframe. The website still shows Buy A Game Code. Discord Store is the
   fallback when PHP did not see the proxy headers. */
html.gf-activity-no-lobby .gf-web-store {
  display: none !important;
}

.gf-discord-store {
  display: none !important;
}

html.gf-activity-no-lobby .gf-discord-store {
  display: inline-block !important;
}

html.gf-activity-no-lobby div.gf-discord-store {
  display: block !important;
}

.lobby-panel .site-title {
  display: inline-block;
}

.lobby-layout {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: stretch;
}

.lobby-members,
.lobby-chat {
  flex: 1 1 280px;
  min-width: 240px;
}

.lobby-members h3,
.lobby-chat h3 {
  margin: 0 0 6px;
  color: var(--site-ink);
}

.lobby-member-list,
.lobby-chat-log {
  list-style: none;
  margin: 0;
  padding: 8px;
  min-height: 160px;
  max-height: 240px;
  overflow-y: auto;
  background: var(--site-input-bg);
  border: 1px solid rgba(107, 62, 31, 0.35);
  border-radius: 4px;
}

.lobby-member-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(107, 62, 31, 0.15);
}

.lobby-member-list li:last-child {
  border-bottom: none;
}

.lobby-empty {
  font-style: italic;
  color: var(--site-muted);
}

.lobby-chat-log {
  display: block;
}

.lobby-chat-line {
  margin: 0 0 6px;
  color: var(--site-ink);
}

.lobby-chat-who {
  font-weight: bold;
  color: var(--site-gold);
}

/* Small text control. Beats the parchment button rules, including the
   phone min-height, so it stays a quiet link beside the line. */
body.mm-page button.lobby-chat-report,
body.mm-page button.lobby-chat-report:hover,
body.mm-page button.lobby-chat-report:focus {
  display: inline;
  width: auto;
  min-height: 0;
  margin: 0 0 0 6px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: var(--site-muted);
  font-size: 0.72em;
  font-weight: normal;
  line-height: 1;
  text-decoration: underline;
  vertical-align: baseline;
  cursor: pointer;
}

body.mm-page button.lobby-chat-report:hover,
body.mm-page button.lobby-chat-report:focus {
  color: var(--site-rust);
  background: none;
}

body.mm-page button.lobby-chat-report:focus {
  outline: 1px solid var(--site-gold);
  outline-offset: 1px;
}

.lobby-chat-form {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}

.lobby-chat-form input[type="text"] {
  flex: 1 1 auto;
  padding: 6px 8px;
  border: 1px solid var(--site-border);
  border-radius: 4px;
  background: var(--site-input-bg);
  color: var(--site-ink);
}

.lobby-status {
  margin: 6px 0 0;
  color: var(--site-muted);
  font-size: 0.95em;
}

.lobby-status-error {
  color: var(--site-rust);
  font-weight: bold;
}

.lobby-incoming {
  display: none;
  margin: 8px 0;
  padding: 8px 10px;
  background: var(--site-highlight);
  border: 1px solid rgba(107, 62, 31, 0.35);
  border-radius: 4px;
}

.lobby-incoming-live {
  display: block;
  border: 2px solid var(--site-rust);
  background: rgba(255, 236, 200, 0.95);
  box-shadow: 0 0 0 2px rgba(122, 59, 46, 0.12);
}

.lobby-incoming-from {
  margin: 4px 0 0;
}

.lobby-incoming-actions {
  margin-top: 6px;
}

.lobby-challenge-btn {
  width: auto;
  min-width: 8.5em;
  padding: 3px 8px;
  font-size: 0.85em;
  text-align: center;
}

.lobby-challenge-btn:disabled,
.lobby-challenge-sent {
  opacity: 0.85;
  cursor: default;
}

.lobby-note {
  margin: 4px 0 8px;
  color: var(--site-muted);
  font-size: 0.9em;
}

/* ---------- Phone-width dashboard + gunfighter creation ----------
   Viewport under 800px (phone portrait, including the Discord iOS
   in-app browser) swaps the dashboard table for .dash-mobile and
   stacks the create-a-gunfighter sheet. Wider viewports keep the
   multi-column table. .dash-mobile stays hidden attribute + display
   none until that breakpoint. */

.dash-mobile {
  display: none;
}

.create-return {
  margin-left: 0.75rem;
}

.create-inline-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  vertical-align: middle;
}

.create-avatars {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
}

.create-avatar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  min-width: 0;
}

.create-avatar img.site-portrait {
  width: 100%;
  height: 200px;
  object-fit: contain;
}

.create-avatar button {
  width: 100%;
}

.create-avatar.is-selected img.site-portrait {
  box-shadow: 0 0 0 3px var(--site-gold), 0 3px 10px rgba(40, 20, 5, 0.45);
}

/* A replaced creation name. Dark red on the parchment, same weight as the
   other short warnings, and outside the 800px query so the wide site and
   the narrow Activity sheet both show it. */
body.create-page .create-name-note {
  display: block;
  margin: 4px 0 0;
  color: #8b0000;
  font-weight: bold;
  line-height: 1.35;
  white-space: normal;
}

/* Assigned gunfighter name on the creation sheet only. A modest step up
   from the surrounding text, bold, and vertically centered in this cell.
   Copperplate has no bold face, so the name uses Arial (same reason as
   store prices). Scoped to this cell so other tables are unchanged, and
   outside the 800px query so the wide site and the narrow Activity sheet
   both get it. */
body.create-page td.create-assigned-name {
  vertical-align: middle;
}

body.create-page td.create-assigned-name .create-assigned-name-value {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 1.2em;
  font-weight: bold;
  line-height: 1.2;
  overflow-wrap: break-word;
}

@media (max-width: 800px) {
  body.dash-page.site-page,
  body.create-page.site-page {
    margin: 0;
    padding: 0;
    width: auto;
    min-width: 0;
  }

  body.dash-page .dash-desktop {
    display: none;
  }

  body.dash-page .dash-mobile,
  body.dash-page .dash-mobile[hidden] {
    display: block !important;
  }

  body.dash-page .dash-mobile {
    width: 100%;
    padding-bottom: 12px;
  }

  body.dash-page table.footer,
  body.dash-page table.site-footer,
  body.create-page table.footer,
  body.create-page table.site-footer,
  body.create-page table.create-sheet {
    width: 100%;
    max-width: 100%;
    margin-left: 0;
    margin-right: 0;
  }

  .mob-chrome {
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    z-index: 30;
    background: var(--site-panel-solid);
    border-bottom: 2px solid var(--site-border);
    box-shadow: var(--site-shadow);
    padding: calc(8px + env(safe-area-inset-top)) 10px 10px;
  }

  .mob-chrome-title {
    display: block;
    line-height: 1.25;
  }

  .mob-chrome-tools,
  .mob-chrome-links {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    margin-top: 8px;
  }

  .mob-chrome-links {
    padding: 0 10px;
  }

  .mob-chrome-tools a,
  .mob-chrome-tools button.gf-activity-exit,
  .mob-chrome-links a {
    min-height: 36px;
  }

  .mob-stable {
    margin: 8px 0 0;
    font-size: 0.92rem;
  }

  .mob-turns {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 8px 10px 0;
  }

  .mob-turn {
    display: block;
    background: #6e1c1c;
    color: #fff !important;
    text-decoration: none;
    font-weight: bold;
    text-align: center;
    padding: 12px 10px;
    border-radius: 4px;
    border: 2px solid #3a0c0c;
  }

  .mob-turn-kicker {
    display: block;
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  .mob-roster {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 10px;
  }

  .mob-gf {
    border: 2px solid var(--site-border);
    border-radius: var(--site-radius);
    background: var(--site-panel);
    box-shadow: var(--site-shadow), var(--site-inset);
    padding: 12px;
    scroll-margin-top: 11rem;
  }

  .mob-gf-head {
    display: flex;
    gap: 12px;
    align-items: center;
  }

  img.site-portrait-sm.mob-portrait {
    width: 96px;
    height: 96px;
    flex: 0 0 auto;
    scroll-margin-top: 11rem;
  }

  .mob-gf-name {
    margin: 0 0 6px;
    font-size: 1.25rem;
    line-height: 1.2;
    color: var(--site-ink);
    overflow-wrap: anywhere;
  }

  .mob-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }

  .mob-chip {
    background: var(--site-th);
    border: 1px solid var(--site-border);
    border-radius: 999px;
    padding: 2px 8px;
    font-size: 0.85rem;
    font-weight: bold;
  }

  .mob-cta,
  .mob-duels,
  .mob-manage,
  .mob-where,
  .mob-news {
    margin-top: 12px;
  }

  body.dash-page .mob-cta .site-btn-stack,
  body.dash-page .mob-manage .site-btn-stack {
    display: flex;
    width: 100%;
  }

  body.dash-page .mob-cta .site-dash-actions button,
  body.dash-page .mob-cta .site-dash-actions a.site-btn-retire,
  body.dash-page .mob-manage .site-btn-stack > a,
  body.dash-page .mob-manage .site-btn-stack > button,
  body.dash-page .mob-where > a.site-btn-nav,
  body.dash-page .mob-create .site-btn,
  body.dash-page .mob-create .site-btn-nav {
    white-space: normal;
    width: 100%;
    min-height: 44px;
  }

  .mob-duels h3,
  .mob-kicker {
    margin: 0 0 4px;
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--site-muted);
  }

  .mob-duels .gf-duel-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }

  .mob-duels .gf-duel-chips a.gf-duel-chip {
    display: inline-block;
    width: auto;
    min-height: 44px;
    padding: 8px 10px;
    font-size: 1.05rem;
  }

  .mob-duels form,
  .mob-duels button,
  .mob-retire button,
  .mob-retire a {
    width: 100%;
    min-height: 44px;
    white-space: normal;
  }

  .mob-manage {
    border: 1px solid var(--site-border);
    border-radius: 4px;
    background: var(--site-highlight);
  }

  .mob-manage summary {
    cursor: pointer;
    font-weight: bold;
    padding: 12px;
    min-height: 44px;
    background: linear-gradient(var(--site-btn-top), var(--site-btn-bot));
    color: var(--site-ink);
  }

  .mob-manage-body,
  .mob-equipped,
  .mob-manage-meta,
  .mob-retire {
    padding: 0 12px 12px;
  }

  .mob-manage-meta,
  .mob-equipped {
    margin: 0 0 8px;
  }

  .mob-region {
    font-weight: bold;
  }

  .mob-env {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 4px 0;
  }

  .mob-news {
    padding: 8px 10px;
    background: var(--site-highlight);
    border-left: 3px solid var(--site-rust);
  }

  .mob-news p {
    margin: 0 0 6px;
  }

  .mob-create {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 4px 10px 8px;
  }

  body.create-page table.create-sheet,
  body.create-page table.create-sheet tbody,
  body.create-page table.create-sheet tr,
  body.create-page table.create-sheet td {
    display: block;
    width: 100% !important;
    box-sizing: border-box;
  }

  body.create-page table.create-sheet {
    border-radius: 0;
  }

  body.create-page table.create-sheet td {
    padding: 12px !important;
  }

  /* The stacked name cell is no longer a table-cell, so vertical-align
     does not center it. Flex keeps the assigned name in the vertical
     middle of this cell without changing the full-width stack. */
  body.create-page table.create-sheet td.create-assigned-name {
    display: flex;
    align-items: center;
    min-width: 0;
  }

  body.create-page table.create-sheet td.create-assigned-name > b {
    min-width: 0;
    max-width: 100%;
  }

  body.create-page .create-return {
    display: block;
    margin: 10px 0 0;
  }

  body.create-page .create-inline-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  body.create-page .create-sheet select,
  body.create-page .create-sheet input,
  body.create-page .create-sheet button,
  body.create-page .create-sheet a.site-btn-nav,
  body.create-page .create-inline-actions button {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin-top: 8px;
    font-size: 16px;
    min-height: 44px;
    white-space: normal;
  }

  body.create-page .create-avatars {
    grid-template-columns: 1fr;
  }

  body.create-page .create-avatar {
    align-items: center;
  }

  body.create-page .create-avatar img.site-portrait {
    width: min(180px, 52vw);
    height: 180px;
    max-height: 180px;
  }
}

/* Each gunfighter is its own table so News and unequipped gear sit inside
   that fighter's border. Header and fighter tables share width and border
   width so the eight columns still line up. Active duels are a full-width
   row under News, not a column. A double rule plus a short gap separates
   fighters without a tall spacer row. These rules follow the phone
   breakpoint so .dash-desktop is not referenced above it. */
body.dash-page .dash-desktop > table.dash-sheet,
body.dash-page .dash-desktop > table.gf-fighter,
body.dash-page .dash-desktop > table.dash-foot {
  /* Wide screens stay at 80%. Narrower desktops grow toward 1180px
     (fixed columns plus room for Equipped Gear) before that column
     compresses, and never wider than the dashboard wrapper. */
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
  border-width: 3px;
  border-style: solid;
  border-color: var(--site-border);
}

/* Copyright table sits outside .dash-desktop, so it does not inherit the
   sheet width above. Same ladder, and no min-width: the fighter tables
   already refuse a pixel floor that would scroll the Activity iframe. */
body.dash-page > table.footer,
body.dash-page > table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
}

body.dash-page .dash-desktop > table.dash-sheet,
body.dash-page .dash-desktop > table.gf-fighter {
  table-layout: fixed;
}

body.dash-page .dash-desktop > table.dash-sheet {
  margin-top: 12px;
}

body.dash-page .dash-desktop > table.gf-fighter,
body.dash-page .dash-desktop > table.dash-foot {
  margin-top: 8px;
}

body.dash-page .dash-desktop > table.gf-fighter {
  border-style: double;
}

body.dash-page .dash-desktop > table.gf-fighter tr.gf-news td,
body.dash-page .dash-desktop > table.gf-fighter tr.gf-duels td {
  background: var(--site-row);
  border-top: 1px solid var(--site-border);
}

/* Bright green on the parchment duel row. #006900 is the lightest pure
   green that still clears the tan page, the solid panel, and the news row. */
body.dash-page .gf-duel-legend {
  color: #006900;
  font-weight: bold;
}

body.dash-page .gf-duel-legend form {
  margin: 4px 0 0;
}

body.dash-page .gf-name-line {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

body.dash-page .gf-name-line > b,
body.dash-page .gf-name-line > .mob-gf-name {
  min-width: 0;
  margin-top: 0;
  overflow-wrap: anywhere;
}

body.dash-page button.gf-drag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 28px;
  height: 28px;
  min-width: 28px;
  min-height: 28px;
  margin: 0;
  padding: 0;
  line-height: 1;
  flex: 0 0 28px;
  touch-action: none;
  cursor: grab;
  vertical-align: top;
}

body.dash-page button.gf-drag:active,
body.dash-page .gf-dragging button.gf-drag {
  cursor: grabbing;
}

body.dash-page button.gf-drag .gf-drag-grip {
  display: block;
  width: 12px;
  height: 10px;
  background-image:
    linear-gradient(#3a2412, #3a2412),
    linear-gradient(#3a2412, #3a2412),
    linear-gradient(#3a2412, #3a2412);
  background-size: 12px 2px;
  background-repeat: no-repeat;
  background-position: 0 0, 0 4px, 0 8px;
}

body.dash-page table.gf-fighter.gf-dragging,
body.dash-page article.mob-gf.gf-dragging {
  outline: 3px solid #517a2e;
  outline-offset: 2px;
  -webkit-user-select: none;
  user-select: none;
}

body.dash-page .gf-order-note {
  margin: 8px 10px;
  color: #8b0000;
  font-weight: bold;
}

.gf-duel-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: 4px;
}

a.gf-duel-chip {
  display: inline-block;
  padding: 4px 8px;
  border: 1px solid #5c3b16;
  border-radius: 4px;
  background: #fff8ea;
  color: #2a1a0a;
  text-decoration: none;
  line-height: 1.3;
}

a.gf-duel-chip:visited {
  color: #2a1a0a;
}

a.gf-duel-chip-turn,
a.gf-duel-chip-turn:visited {
  color: #0b2a6b;
  background: #e7eef8;
  border-color: #0b2a6b;
  font-weight: bold;
}

a.gf-duel-chip-turn b {
  color: #0b2a6b;
  font-weight: bold;
}

.gf-duel-turn-label {
  color: #0b2a6b;
}

.gf-limit-slot {
  display: block;
  width: 100%;
  max-width: 100%;
}

.gf-limit-reason,
.gf-limit-error {
  display: block;
  margin: 4px 0 0;
  color: #8b0000;
  font-weight: bold;
  white-space: normal;
}

/* display:block above would otherwise show a reason that is still hidden.
   Tap removes the attribute, and the block rule above shows it. */
.gf-limit-reason[hidden],
.gf-limit-error[hidden] {
  display: none !important;
}

button.gf-limit-off,
a.gf-limit-off,
input.gf-limit-off,
button.gf-limit-off:disabled,
button.mm-create-btn:disabled {
  opacity: 0.72;
  cursor: not-allowed;
  filter: grayscale(0.35);
  color: var(--site-ink);
}

/* The shared button rule sets cursor:pointer and a lighter hover.
   A gated control must not look clickable. Specificity beats
   body:not(.duel-page) button:hover and a.site-btn-nav:hover. */
body:not(.duel-page) button.gf-limit-off,
body:not(.duel-page) button.gf-limit-off:hover,
body:not(.duel-page) button.gf-limit-off:focus,
body:not(.duel-page) button.gf-limit-off:active,
body:not(.duel-page) a.gf-limit-off,
body:not(.duel-page) a.gf-limit-off:hover,
body:not(.duel-page) a.gf-limit-off:focus,
body:not(.duel-page) a.gf-limit-off:active,
body:not(.duel-page) a.site-btn-nav.gf-limit-off,
body:not(.duel-page) a.site-btn-nav.gf-limit-off:hover,
body:not(.duel-page) a.site-btn-nav.gf-limit-off:focus,
body:not(.duel-page) a.site-btn-nav.gf-limit-off:active {
  cursor: not-allowed;
  background: linear-gradient(var(--site-btn-top), var(--site-btn-bot));
  opacity: 0.72;
  filter: grayscale(0.35);
  text-decoration: none;
  color: var(--site-ink);
}

/* Equipped Gear stays in the eight-column row on wide screens. The matching
   full-width row is in the fighter table (above News) for the mid band. */
body.dash-page .dash-desktop tr.gf-gear {
  display: none;
}

/* "Equipped" is about 73px in this header. 1100px moves that column into
   the full-width gear row while the header still fits. Phones stay on the
   800px card layout. No min-width: the fixed columns already floor near
   812px once Equipped Gear is collapsed, and this must not push that
   scrollbar any wider. Active duels stay on their own full-width row. */
@media (max-width: 1100px) {
  body.dash-page .dash-desktop col.dash-col-gear,
  body.dash-page .dash-desktop th.dash-gear,
  body.dash-page .dash-desktop td.dash-gear {
    visibility: collapse;
  }

  body.dash-page .dash-desktop tr.gf-gear {
    display: table-row;
  }

  body.dash-page .dash-desktop > table.gf-fighter tr.gf-gear td {
    background: var(--site-row);
    border-top: 1px solid var(--site-border);
  }
}

/* After Equipped Gear is a row, the other columns are fixed pixels that
   add up to 812px (name 150, four 68s, skills 190, store 200). Body margin
   is 0, so that fits the viewport down to about 812px. Below that the
   desktop table would scroll. 960px starts a proportional shrink — same
   ratios as the pixel columns, gear stays collapsed — so the table is
   100% of the wrapper through the 800px phone switch. No px min-width,
   so this cannot force a scrollbar. */
@media (max-width: 960px) {
  body.dash-page .dash-desktop > table.dash-sheet,
  body.dash-page .dash-desktop > table.gf-fighter,
  body.dash-page .dash-desktop > table.dash-foot,
  body.dash-page > table.footer,
  body.dash-page > table.site-footer {
    width: 100%;
    max-width: 100%;
  }

  body.dash-page .dash-desktop colgroup col:nth-child(1) { width: calc(150 / 812 * 100%) !important; }
  body.dash-page .dash-desktop colgroup col:nth-child(2),
  body.dash-page .dash-desktop colgroup col:nth-child(3),
  body.dash-page .dash-desktop colgroup col:nth-child(4),
  body.dash-page .dash-desktop colgroup col:nth-child(5) { width: calc(68 / 812 * 100%) !important; }
  body.dash-page .dash-desktop colgroup col:nth-child(6) { width: calc(190 / 812 * 100%) !important; }
  body.dash-page .dash-desktop colgroup col:nth-child(7) { width: calc(200 / 812 * 100%) !important; }
  body.dash-page .dash-desktop colgroup col:nth-child(8) { width: 0 !important; }
}

/* Shorter Max UI dashboard actions. Horizontal padding stays 10px and the
   type size stays at the nav size, so the buttons keep the pre-change
   content width. Only the vertical padding and line-height come down.
   max-width keeps a label inside the cell; normal wrapping is the fallback
   when a cell is narrower than the label. */
body.dash-page .dash-desktop .site-dash-actions,
body.dash-page .dash-desktop td > .site-btn-stack {
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

body.dash-page .dash-desktop a.site-btn-nav.site-btn-compact,
body.dash-page .dash-desktop .site-dash-actions button.site-btn-compact,
body.dash-page .dash-desktop .site-dash-actions a.site-btn-compact {
  max-width: 100%;
  box-sizing: border-box;
  padding: 2px 10px;
  font-size: 0.95em;
  line-height: 1.15;
  white-space: normal;
}

/* "PBEM Challenge" is the long name-column label. Same padding, width,
   and line box as the buttons above it. Only this label's type size
   drops 3px, so Copperplate still fits on one line in the 150px cell.
   The phone card and the Activity are full-width, so they keep the
   shared size and only refuse to wrap. */
body.dash-page .mob-cta .site-dash-actions button.site-btn-pbem {
  white-space: nowrap;
}

body.dash-page .dash-desktop .site-dash-actions button.site-btn-compact.site-btn-pbem {
  font-size: calc(0.95em - 3px);
  /* em in line-height follows this smaller font, so a unitless line
     would shrink the box. This minimum is the shared compact button:
     1.15 line at 0.95rem, plus 4px padding and 4px border. */
  min-height: calc(1.15 * 0.95rem + 8px);
  white-space: nowrap;
}

/* Muted red-gold. Same gradient and dark ink as the louder retire tint,
   with the chroma pulled back so the control stays readable and still
   reads as a button on desktop and in the phone manage section. */
body.dash-page a.site-btn-retire {
  background: linear-gradient(#e7d6d0, #c5a7a0);
  border-color: #8d5549;
  color: #3d1812;
}

body.dash-page a.site-btn-retire:hover {
  background: linear-gradient(#efe1dc, #d3b7b1);
  color: #3d1812;
}

/* The guide button is taken out of flow and centered in the existing header
   cell. The title and the right-hand controls are a separate flex row, so
   the controls stay at the top when the Manager name wraps onto a second
   line. The middle track is the button's width: neither column can slide
   under it, including inside a ~1000px Activity iframe. */
body.dash-page .dash-desktop td.dash-head {
  position: relative;
}

/* One line inside that gunfighter's News, after the first news item. */
.dash-pbem-posted {
  display: block;
  margin: 0.15rem 0 0;
  padding: 0;
  max-width: 100%;
  font-size: 1em;
  font-weight: bold;
  line-height: 1.3;
  color: #0b2a6b;
  overflow-wrap: anywhere;
}

body.dash-page .dash-desktop td.dash-head > .dash-head-row {
  display: flex;
  align-items: flex-start;
  width: 100%;
  min-width: 0;
}

body.dash-page .dash-desktop td.dash-head > .dash-head-row > .dash-head-main,
body.dash-page .dash-desktop td.dash-head > .dash-head-row > .dash-head-side {
  flex: 1 1 0;
  min-width: 0;
}

/* Measured guide button is 149px wide. 172px leaves a gap on both sides
   so the absolutely centered button is not covered by the title or the
   UX column. */
body.dash-page .dash-desktop td.dash-head > .dash-head-row > .dash-head-gap {
  flex: 0 0 172px;
  width: 172px;
}

body.dash-page .dash-desktop td.dash-head > .dash-head-row > .dash-head-main .site-title {
  overflow-wrap: anywhere;
}

body.dash-page .dash-desktop td.dash-head > .dash-head-row > .dash-head-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}

body.dash-page .dash-desktop .dash-head-ux,
body.dash-page .dash-desktop .dash-head-links {
  max-width: 100%;
}

body.dash-page .dash-desktop .dash-head-links {
  margin-top: 8px;
}

body.dash-page .dash-desktop .dash-head-ux > a.site-btn-nav,
body.dash-page .dash-desktop .dash-head-ux > button.site-btn-nav,
body.dash-page .dash-desktop .dash-head-links > a.site-btn-nav {
  max-width: 100%;
  box-sizing: border-box;
  white-space: normal;
}

body.dash-page .dash-desktop td.dash-head > a.site-btn-duel-guide {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: max-content;
  max-width: calc(100% - 16px);
}

/* Green-gold go cue. Outer height is twice a .site-btn-nav:
   1.25 line at this button's 0.95em, plus 8px padding and 4px of border.
   min-height is border-box, so 2 * (1.25em + 12px) = 2.5em + 24px.
   Two spans stack the label without a flex line-break quirk.
   Hue is shifted from the old red-gold; saturation and lightness stay. */
body.dash-page a.site-btn-duel-guide {
  box-sizing: border-box;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: calc(2.5em + 24px);
  padding: 4px 10px;
  font-size: 0.95em;
  line-height: 1.15;
  max-width: 100%;
  white-space: normal;
  text-align: center;
  background: linear-gradient(#c7e7b6, #96c374);
  border-color: #517a2e;
  color: #273d12;
}

body.dash-page a.site-btn-duel-guide:hover,
body.dash-page a.site-btn-duel-guide:focus {
  background: linear-gradient(#d2efc2, #a7d284);
  color: #273d12;
}

/* Same green-gold as How to Play. Only the bot-duel button in the stack. */
body.dash-page button.site-btn-go {
  background: linear-gradient(#c7e7b6, #96c374);
  border-color: #517a2e;
  color: #273d12;
}

body.dash-page button.site-btn-go:hover,
body.dash-page button.site-btn-go:focus {
  background: linear-gradient(#d2efc2, #a7d284);
  color: #273d12;
}

/* Phone chrome is a stack, not the desktop header row. The guide stays
   centered between the title and the UX tools and does not widen the page. */
.mob-chrome-guide {
  display: flex;
  justify-content: center;
  margin-top: 8px;
  min-width: 0;
  max-width: 100%;
}

.mob-chrome-guide > a.site-btn-duel-guide {
  width: max-content;
  max-width: 100%;
}

/* Hotel Room (/equip/index.php). Same grow ladder as the dashboard:
   wide screens stay at 80%, narrower desktops grow toward 1180px before
   Effects compresses, and the table never exceeds the viewport.
   Fixed columns are pixel widths that add up to 901px (type 90, name 150,
   level 56, rarity 88, territory 118, owned 76, equipped 99, equip 60,
   unequip 80, sell 84). Effects is the remainder and has no min-width.
   Owned and Equipped stay one line through the band just above 800px.
   Above 1100px that extra 19px comes out of Effects. Below 1100px Effects
   is already collapsed, so the same 19px comes out of Item Name.
   Consumables has no + or - controls. Equipped spans the equip track and
   Sell spans the unequip track, so those columns stay inside the cells
   instead of opening a hole. Effects is a single column, so collapsing it
   does not leave a gap between Rarity and Territory.
   min-width: 0 lets the table shrink inside gf.css's flex column; it is
   not a floor. Phone cards stay on the 800px hub rules. */
body.hub-page table.equip-sheet {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  table-layout: fixed;
}

/* Copyright line follows the Hotel Room sheet. A <p> sits between them,
   so this is a following sibling, not an adjacent one. min-width: 0
   matches the sheet (a flex shrink, not a floor). */
body.hub-page > table.equip-sheet ~ table.footer,
body.hub-page > table.equip-sheet ~ table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
}

body.hub-page table.equip-sheet th,
body.hub-page table.equip-sheet td,
body.hub-page table.equip-sheet .equip-effects {
  /* break-word splits a token only when it cannot fit the cell.
     anywhere was splitting Level, Owned, SELL, and rarity words. */
  overflow-wrap: break-word;
  word-break: normal;
}

/* Territory wraps at a space (Sierra Nevada) and nowhere else.
   Southwest must stay one word at every width. hyphens stay manual
   so lang=en cannot split it either. */
body.hub-page table.equip-sheet td[data-label="Territory"] {
  overflow-wrap: normal;
  word-break: normal;
  hyphens: manual;
  -webkit-hyphens: manual;
}

body.hub-page table.equip-sheet form {
  margin: 0;
  max-width: 100%;
}

/* Hotel Room + and - only. The square matches that row's SELL button.
   SELL keeps the browser control font (10pt, 13.333333px) here: line
   box 1.25em, 6px vertical padding, 2px border. The marks are drawn,
   not set in Copperplate, which has no bold face and paints a hairline
   plus. Each arm is 70% of the button face. The stroke stays 12% of
   the face (about 3.44px at desktop and 1.80px below 520px), the same
   absolute thickness as the 80% arm.
   Other pages keep the 44px control. */
body.hub-page table.equip-sheet a.site-qty-btn {
  position: relative;
  box-sizing: border-box;
  padding: 0;
  min-width: 0;
  min-height: 0;
  width: calc(1.25 * 13.333333px + 16px);
  height: calc(1.25 * 13.333333px + 16px);
  max-width: none;
  margin-top: 0;
  font-size: 0;
  line-height: 0;
  color: var(--site-ink);
}

body.hub-page table.equip-sheet a.site-qty-plus::before,
body.hub-page table.equip-sheet a.site-qty-minus::before,
body.hub-page table.equip-sheet a.site-qty-plus::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: currentColor;
  border-radius: 1px;
}

body.hub-page table.equip-sheet a.site-qty-plus::before,
body.hub-page table.equip-sheet a.site-qty-minus::before {
  width: 70%;
  height: 12%;
}

body.hub-page table.equip-sheet a.site-qty-plus::after {
  width: 12%;
  height: 70%;
}

/* "uncommon" is the longest rarity word on this sheet. Keep it one
   line. A narrower query lets it wrap when the column cannot hold it. */
body.hub-page table.equip-sheet td[data-label="Rarity"] {
  white-space: nowrap;
}

/* Bottom dashboard control. Same right edge as How Duels Work and
   Previous Duel AARs. overflow:hidden keeps the floated hub-return
   inside the cell. */
body.hub-page table.equip-sheet tr.equip-foot td {
  overflow: hidden;
  text-align: right;
}

/* Consumables Sell spans the unused unequip track. Keep the button in
   the cell, centered, so the extra width does not read as an empty column. */
body.hub-page table.equip-sheet td.equip-cons-sell {
  text-align: center;
}

body.hub-page table.equip-sheet td.equip-cons-sell form {
  display: inline-block;
  width: auto;
  max-width: 100%;
}

body.hub-page table.equip-sheet td.equip-cons-sell button {
  width: auto !important;
  display: inline-block;
  max-width: 100%;
}

/* Effects stays in the item row above the demotion gate. The matching
   full-width row is in the markup for the mid band. */
body.hub-page table.equip-sheet tr.equip-effects-row {
  display: none;
}

/* Activity-safe ladder (same as dashboard Equipped Gear, PR #55):
   the width rule above is not a media query, so it follows the table's
   box. The demotion IS a viewport @media (max-width: 1100px), with no
   min-width floor. Dashboard uses that exact query. An extra
   (min-width: 801px) made Hotel Room skip demotion whenever the
   Activity iframe was at or under the phone gate, and the phone rules
   then set tr/td to display:block, which zeroes those panels in the
   Activity iframe (MatchMaker already opts out of that). Do not add
   that floor back. Do not turn this sheet into display:block cards.
   Narrow widths keep this table and the full-width Effects row.
   Measured: Effects is 179px at 1120px and about 160px at 1100px.
   By 1000px an in-column Effects cell was under 100px. */
@media (max-width: 1100px) {
  body.hub-page table.equip-sheet {
    width: 100%;
    max-width: 100%;
  }

  body.hub-page > table.equip-sheet ~ table.footer,
  body.hub-page > table.equip-sheet ~ table.site-footer {
    width: 100%;
    max-width: 100%;
  }

  /* Shares still sum to 882. Level, Rarity, Equip, and Unequip take
     space from Item Type, Item Name, and Territory so a single word
     still fits when the sheet is about 800px wide. Name and Territory
     can wrap at a space. A narrower band below 800px retunes these. */
  body.hub-page table.equip-sheet col.equip-col-type { width: calc(72 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-name { width: calc(123 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-level { width: calc(62 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-rarity { width: calc(112 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-effects { width: 0 !important; }
  body.hub-page table.equip-sheet col.equip-col-terr { width: calc(100 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-owned { width: calc(76 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equipped { width: calc(99 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equip { width: calc(66 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-unequip { width: calc(88 / 882 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-sell { width: calc(84 / 882 * 100%) !important; }

  body.hub-page table.equip-sheet col.equip-col-effects,
  body.hub-page table.equip-sheet th.equip-effects,
  body.hub-page table.equip-sheet tr.hub-card > td.equip-effects {
    visibility: collapse;
  }

  /* visibility:collapse still counts the cell toward the row height.
     The column is width 0, so a long Effects string would stack one
     character per line and stretch the item row. Drop that copy out of
     flow. The full-width row below is the visible Effects text. Keep
     the cell so later columns stay put under table-layout:fixed. */
  body.hub-page table.equip-sheet th.equip-effects,
  body.hub-page table.equip-sheet tr.hub-card > td.equip-effects {
    padding: 0 !important;
    font-size: 0 !important;
    line-height: 0 !important;
    overflow: hidden;
  }

  body.hub-page table.equip-sheet tr.hub-card > td.equip-effects > * {
    display: none !important;
  }

  body.hub-page table.equip-sheet tr.equip-effects-row {
    display: table-row;
  }

  /* A double rule around the item row plus its Effects row, same idea
     as the double border between dashboard gunfighter tables. */
  body.hub-page table.equip-sheet tr.hub-card > td {
    border-top: 3px double var(--site-border);
  }

  body.hub-page table.equip-sheet tr.hub-card > td:first-child {
    border-left: 3px double var(--site-border);
  }

  body.hub-page table.equip-sheet tr.hub-card > td:last-child {
    border-right: 3px double var(--site-border);
  }

  body.hub-page table.equip-sheet tr.equip-effects-row > td {
    border-top: 1px solid var(--site-border);
    border-bottom: 3px double var(--site-border);
    border-left: 3px double var(--site-border);
    border-right: 3px double var(--site-border);
    background: transparent;
    padding: 8px 10px;
  }

  /* SELL padding here is 4px 8px. The square side is the 13.333333px
     line box plus that vertical padding and the 2px border. */
  body.hub-page table.equip-sheet a.site-qty-btn {
    width: calc(1.25 * 13.333333px + 12px);
    height: calc(1.25 * 13.333333px + 12px);
  }

  /* SELL stays one word inside the button. 8px side padding fits the
     sell track once the sheet is about 800px wide. */
  body.hub-page table.equip-sheet button {
    max-width: 100%;
    width: auto;
    box-sizing: border-box;
    white-space: nowrap;
    overflow-wrap: normal;
    padding: 4px 8px;
  }
}

/* Ol' Jeb's inventory (/gear/inventory.php), clothing and consumables.
   Same ladder as Hotel Room: wide screens stay at 80%, narrower desktops
   grow toward 1180px before Description of Benefits compresses, and the
   table never exceeds the viewport. Fixed columns are pixel widths that
   add up to 620px (item name 168, territory 124, level 76, rarity 100,
   price 76, buy 76). Description is the remainder and has no min-width.
   min-width: 0 lets the table shrink inside gf.css's flex column; it is
   not a floor. The pistol sheet has no Description column and stays on
   the hub cards. */
body.hub-page table.inventory-sheet {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  table-layout: fixed;
}

/* Clothing and consumables only. The pistol tuning table is
   pistol-skills, so this width does not apply to it. */
body.hub-page > table.inventory-sheet ~ table.footer,
body.hub-page > table.inventory-sheet ~ table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
}

body.hub-page table.inventory-sheet th,
body.hub-page table.inventory-sheet td,
body.hub-page table.inventory-sheet .inv-benefits {
  overflow-wrap: break-word;
  word-break: normal;
}

/* Same rule as Hotel Room. Jeb's tables share the territory label,
   and a narrow track must not split Southwest inside the word. */
body.hub-page table.inventory-sheet td[data-label="Territory"] {
  overflow-wrap: normal;
  word-break: normal;
  hyphens: manual;
  -webkit-hyphens: manual;
}

body.hub-page table.inventory-sheet form {
  margin: 0;
  max-width: 100%;
}

body.hub-page table.inventory-sheet button {
  max-width: 100%;
  min-width: 0;
  width: auto;
  box-sizing: border-box;
  white-space: nowrap;
  overflow-wrap: normal;
  padding: 4px 8px;
}

/* Description stays in the item row above the demotion gate. The matching
   full-width row is in the markup for the mid band. Its background is the
   item row's inline color (zebra or owned), not a second panel. */
body.hub-page table.inventory-sheet tr.inv-benefits-row {
  display: none;
}

/* Activity-safe ladder (same as dashboard Equipped Gear and Hotel Room):
   the width rule above is not a media query. The demotion IS a viewport
   @media (max-width: 1100px), with no min-width floor. Do not add that
   floor back. Do not turn this sheet into display:block cards. Narrow
   widths keep this table and the full description row. */
@media (max-width: 1100px) {
  body.hub-page table.inventory-sheet {
    width: 100%;
    max-width: 100%;
  }

  body.hub-page > table.inventory-sheet ~ table.footer,
  body.hub-page > table.inventory-sheet ~ table.site-footer {
    width: 100%;
    max-width: 100%;
  }

  body.hub-page table.inventory-sheet col.inv-col-type { width: calc(168 / 620 * 100%) !important; }
  body.hub-page table.inventory-sheet col.inv-col-terr { width: calc(124 / 620 * 100%) !important; }
  body.hub-page table.inventory-sheet col.inv-col-level { width: calc(76 / 620 * 100%) !important; }
  body.hub-page table.inventory-sheet col.inv-col-rarity { width: calc(100 / 620 * 100%) !important; }
  body.hub-page table.inventory-sheet col.inv-col-benefits { width: 0 !important; }
  body.hub-page table.inventory-sheet col.inv-col-price { width: calc(76 / 620 * 100%) !important; }
  body.hub-page table.inventory-sheet col.inv-col-buy { width: calc(76 / 620 * 100%) !important; }

  body.hub-page table.inventory-sheet col.inv-col-benefits,
  body.hub-page table.inventory-sheet td.inv-benefits,
  body.hub-page table.inventory-sheet tr.hub-card > td.inv-benefits {
    visibility: collapse;
  }

  /* visibility:collapse still counts the cell toward the row height.
     The column is width 0, so a long description would stack one
     character per line and stretch the item row. Drop that copy out of
     flow. The full-width row below is the visible description. Keep
     the cell so later columns stay put under table-layout:fixed. */
  body.hub-page table.inventory-sheet td.inv-benefits,
  body.hub-page table.inventory-sheet tr.hub-card > td.inv-benefits {
    padding: 0 !important;
    font-size: 0 !important;
    line-height: 0 !important;
    overflow: hidden;
  }

  body.hub-page table.inventory-sheet tr.hub-card > td.inv-benefits > * {
    display: none !important;
  }

  body.hub-page table.inventory-sheet tr.inv-benefits-row {
    display: table-row;
  }

  /* A double rule around the item row plus its description row, same
     idea as Hotel Room and the dashboard gunfighter tables. The fill
     stays the inline row color from the page. */
  body.hub-page table.inventory-sheet tr.hub-card > td {
    border-top: 3px double var(--site-border);
  }

  body.hub-page table.inventory-sheet tr.hub-card > td:first-child {
    border-left: 3px double var(--site-border);
  }

  body.hub-page table.inventory-sheet tr.hub-card > td:last-child {
    border-right: 3px double var(--site-border);
  }

  body.hub-page table.inventory-sheet tr.inv-benefits-row > td {
    border-top: 1px solid var(--site-border);
    border-bottom: 3px double var(--site-border);
    border-left: 3px double var(--site-border);
    border-right: 3px double var(--site-border);
    padding: 8px 10px;
  }
}

/* Jack D's previous reports (/intel/results.php). Same ladder as Hotel
   Room and gear inventory: wide screens stay at 80%, narrower desktops
   grow toward 1180px before Results compresses, and the table never
   exceeds the viewport. Fixed columns are pixel widths that add up to
   604px (report 88, agent 120, target 200, observation 196). Results is
   the remainder and has no min-width. min-width: 0 lets the table shrink
   inside gf.css's flex column; it is not a floor. */
body.hub-page table.intel-sheet {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  table-layout: fixed;
}

/* Title, summary, and footer use that same ladder. gf.css leaves the
   first two at 80%, and the footer is min(1165px, 96%), so on a
   narrower desktop they were a different width from the reports.
   min-width: 0 lets them shrink inside the flex column. */
body.hub-page.intel-results > table.intel-title,
body.hub-page.intel-results > table.intel-summary,
body.hub-page.intel-results > table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
}

body.hub-page.intel-results > table.intel-title th,
body.hub-page.intel-results > table.intel-summary th,
body.hub-page.intel-results > table.site-footer td {
  overflow-wrap: break-word;
  word-break: normal;
}

body.hub-page table.intel-sheet th,
body.hub-page table.intel-sheet td,
body.hub-page table.intel-sheet .intel-results {
  overflow-wrap: break-word;
  word-break: normal;
}

/* Results stays in the report row above the demotion gate. The matching
   full-width row is in the markup for the mid band. This sheet has no
   per-report stripe, so that row stays transparent and shares the panel
   with the report row above it. */
body.hub-page table.intel-sheet tr.intel-results-row {
  display: none;
}

/* Activity-safe ladder (same as dashboard Equipped Gear, Hotel Room, and
   inventory): the width rule above is not a media query. The demotion IS
   a viewport @media (max-width: 1100px), with no min-width floor. Do not
   add that floor back. Do not turn this sheet into display:block cards.
   Narrow widths keep this table and the full-width Results row. */
@media (max-width: 1100px) {
  body.hub-page table.intel-sheet {
    width: 100%;
    max-width: 100%;
  }

  body.hub-page.intel-results > table.intel-title,
  body.hub-page.intel-results > table.intel-summary,
  body.hub-page.intel-results > table.site-footer {
    width: 100%;
    max-width: 100%;
  }

  body.hub-page table.intel-sheet col.intel-col-report { width: calc(88 / 604 * 100%) !important; }
  body.hub-page table.intel-sheet col.intel-col-agent { width: calc(120 / 604 * 100%) !important; }
  body.hub-page table.intel-sheet col.intel-col-target { width: calc(200 / 604 * 100%) !important; }
  body.hub-page table.intel-sheet col.intel-col-results { width: 0 !important; }
  body.hub-page table.intel-sheet col.intel-col-observed { width: calc(196 / 604 * 100%) !important; }

  body.hub-page table.intel-sheet col.intel-col-results,
  body.hub-page table.intel-sheet th.intel-results,
  body.hub-page table.intel-sheet tr.hub-card > td.intel-results {
    visibility: collapse;
  }

  /* visibility:collapse still counts the cell toward the row height.
     The column is width 0, so a long Results string would stack one
     character per line and stretch the report row. Drop that copy out
     of flow. The full-width row below is the visible Results text.
     Keep the cell so the observation column stays put under
     table-layout:fixed. */
  body.hub-page table.intel-sheet th.intel-results,
  body.hub-page table.intel-sheet tr.hub-card > td.intel-results {
    padding: 0 !important;
    font-size: 0 !important;
    line-height: 0 !important;
    overflow: hidden;
  }

  body.hub-page table.intel-sheet tr.hub-card > td.intel-results > * {
    display: none !important;
  }

  body.hub-page table.intel-sheet tr.intel-results-row {
    display: table-row;
  }

  /* A double rule around the report row plus its Results row, same idea
     as Hotel Room and the dashboard gunfighter tables. The fill stays
     the sheet panel, which is also the report row's fill. */
  body.hub-page table.intel-sheet tr.hub-card > td {
    border-top: 3px double var(--site-border);
  }

  body.hub-page table.intel-sheet tr.hub-card > td:first-child {
    border-left: 3px double var(--site-border);
  }

  body.hub-page table.intel-sheet tr.hub-card > td:last-child {
    border-right: 3px double var(--site-border);
  }

  body.hub-page table.intel-sheet tr.intel-results-row > td {
    border-top: 1px solid var(--site-border);
    border-bottom: 3px double var(--site-border);
    border-left: 3px double var(--site-border);
    border-right: 3px double var(--site-border);
    background: transparent;
    padding: 8px 10px;
  }
}

/* How Duels Work (/tutorials/how-duels-work.php). Same grow ladder as
   Hotel Room, inventory, and reports: wide screens stay at 80%, narrower
   desktops grow toward 1180px, then the sheet compresses with the viewport.
   min-width: 0 lets the table shrink inside gf.css's flex column; it is
   not a floor. No min-width that would force a scrollbar in the Activity iframe. */
body.hub-page.duel-guide > table.duel-guide-sheet,
body.hub-page.duel-guide > table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  margin-left: auto;
  margin-right: auto;
}

body.hub-page.duel-guide > table.duel-guide-sheet {
  table-layout: fixed;
}

body.hub-page.duel-guide > table.duel-guide-sheet th,
body.hub-page.duel-guide > table.duel-guide-sheet td {
  min-width: 0;
  overflow-wrap: anywhere;
}

body.hub-page.duel-guide h1.duel-guide-title {
  display: inline;
  margin: 0;
  color: inherit;
  font: inherit;
  font-size: 1.5em;
  font-weight: bold;
  line-height: 1.25;
}

body.hub-page.duel-guide .duel-guide-sheet h2 {
  margin: 1.15rem 0 0.45rem;
  color: inherit;
  font: inherit;
  font-size: 1.25em;
  font-weight: bold;
  line-height: 1.3;
}

body.hub-page.duel-guide .duel-guide-sheet p {
  margin: 0.7rem 0;
}

body.hub-page.duel-guide .duel-guide-sheet blockquote.terms-summary {
  margin: 0.8rem 0;
  padding: 0.2rem 0.9rem;
  border-left: 4px solid var(--site-border);
}

body.hub-page.duel-guide .duel-guide-sheet ul {
  margin: 0.35rem 0 0.8rem;
  padding-left: 1.4em;
}

body.hub-page.duel-guide .duel-guide-sheet li {
  margin: 0.35rem 0;
}

body.hub-page.duel-guide tr.duel-guide-foot td {
  overflow: hidden;
  text-align: right;
}

body.hub-page.duel-guide .duel-guide-video {
  max-width: 100%;
  min-width: 0;
  margin: 1rem 0 0.25rem;
}

/* Section gates reuse the see-more button. They span the sheet and wrap
   instead of using the inline chip's nowrap width, which would scroll. */
body.hub-page.duel-guide .gf-collapse-section {
  display: block;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  margin: 0.9rem 0;
}

body.hub-page.duel-guide .gf-collapse-section > button.gf-collapse-btn {
  display: block !important;
  box-sizing: border-box;
  width: 100% !important;
  max-width: 100% !important;
  min-width: 0 !important;
  margin: 0 !important;
  padding: 8px 12px !important;
  white-space: normal !important;
  overflow-wrap: anywhere;
  text-align: center;
  font-size: 0.95em !important;
  line-height: 1.35 !important;
}

body.hub-page.duel-guide .duel-guide-video iframe {
  display: block;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  height: auto;
  min-height: 200px;
  aspect-ratio: 16 / 9;
  border: 0;
}

/* Previous Duel AARs (/duel/previous-aars.php). Same grow ladder as
   How Duels Work, Hotel Room, and reports: wide screens stay at 80%,
   narrower desktops grow toward 1180px, then the sheet is the full
   width of the viewport. Columns are auto, not fixed rem tracks. A
   fixed track that cannot yield leaves Opponent at zero once Date,
   Race, and the two buttons no longer fit. Auto layout sizes each
   column between its longest word and its one-line text, so the date
   drops to two lines, a clan name wraps, and the buttons stack before
   a name is broken. min-width: 0 on the table lets it shrink inside
   gf.css's flex column; it is not a column floor. No min-width that
   would force a scrollbar in the Activity iframe. */
body.hub-page.aar-history > table.aar-sheet,
body.hub-page.aar-history > table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  margin-left: auto;
  margin-right: auto;
}

body.hub-page.aar-history > table.aar-sheet {
  table-layout: auto;
}

body.hub-page.aar-history > table.aar-sheet th,
body.hub-page.aar-history > table.aar-sheet td {
  /* middle keeps the button on the same line as the row text. top
     left the taller button hanging toward the next row's border. */
  vertical-align: middle;
  /* normal wraps at spaces and at <wbr>. break-word and anywhere were
     splitting Opponent, Level, and Race in the middle of a word. */
  overflow-wrap: normal;
  word-break: normal;
  hyphens: manual;
}

body.hub-page.aar-history > table.aar-sheet th.aar-level,
body.hub-page.aar-history > table.aar-sheet td.aar-level,
body.hub-page.aar-history > table.aar-sheet th.aar-race,
body.hub-page.aar-history > table.aar-sheet td.aar-race {
  text-align: center;
  white-space: nowrap;
}

/* Date/Time uses the same type as the other headers, and the stamp uses
   the same type as the rest of the row. The header stays one line while
   the column is wide. The cell itself may wrap, but only between the
   date and the time: each of those is nowrap, so Sep and 2026 stay whole. */
body.hub-page.aar-history > table.aar-sheet th.aar-when {
  color: var(--site-ink);
  text-align: left;
  white-space: nowrap;
}

body.hub-page.aar-history > table.aar-sheet td.aar-when {
  color: var(--site-ink);
  text-align: left;
  white-space: normal;
}

/* Each stamp wraps on its own. Date and time are inline-blocks that
   refuse to shrink, so a short stamp stays "Sep 30, 2026 / 12:59 PM ET"
   while a narrower cell in the same column puts the time on the next
   line. Inline-blocks participate in the cell's line boxes, so the row
   stays as tall as the text and the buttons. A flex line inside the
   cell was a second formatting context and fought the table. The slash
   is painted in the date's padding and hangs off the left of the time.
   When the time starts a new line, that hang falls outside the stack
   and overflow clips it. A column container query would wrap every row
   at the same width, including a short stamp that still fits. */
body.hub-page.aar-history .aar-when-stack {
  display: block;
  max-width: 100%;
  overflow: hidden;
}

body.hub-page.aar-history .aar-when-date,
body.hub-page.aar-history .aar-when-time {
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: normal;
  vertical-align: baseline;
}

body.hub-page.aar-history .aar-when-date {
  padding-right: 0.95em;
  box-sizing: border-box;
}

body.hub-page.aar-history .aar-when-time {
  position: relative;
}

body.hub-page.aar-history .aar-when-time::before {
  content: "/";
  position: absolute;
  right: calc(100% + 0.32em);
}

/* Opponent header is a phrase, left aligned (th is centered by default).
   The name and "(skill path)" are inline. A space between them is the
   wrap point, so a path that does not fit starts the next line flush
   left, and a path that fits stays on the name's line. nowrap on the
   path keeps "(Guts/bravado)" together; the name still breaks on spaces.
   An empty path is omitted, so there are no empty parentheses. */
body.hub-page.aar-history > table.aar-sheet th.aar-opponent {
  text-align: left;
}

body.hub-page.aar-history .aar-opponent-fit {
  display: block;
  max-width: 100%;
}

body.hub-page.aar-history .aar-opponent-name,
body.hub-page.aar-history .aar-opponent-path {
  max-width: 100%;
  overflow-wrap: normal;
  word-break: normal;
}

body.hub-page.aar-history .aar-opponent-name {
  white-space: normal;
}

/* The path is an atomic inline box, so "(Guts/Resistance)" drops to
   the next line whole before a name is split. max-width lets that box
   shrink to the cell, and then the <wbr> after the slash is the only
   break inside it. A name still breaks only at spaces. */
body.hub-page.aar-history .aar-opponent-path {
  display: inline-block;
  vertical-align: baseline;
  white-space: normal;
}

/* Fought time under the AAR headline. Same Eastern label as the list.
   site-ink is the dark brown that reads on the tan panel. */
.aar-fought {
  display: block;
  margin: 0.2rem 0 0;
  color: var(--site-ink);
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.35;
  text-shadow: none;
}

body.hub-page.aar-history h1.aar-title {
  display: inline;
  margin: 0;
  color: inherit;
  font: inherit;
  font-size: 1.5em;
  font-weight: bold;
  line-height: 1.25;
}

body.hub-page.aar-history .aar-whose {
  clear: both;
  margin-top: 0.35rem;
  font-weight: bold;
}

body.hub-page.aar-history tr.aar-foot td {
  overflow: hidden;
  text-align: right;
}

/* Side by side while the sheet has room. They stack only once the date
   has already gone to two lines and the opponent column would otherwise
   be squeezed down to a single word. nowrap on the label keeps
   "View Duel Summary" whole until the smallest step. */
body.hub-page.aar-history .aar-actions {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
}

body.hub-page.aar-history .aar-actions > a.site-btn-nav {
  flex: 0 0 auto;
  width: auto;
  max-width: none;
  white-space: nowrap;
}

body.hub-page.aar-history button.aar-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  min-width: 2.1rem;
  margin: 0;
  padding: 0;
  line-height: 1;
  flex: 0 0 auto;
  white-space: nowrap;
}

body.hub-page.aar-history button.aar-copy .aar-copy-icon {
  width: 1rem;
  height: 1rem;
  display: block;
}

/* Activity-safe ladder (same as How Duels Work and the reports sheet):
   the width rule above is not a media query. Below 1100px the sheet is
   the full width of the viewport. Columns keep sharing that width.
   No min-width on this query. */
@media (max-width: 1100px) {
  body.hub-page.aar-history > table.aar-sheet,
  body.hub-page.aar-history > table.site-footer {
    width: 100%;
    max-width: 100%;
  }
}

/* Pistol tuning table (/gear/pistols.php). A fragment link scrolls to the
   equipped pistol. The wash is the parchment hover gold, held lightly so
   the row stays readable. scroll-margin clears a sticky phone return. */
body.hub-page table.pistol-skills tr.hub-card[id] {
  scroll-margin-top: 4.5rem;
}

body.hub-page table.pistol-skills tr.hub-card:target,
body.hub-page table.pistol-skills tr.hub-card:target > td {
  background-color: #f3ddb4;
  animation: pistol-row-target 1.2s ease;
}

@keyframes pistol-row-target {
  from { background-color: #ffe8b8; }
  to { background-color: #f3ddb4; }
}

body.hub-page tr.pistol-foot td {
  overflow: hidden;
  text-align: right;
}

body.hub-page table.pistol-skills tr.pistol-title > td {
  overflow: hidden;
  text-align: left;
}

/* Title, equipped note, and the italic intro are rows of the skills
   table. A double rule separates those sections. The title keeps the
   panel fill the old title box used. */
body.hub-page table.pistol-skills tr.pistol-title > td,
body.hub-page table.pistol-skills tr.pistol-equipped > td,
body.hub-page table.pistol-skills tr.pistol-intro > td {
  border-top: 3px double var(--site-border);
  border-bottom: 3px double var(--site-border);
  background: var(--site-panel);
  padding: 10px 12px;
}

/* Pistol tuning (/gear/pistols.php). One content table, plus the
   copyright line. Same ladder as Hotel Room, inventory, and espionage
   reports. Wide screens stay at 80%. Narrower desktops grow toward
   1180px, and the table never exceeds the viewport, before a column
   compresses. min-width: 0 lets the table shrink inside gf.css's flex
   column; it is not a floor. Do not put a pixel min-width on it.
   Fixed tracks are name 210, fights 110, magnitude 92, conditions 200.
   Experience Benefit is the remainder and has no pixel width, so it is
   the only column that gives up space, and only once the table is
   already the full viewport. */
body.hub-page.pistol-tuning > table {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  margin-left: auto;
  margin-right: auto;
}

body.hub-page.pistol-tuning > table.pistol-skills {
  table-layout: fixed;
}

body.hub-page.pistol-tuning > table th,
body.hub-page.pistol-tuning > table td {
  overflow-wrap: break-word;
  word-break: normal;
}

/* Same 1100px handoff as Hotel Room, inventory, and the reports page.
   Below it every table is the full viewport width. The skills columns
   stay on their pixel tracks; Benefit absorbs the shrink. */
@media (max-width: 1100px) {
  body.hub-page.pistol-tuning > table {
    width: 100%;
    max-width: 100%;
  }
}

/* ---------- Phone-width manage hubs + path to a duel ----------
   Same 800px gate as the dashboard. Desktop tables stay.
   .hub-cols (column titles) hide only inside the breakpoint.
   .hub-card rows become labeled stacks via data-label.
   .hub-return keeps the floated dashboard link on wide screens. */

.hub-return {
  float: right;
}

.hub-return > a + a {
  margin-left: 0.45rem;
}

@media (max-width: 800px) {
  body.hub-page.site-page,
  body.path-page.site-page {
    margin: 0;
    padding: 0;
    width: auto;
    min-width: 0;
  }

  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet),
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted),
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > thead,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) > thead,
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > tbody,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) > tbody,
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > tr,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) > tr,
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > thead > tr,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) > thead > tr,
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > tbody > tr,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) > tbody > tr,
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) th,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) th,
  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) td,
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) td {
    display: block;
    width: 100% !important;
    max-width: 100%;
    box-sizing: border-box;
    text-align: left;
  }

  body.hub-page table:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet),
  body.path-page table:not(.footer):not(.site-footer):not(.duel-accepted) {
    margin-left: 0;
    margin-right: 0;
    border-radius: 0;
  }

  body.hub-page tr.hub-cols,
  body.path-page tr.hub-cols {
    display: none !important;
  }

  body.hub-page tr.spacerRow,
  body.path-page tr.spacerRow {
    display: none !important;
  }

  body.hub-page tr.hub-card,
  body.path-page tr.hub-card {
    width: auto !important;
    margin: 10px;
    padding: 4px 8px 8px;
    border: 2px solid var(--site-border);
    border-radius: var(--site-radius);
    background: var(--site-panel);
    box-shadow: var(--site-shadow), var(--site-inset);
  }

  /* Pistol tuning cards. The shared hub-card rule is width 100% of the
     table plus a 10px margin. The table border is 2px, so the row's
     right edge lands 8px past a 420px Activity iframe. Keep the inset
     and take the margin out of the width. */
  body.hub-page table.pistol-skills:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > tr.hub-card,
  body.hub-page table.pistol-skills:not(.footer):not(.site-footer):not(.equip-sheet):not(.inventory-sheet):not(.intel-sheet):not(.aar-sheet) > tbody > tr.hub-card {
    width: calc(100% - 20px) !important;
    max-width: calc(100% - 20px);
    margin-left: 10px;
    margin-right: 10px;
  }

  /* Title, equipped note, and intro are full-width blocks above
     the skill cards, not inset cards. */
  body.hub-page table.pistol-skills > tr.pistol-title,
  body.hub-page table.pistol-skills > tr.pistol-equipped,
  body.hub-page table.pistol-skills > tr.pistol-intro,
  body.hub-page table.pistol-skills > tr.pistol-title > td,
  body.hub-page table.pistol-skills > tr.pistol-equipped > td,
  body.hub-page table.pistol-skills > tr.pistol-intro > td {
    width: 100% !important;
    max-width: 100%;
    margin-left: 0;
    margin-right: 0;
    box-sizing: border-box;
  }

  body.hub-page tr.hub-card > td,
  body.hub-page tr.hub-card > th,
  body.path-page tr.hub-card > td,
  body.path-page tr.hub-card > th {
    border: 0;
    border-bottom: 1px solid rgba(107, 62, 31, 0.22);
    padding: 8px 4px;
    text-align: left !important;
  }

  body.hub-page tr.hub-card > td:last-child,
  body.hub-page tr.hub-card > th:last-child,
  body.path-page tr.hub-card > td:last-child,
  body.path-page tr.hub-card > th:last-child {
    border-bottom: 0;
  }

  body.hub-page tr.hub-card form,
  body.path-page tr.hub-card form {
    width: 100%;
    margin: 0;
  }

  body.hub-page tr.hub-card > td[data-label]::before,
  body.hub-page tr.hub-card > th[data-label]::before,
  body.path-page tr.hub-card > td[data-label]::before,
  body.path-page tr.hub-card > th[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-weight: bold;
    color: var(--site-muted);
  }

  body.hub-page tr.hub-banner > td,
  body.hub-page tr.hub-banner > th,
  body.path-page tr.hub-banner > td,
  body.path-page tr.hub-banner > th {
    padding: 12px 10px;
    background: var(--site-th);
  }

  body.hub-page tr.hub-card img,
  body.path-page tr.hub-card img,
  body.hub-page img,
  body.path-page img {
    max-width: 100%;
    height: auto;
  }

  body.hub-page tr.hub-card img,
  body.path-page tr.hub-card img {
    max-width: 96px;
  }

  body.hub-page .hub-return,
  body.path-page .hub-return {
    float: none;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    margin: 8px 0 0;
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    z-index: 30;
    background: var(--site-panel-solid);
    padding: calc(8px + env(safe-area-inset-top)) 8px 8px;
    border-bottom: 2px solid var(--site-border);
    box-shadow: var(--site-shadow);
  }

  body.hub-page .hub-return > a,
  body.path-page .hub-return > a,
  body.hub-page .hub-return > a + a,
  body.path-page .hub-return > a + a {
    margin-left: 0;
    width: 100%;
  }

  body.hub-page .site-btn-row,
  body.path-page .site-btn-row,
  body.path-page .mm-create-row,
  body.path-page .lobby-chat-form {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    gap: 8px;
  }

  body.hub-page a.site-btn,
  body.hub-page a.site-btn-nav,
  body.hub-page button,
  body.hub-page input[type="submit"],
  body.hub-page input[type="button"],
  body.path-page a.site-btn,
  body.path-page a.site-btn-nav,
  body.path-page button,
  body.path-page input[type="submit"],
  body.path-page input[type="button"],
  body.path-page .mm-create-btn {
    display: block;
    width: 100% !important;
    box-sizing: border-box;
    min-height: 44px;
    margin-top: 6px;
    white-space: normal;
    text-align: center;
  }

  body.path-page table.duel-accepted button,
  body.path-page table.duel-accepted input[type="submit"],
  body.path-page table.duel-accepted input[type="button"] {
    display: inline-block;
    width: auto !important;
    min-width: 0;
    min-height: 0;
    margin-top: 0;
  }

  /* Full-width Buy still has a definite box for the reason tip. Card
     rows (pistol sheet) are not the last table column, so the tip
     grows to the right. Inventory stays a table and keeps the
     right-aligned tip from the base rule. */
  body.hub-page .buy-why {
    display: block;
    width: 100%;
  }

  body.hub-page table:not(.inventory-sheet) .buy-why-tip {
    left: 0;
    right: auto;
  }

  body.hub-page input[type="text"],
  body.hub-page input[type="search"],
  body.hub-page input[type="email"],
  body.hub-page input[type="number"],
  body.hub-page input:not([type]),
  body.hub-page select,
  body.hub-page textarea,
  body.path-page input[type="text"],
  body.path-page input[type="search"],
  body.path-page input[type="email"],
  body.path-page input[type="number"],
  body.path-page input:not([type]),
  body.path-page select,
  body.path-page textarea {
    display: block;
    width: 100% !important;
    max-width: 100%;
    box-sizing: border-box;
    font-size: 16px;
    min-height: 44px;
    margin-top: 6px;
  }

  body.hub-page input[type="checkbox"],
  body.hub-page input[type="radio"],
  body.hub-page input[type="hidden"],
  body.path-page input[type="checkbox"],
  body.path-page input[type="radio"],
  body.path-page input[type="hidden"] {
    display: inline-block;
    width: auto !important;
    min-height: 0;
    margin-top: 0;
    font-size: inherit;
  }

  body.hub-page a.site-qty-btn,
  body.path-page a.site-qty-btn {
    display: inline-flex;
    width: auto !important;
    min-width: 44px;
    margin-top: 4px;
  }

  body.hub-page tr.site-btn-grid > td,
  body.path-page tr.site-btn-grid > td {
    padding: 4px 10px;
  }

  body.hub-page #livesearch a,
  body.path-page #livesearch a {
    display: block;
    padding: 10px 4px;
    min-height: 44px;
  }

  body.hub-page .popup .popuptext,
  body.path-page .popup .popuptext {
    width: min(260px, 86vw);
    left: 0;
    margin-left: 0;
  }

  /* Keep real table rows on MatchMaker. display:block on tr/td
     zeroes the panels in the Activity iframe. */
  body.mm-page table:not(.footer):not(.site-footer) {
    display: table;
    width: 100% !important;
  }

  body.mm-page table:not(.footer):not(.site-footer) > thead,
  body.mm-page table:not(.footer):not(.site-footer) > tbody {
    display: table-row-group;
  }

  body.mm-page table:not(.footer):not(.site-footer) > tr,
  body.mm-page table:not(.footer):not(.site-footer) > thead > tr,
  body.mm-page table:not(.footer):not(.site-footer) > tbody > tr,
  body.mm-page tr.hub-cols {
    display: table-row !important;
  }

  body.mm-page table:not(.footer):not(.site-footer) th,
  body.mm-page table:not(.footer):not(.site-footer) td {
    display: table-cell;
    width: auto !important;
  }

  /* Opponent list stays a real table at phone width. display:block on
     tr/td zeroes the sheet in the Activity iframe and makes a wide
     min-content floor. Fixed columns wrap instead of scrolling. */
  body.path-page.challenge-search table.private-opponents:not(.footer):not(.site-footer) {
    display: table;
    width: 100% !important;
    max-width: 100%;
    table-layout: fixed;
    margin-left: 0;
    margin-right: 0;
  }

  body.path-page.challenge-search table.private-opponents:not(.footer):not(.site-footer) > tr,
  body.path-page.challenge-search table.private-opponents tr.hub-cols,
  body.path-page.challenge-search table.private-opponents tr.hub-card,
  body.path-page.challenge-search table.private-opponents tr.hub-banner,
  body.path-page.challenge-search table.private-opponents tr.private-opponents-pager {
    display: table-row !important;
    width: auto !important;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }

  body.path-page.challenge-search table.private-opponents:not(.footer):not(.site-footer) th,
  body.path-page.challenge-search table.private-opponents:not(.footer):not(.site-footer) td {
    display: table-cell;
    width: auto !important;
    max-width: none;
    text-align: left;
    white-space: normal;
  }

  body.path-page.challenge-search table.private-opponents tr.hub-card > td[data-label]::before,
  body.path-page.challenge-search table.private-opponents tr.hub-card > th[data-label]::before {
    content: none;
    display: none;
  }

  body.path-page.challenge-search table.private-opponents a.site-btn-nav {
    display: inline-block;
    width: auto !important;
    min-width: 0;
    min-height: 44px;
    margin-top: 0;
    white-space: nowrap;
    padding: 8px 6px;
  }

  /* Hotel Room, gear inventory, and Jack D's reports stay tables at
     phone width too. display:block on tr/td zeroes this sheet in the Activity iframe,
     hides the demoted row, and clips see more. The 1100px demotion
     above still applies. Selectors repeat :not() so they beat the card
     rules' specificity. Inventory row color stays on the inline
     background; this reset has no !important, so zebra and owned
     shading still paint both rows of a pair. Reports have no per-row
     stripe, so the same transparent reset keeps Results on the report
     row's panel. */
  body.hub-page table.equip-sheet:not(.footer):not(.site-footer),
  body.hub-page table.inventory-sheet:not(.footer):not(.site-footer),
  body.hub-page table.intel-sheet:not(.footer):not(.site-footer) {
    display: table;
    width: 100% !important;
    max-width: 100%;
    table-layout: fixed;
  }

  body.hub-page table.equip-sheet:not(.footer):not(.site-footer) > tr,
  body.hub-page table.equip-sheet:not(.footer):not(.site-footer) > tbody > tr,
  body.hub-page table.equip-sheet tr.hub-cols,
  body.hub-page table.equip-sheet tr.hub-card,
  body.hub-page table.equip-sheet tr.equip-effects-row,
  body.hub-page table.equip-sheet tr.equip-foot,
  body.hub-page table.inventory-sheet:not(.footer):not(.site-footer) > tr,
  body.hub-page table.inventory-sheet:not(.footer):not(.site-footer) > tbody > tr,
  body.hub-page table.inventory-sheet tr.hub-cols,
  body.hub-page table.inventory-sheet tr.hub-card,
  body.hub-page table.inventory-sheet tr.inv-benefits-row,
  body.hub-page table.intel-sheet:not(.footer):not(.site-footer) > tr,
  body.hub-page table.intel-sheet:not(.footer):not(.site-footer) > tbody > tr,
  body.hub-page table.intel-sheet tr.hub-cols,
  body.hub-page table.intel-sheet tr.hub-card,
  body.hub-page table.intel-sheet tr.intel-results-row {
    display: table-row !important;
    width: auto !important;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }

  body.hub-page table.equip-sheet:not(.footer):not(.site-footer) th,
  body.hub-page table.equip-sheet:not(.footer):not(.site-footer) td,
  body.hub-page table.inventory-sheet:not(.footer):not(.site-footer) th,
  body.hub-page table.inventory-sheet:not(.footer):not(.site-footer) td,
  body.hub-page table.intel-sheet:not(.footer):not(.site-footer) th,
  body.hub-page table.intel-sheet:not(.footer):not(.site-footer) td {
    display: table-cell !important;
    max-width: none;
    text-align: left;
  }

  /* The hub-card stack above sets border:0 on every hub-card cell.
     These three sheets opted out of becoming cards, so that reset was
     wiping the vertical rules on item rows while hub-cols headers
     (not hub-card) and the Effects row (its own borders) kept theirs.
     Restore the grid with longhands. The 1100px double top, and the
     first/last double sides, are more specific and stay. */
  body.hub-page table.equip-sheet tr.hub-card > td,
  body.hub-page table.inventory-sheet tr.hub-card > td,
  body.hub-page table.intel-sheet tr.hub-card > td {
    border-right: 1px solid var(--site-border);
    border-bottom: 1px solid var(--site-border);
    border-left: 1px solid var(--site-border);
  }

  /* 14px and 2px padding, with the short columns widened, keep Level,
     Owned, Equipped, Equip, Unequip, SELL, and "uncommon" in one piece
     down through a 640px window. Name and Territory still wrap at spaces.
     Hotel Room never becomes cards, so this is still table layout. */
  body.hub-page table.equip-sheet,
  body.hub-page table.inventory-sheet,
  body.hub-page table.intel-sheet {
    font-size: 0.875rem;
  }

  body.hub-page table.equip-sheet th,
  body.hub-page table.equip-sheet td,
  body.hub-page table.inventory-sheet th,
  body.hub-page table.inventory-sheet td,
  body.hub-page table.intel-sheet th,
  body.hub-page table.intel-sheet td {
    padding: 2px;
  }

  body.hub-page table.equip-sheet col.equip-col-type { width: calc(50 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-name { width: calc(86 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-level { width: calc(46 / 640 * 100%) !important; }
  /* 8 shares move from Rarity to Territory. At 681px Southwest was
     one pixel inside the cell, and Rarity still held uncommon. */
  body.hub-page table.equip-sheet col.equip-col-rarity { width: calc(76 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-effects { width: 0 !important; }
  body.hub-page table.equip-sheet col.equip-col-terr { width: calc(80 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-owned { width: calc(56 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equipped { width: calc(74 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equip { width: calc(48 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-unequip { width: calc(66 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-sell { width: calc(58 / 640 * 100%) !important; }

  body.hub-page table.equip-sheet button,
  body.hub-page table.inventory-sheet button {
    padding: 2px 4px;
    font-size: 0.875rem;
  }

  /* The hub min-height would stretch SELL to 44px, and a 44px square
     is wider than the old + and - controls. Drop it on this sheet so
     SELL is its label height and the square matches. 0.875rem line box
     plus 2px vertical padding and the 2px border. width is !important
     because the hub qty rule sets width: auto !important. */
  body.hub-page table.equip-sheet button {
    min-height: 0;
    margin-top: 0;
  }

  body.hub-page table.equip-sheet a.site-qty-btn {
    min-width: 0;
    min-height: 0;
    width: calc(1.25 * 0.875rem + 8px) !important;
    height: calc(1.25 * 0.875rem + 8px);
    max-width: none;
    margin-top: 0;
  }

  body.hub-page table.equip-sheet tr.equip-foot td,
  body.hub-page tr.pistol-foot td {
    text-align: right !important;
  }

  /* Buy New Gear stays a button. The scrip note stays on its right and
     wraps instead of stretching the sheet past the Activity iframe. */
  body.hub-page table.equip-sheet tr.hub-banner .site-btn-row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  body.hub-page table.equip-sheet tr.hub-banner .site-btn-row > a.site-btn {
    display: inline-flex;
    width: auto !important;
    max-width: 100%;
    margin-top: 0;
    white-space: normal;
  }

  body.hub-page table.equip-sheet b.equip-scrip {
    flex: 1 1 8rem;
    min-width: 0;
  }

  body.hub-page table.equip-sheet .hub-return {
    box-sizing: border-box;
    max-width: 100%;
    width: 100%;
  }

  body.hub-page table.equip-sheet tr.hub-card > td[data-label]::before,
  body.hub-page table.equip-sheet tr.hub-card > th[data-label]::before,
  body.hub-page table.inventory-sheet tr.hub-card > td[data-label]::before,
  body.hub-page table.inventory-sheet tr.hub-card > th[data-label]::before,
  body.hub-page table.intel-sheet tr.hub-card > td[data-label]::before,
  body.hub-page table.intel-sheet tr.hub-card > th[data-label]::before {
    content: none !important;
    display: none !important;
  }

  /* Previous Duel AARs stays a table at phone width too. display:block
     on tr/td zeroes this sheet in the Activity iframe. Columns stay
     auto, between a word and a line. The copy control stays beside the
     summary button until that pair would crush an opponent name. */
  body.hub-page table.aar-sheet:not(.footer):not(.site-footer) {
    display: table;
    width: 100% !important;
    max-width: 100%;
    table-layout: auto;
  }

  body.hub-page table.aar-sheet:not(.footer):not(.site-footer) > tr,
  body.hub-page table.aar-sheet:not(.footer):not(.site-footer) > tbody > tr,
  body.hub-page table.aar-sheet tr.hub-cols,
  body.hub-page table.aar-sheet tr.hub-card,
  body.hub-page table.aar-sheet tr.aar-foot {
    display: table-row !important;
    width: auto !important;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }

  body.hub-page table.aar-sheet:not(.footer):not(.site-footer) th,
  body.hub-page table.aar-sheet:not(.footer):not(.site-footer) td {
    display: table-cell !important;
    max-width: none;
    text-align: left;
  }

  /* The hub-card stack zeroes every cell border and drops the last
     cell's edge. This sheet stays a real table, so the outer frame
     and every cell border stay painted at phone width too. */
  body.hub-page.aar-history table.aar-sheet:not(.footer):not(.site-footer) {
    border: 2px solid var(--site-border);
  }

  body.hub-page.aar-history table.aar-sheet:not(.footer):not(.site-footer) th,
  body.hub-page.aar-history table.aar-sheet:not(.footer):not(.site-footer) td,
  body.hub-page.aar-history table.aar-sheet tr.hub-card > th,
  body.hub-page.aar-history table.aar-sheet tr.hub-card > td,
  body.hub-page.aar-history table.aar-sheet tr.hub-card > th:last-child,
  body.hub-page.aar-history table.aar-sheet tr.hub-card > td:last-child {
    border: 1px solid var(--site-border);
  }

  body.hub-page.aar-history table.aar-sheet th.aar-level,
  body.hub-page.aar-history table.aar-sheet td.aar-level,
  body.hub-page.aar-history table.aar-sheet th.aar-race,
  body.hub-page.aar-history table.aar-sheet td.aar-race {
    text-align: center !important;
  }

  body.hub-page.aar-history table.aar-sheet tr.aar-foot td {
    text-align: right !important;
  }

  body.hub-page table.aar-sheet tr.hub-card > td[data-label]::before,
  body.hub-page table.aar-sheet tr.hub-card > th[data-label]::before {
    content: none !important;
    display: none !important;
  }

  /* Card padding would make every row as tall as a stacked button.
     This sheet stays a table, so the cells keep the desktop padding. */
  body.hub-page.aar-history table.aar-sheet tr.hub-cols > th,
  body.hub-page.aar-history table.aar-sheet tr.hub-card > td,
  body.hub-page.aar-history table.aar-sheet tr.hub-banner > th,
  body.hub-page.aar-history table.aar-sheet tr.aar-foot > td {
    padding: 5px;
  }

  body.hub-page.aar-history table.aar-sheet .aar-actions > a.site-btn-nav,
  body.hub-page.aar-history table.aar-sheet button.aar-copy {
    display: inline-flex !important;
    width: auto !important;
    max-width: none;
    min-width: auto !important;
    margin-top: 0;
    white-space: nowrap;
    vertical-align: middle;
  }

  body.hub-page.aar-history table.aar-sheet button.aar-copy {
    width: 2.1rem !important;
    min-width: 2.1rem !important;
    height: 2.1rem;
    padding: 0;
  }

  body.path-page.mm-page .mm-stack,
  body.path-page.pbem-page .mm-stack,
  body.hub-page table.footer,
  body.hub-page table.site-footer,
  body.path-page table.footer,
  body.path-page table.site-footer {
    width: 100%;
    max-width: 100%;
    margin-left: 0;
    margin-right: 0;
  }

  body.hub-page,
  body.path-page {
    overflow-wrap: anywhere;
  }
}

/* Mobile type, matching the site's 800 / 520 steps. 14px, then 13px.
   Race may wrap at a space or at the hyphen in Half-Demon. The summary
   label may wrap at spaces once the sheet is at the smaller size, after
   the copy icon has already stacked under it. Date and time stay whole
   lines; only the slash between them goes away. */
@media (max-width: 800px) {
  body.hub-page.aar-history > table.aar-sheet {
    font-size: 0.875rem;
  }

  body.hub-page.aar-history > table.aar-sheet th.aar-race,
  body.hub-page.aar-history > table.aar-sheet td.aar-race {
    white-space: normal;
  }
}

/* Stack the summary above the copy icon before Opponent is down to one
   word. 640px is where a side-by-side pair would push that column under
   its longest word. */
@media (max-width: 640px) {
  body.hub-page.aar-history .aar-actions {
    flex-wrap: wrap;
  }
}

@media (max-width: 520px) {
  body.hub-page.aar-history > table.aar-sheet {
    font-size: 0.8125rem;
  }

  /* The panel's 12px side padding plus 5px cells hold the sheet at about
     405px. Tighter padding lets the same words fit in a 360px window. */
  body.hub-page.aar-history > table.site-panel.aar-sheet {
    padding: 6px 2px 8px;
  }

  body.hub-page.aar-history table.aar-sheet tr.hub-cols > th,
  body.hub-page.aar-history table.aar-sheet tr.hub-card > td,
  body.hub-page.aar-history table.aar-sheet tr.hub-banner > th,
  body.hub-page.aar-history table.aar-sheet tr.aar-foot > td {
    padding: 3px 2px;
  }

  body.hub-page.aar-history table.aar-sheet .aar-actions > a.site-btn-nav {
    display: inline-block !important;
    max-width: 100% !important;
    padding: 3px 6px;
    white-space: normal;
  }
}

/* A 360px window with a vertical scrollbar is about 345px of content.
   The sheet's min-content is a couple of pixels over that while the
   panel still has side padding, which is enough to open a horizontal
   scrollbar. The words still fit once that padding is gone. */
@media (max-width: 400px) {
  body.hub-page.aar-history > table.site-panel.aar-sheet {
    padding-left: 0;
    padding-right: 0;
  }
}

/* Leaderboards (/leaderboards/index.php). One sheet: title, the five
   board buttons, the "Showing" line, then the rankings. Same ladder as
   Previous Duel AARs. The old title box, the button grid, and the
   rankings table were separate 80% boxes, and the rankings table grew
   past them once a cell refused to wrap. */
body.lb-page > table.lb-sheet,
body.lb-page > table.footer,
body.lb-page > table.site-footer {
  width: min(100%, max(80%, 1180px));
  max-width: 100%;
  min-width: 0;
  margin-left: auto;
  margin-right: auto;
}

body.lb-page > table.lb-sheet {
  table-layout: fixed;
}

/* Shares that hold the longest single token in each column at 800px
   (Montgomery, Professional, Fights, Win/Loss, Half-Demon, a manager
   name) without splitting it. */
body.lb-page > table.lb-sheet col.lb-col-stat { width: 11%; }
body.lb-page > table.lb-sheet col.lb-col-name { width: 15%; }
body.lb-page > table.lb-sheet col.lb-col-level { width: 8%; }
body.lb-page > table.lb-sheet col.lb-col-fights { width: 8%; }
body.lb-page > table.lb-sheet col.lb-col-wins { width: 7%; }
body.lb-page > table.lb-sheet col.lb-col-path { width: 12%; }
body.lb-page > table.lb-sheet col.lb-col-race { width: 13%; }
body.lb-page > table.lb-sheet col.lb-col-class { width: 14%; }
body.lb-page > table.lb-sheet col.lb-col-manager { width: 12%; }

body.lb-page > table.lb-sheet th,
body.lb-page > table.lb-sheet td {
  overflow-wrap: break-word;
  word-break: normal;
}

body.lb-page tr.lb-title th,
body.lb-page tr.lb-boards td,
body.lb-page tr.lb-showing td,
body.lb-page tr.lb-return-row td {
  padding: 10px 12px;
}

/* The dashboard link floats on the title line while the line has room.
   The duplicate below is the full-width row used once they would touch. */
body.lb-page .lb-return-inline {
  float: right;
}

body.lb-page tr.lb-return-row,
body.lb-page tr.lb-cols-repeat {
  display: none;
}

/* The grid used to be 80% of the page, between two tables. Inside the
   sheet it fills the row. min-width: 0 lets a label wrap inside the
   track instead of widening the page. height: auto keeps a wrapped
   label from being clipped by the equal-row stretch. */
body.lb-page nav.site-btn-grid-boards {
  width: 100%;
  margin: 0;
  grid-auto-rows: auto;
}

body.lb-page nav.site-btn-grid-boards > a,
body.lb-page nav.site-btn-grid-boards > .site-btn,
body.lb-page nav.site-btn-grid-boards > .site-btn-nav {
  min-width: 0;
  max-width: 100%;
  height: auto;
  overflow: visible;
  white-space: normal;
}

@media (max-width: 1100px) {
  body.lb-page > table.lb-sheet,
  body.lb-page > table.footer,
  body.lb-page > table.site-footer {
    width: 100%;
    max-width: 100%;
  }
}

/* Nine columns stop fitting without a mid-word break at 756px: Win/Loss,
   Fights, and Half-Demon are the first tokens to split. Below that each
   gunfighter is two lines of the same table. The first five cells sum
   to the full width, and the other four sum to the full width, so flex
   wraps them into two rows. A 3px rule separates records. The rule
   between a record's two lines stays 1px. The same split is repeated,
   in bold, every five gunfighters. Shares hold Win/Loss, Montgomery,
   Level, Fights, Wins, resistance, Buffalo Clan, Professional, and
   InnerLizard at 420px. Words wrap on spaces only. */
@media (max-width: 756px) {
  body.lb-page > table.lb-sheet,
  body.lb-page > table.lb-sheet > tbody {
    display: block;
    width: 100%;
  }

  body.lb-page tr.lb-title,
  body.lb-page tr.lb-boards,
  body.lb-page tr.lb-showing {
    display: block;
    width: 100%;
  }

  body.lb-page tr.lb-title > th,
  body.lb-page tr.lb-boards > td,
  body.lb-page tr.lb-showing > td {
    display: block;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--site-border);
  }

  body.lb-page tr.lb-cols,
  body.lb-page tr.lb-row {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
  }

  body.lb-page tr.lb-cols-repeat {
    display: flex;
  }

  body.lb-page tr.lb-cols > td,
  body.lb-page tr.lb-row > td {
    display: block;
    box-sizing: border-box;
    min-width: 0;
    max-width: 100%;
    padding: 3px 2px;
    overflow-wrap: normal;
    word-break: normal;
    border: 1px solid var(--site-border);
  }

  body.lb-page tr.lb-cols > td:nth-child(1),
  body.lb-page tr.lb-row > td:nth-child(1) { width: 22%; }
  body.lb-page tr.lb-cols > td:nth-child(2),
  body.lb-page tr.lb-row > td:nth-child(2) { width: 32%; }
  body.lb-page tr.lb-cols > td:nth-child(3),
  body.lb-page tr.lb-row > td:nth-child(3) { width: 14%; }
  body.lb-page tr.lb-cols > td:nth-child(4),
  body.lb-page tr.lb-row > td:nth-child(4) { width: 16%; }
  body.lb-page tr.lb-cols > td:nth-child(5),
  body.lb-page tr.lb-row > td:nth-child(5) { width: 16%; }

  body.lb-page tr.lb-cols > td:nth-child(6),
  body.lb-page tr.lb-row > td:nth-child(6) { width: 22%; }
  body.lb-page tr.lb-cols > td:nth-child(7),
  body.lb-page tr.lb-row > td:nth-child(7) { width: 26%; }
  body.lb-page tr.lb-cols > td:nth-child(8),
  body.lb-page tr.lb-row > td:nth-child(8) { width: 28%; }
  body.lb-page tr.lb-cols > td:nth-child(9),
  body.lb-page tr.lb-row > td:nth-child(9) { width: 24%; }

  body.lb-page tr.lb-cols > td:nth-child(-n+5),
  body.lb-page tr.lb-row > td:nth-child(-n+5) {
    border-top-width: 3px;
    border-bottom-width: 1px;
  }

  body.lb-page tr.lb-cols > td:nth-child(n+6),
  body.lb-page tr.lb-row > td:nth-child(n+6) {
    border-top-width: 1px;
    border-bottom-width: 3px;
  }
}

/* The floated dashboard link meets the title at 558px (the gap closes,
   and one pixel narrower the link drops under the title). From here it
   is its own row, a single button as wide as the sheet, just above the
   board buttons. Wider viewports keep the link on the title line. */
@media (max-width: 558px) {
  body.lb-page .lb-return-inline {
    display: none;
  }

  body.lb-page tr.lb-return-row {
    display: block;
    width: 100%;
  }

  body.lb-page tr.lb-return-row > td {
    display: block;
    width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--site-border);
  }

  body.lb-page tr.lb-return-row a.site-btn-nav {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
  }
}

/* Hotel Room stays a table below 800px (it is opted out of phone cards).
   At 13px the old Territory track was narrower than Southwest, and
   break-word put the last letter on its own line (Southwes / t around
   640px). This band uses the same shares as the 520px band, so
   Southwest and uncommon both fit, and Item Type and Item Name are
   wider than they were. Level, Equipped, Equip, and Unequip read as
   the short labels so those narrower tracks do not split a header.
   The +/− and SELL sizes stay on the rules below 520px. A pixel floor
   here would scroll the page. */
@media (max-width: 680px) {
  body.hub-page table.equip-sheet,
  body.hub-page table.inventory-sheet,
  body.hub-page table.intel-sheet {
    font-size: 0.8125rem;
  }

  body.hub-page table.equip-sheet th,
  body.hub-page table.equip-sheet td,
  body.hub-page table.inventory-sheet th,
  body.hub-page table.inventory-sheet td,
  body.hub-page table.intel-sheet th,
  body.hub-page table.intel-sheet td {
    padding: 2px 1px;
  }

  /* Same 640-part balance as the band below 520px. Territory is wide
     enough for Southwest, Rarity for uncommon, and the item columns
     are wider than the previous 13px tracks. Sum is 640. */
  body.hub-page table.equip-sheet col.equip-col-type { width: calc(72 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-name { width: calc(96 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-level { width: calc(36 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-rarity { width: calc(100 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-effects { width: 0 !important; }
  body.hub-page table.equip-sheet col.equip-col-terr { width: calc(96 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-owned { width: calc(48 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equipped { width: calc(32 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equip { width: calc(40 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-unequip { width: calc(40 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-sell { width: calc(80 / 640 * 100%) !important; }

  body.hub-page table.equip-sheet tr.hub-cols th {
    overflow-wrap: normal;
    word-break: normal;
    hyphens: manual;
    -webkit-hyphens: manual;
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-level,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equipped,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equip,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-unequip {
    font-size: 0;
    line-height: 0;
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-level::after,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equipped::after,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equip::after,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-unequip::after {
    font-size: 0.8125rem;
    font-weight: 700;
    line-height: 1.25;
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-level::after {
    content: "Lvl";
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equipped::after {
    content: "Eq";
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equip::after {
    content: "+";
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-unequip::after {
    content: "\2212";
  }

  body.hub-page table.inventory-sheet button {
    padding: 1px 3px;
    font-size: 0.8125rem;
  }

  body.hub-page table.equip-sheet button {
    padding: 0 1px;
    font-size: 0.8125rem;
  }

  /* 0.8125rem line box plus the 2px border. Side padding is 1px. */
  body.hub-page table.equip-sheet a.site-qty-btn {
    width: calc(1.25 * 0.8125rem + 4px) !important;
    height: calc(1.25 * 0.8125rem + 4px);
  }
}

/* Below this, 13px still cannot hold every short word. 12px keeps the
   Activity iframe from scrolling. break-word is the last step when a
   token is still wider than its cell. Hotel Room does not become cards. */
@media (max-width: 520px) {
  body.hub-page table.equip-sheet,
  body.hub-page table.inventory-sheet,
  body.hub-page table.intel-sheet {
    font-size: 0.75rem;
  }

  body.hub-page table.equip-sheet th,
  body.hub-page table.equip-sheet td,
  body.hub-page table.inventory-sheet th,
  body.hub-page table.inventory-sheet td,
  body.hub-page table.intel-sheet th,
  body.hub-page table.intel-sheet td {
    padding: 1px 2px;
  }

  body.hub-page table.inventory-sheet button {
    padding: 1px 3px;
    font-size: 0.75rem;
  }

  /* No vertical padding, so SELL's height is the 12px line box plus
     the 2px border. Side padding stays 1px so the label can fit the
     sell track. The square uses that same height. */
  body.hub-page table.equip-sheet button {
    padding: 0 1px;
    font-size: 0.75rem;
    min-height: 0;
  }

  body.hub-page table.equip-sheet a.site-qty-btn {
    width: calc(1.25 * 0.75rem + 4px) !important;
    height: calc(1.25 * 0.75rem + 4px);
  }

  /* Territory is as wide as "Southwest", and rarity as wide as
     "uncommon". The rest goes to Item Type and Item Name so boots,
     buckle, chaps, charm, Shindig, Longhorn, Fastener, and Blessing
     stay whole words around 450px. A tighter query below restores
     Southwest and uncommon when those tracks would overflow.
     Level reads "Lvl" and Equipped reads "Eq" so the header row stays
     two lines. Equip and Unequip read + and −, the same marks as the
     buttons. The shares sum to 640. */
  body.hub-page table.equip-sheet col.equip-col-type { width: calc(72 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-name { width: calc(96 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-level { width: calc(36 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-rarity { width: calc(100 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-effects { width: 0 !important; }
  body.hub-page table.equip-sheet col.equip-col-terr { width: calc(96 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-owned { width: calc(48 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equipped { width: calc(32 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equip { width: calc(40 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-unequip { width: calc(40 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-sell { width: calc(80 / 640 * 100%) !important; }

  /* Hyphenate a single word only when it is wider than the cell.
     break-word remains the last resort. anywhere and break-all are not
     used. lang=en is on the sheet. */
  body.hub-page table.equip-sheet td[data-label="Item Type"],
  body.hub-page table.equip-sheet td[data-label="Item Name"] {
    hyphens: auto;
    -webkit-hyphens: auto;
    overflow-wrap: break-word;
    word-break: normal;
  }

  body.hub-page table.equip-sheet tr.hub-cols th {
    overflow-wrap: normal;
    word-break: normal;
  }

  body.hub-page table.equip-sheet td[data-label="Level"],
  body.hub-page table.equip-sheet td[data-label="Owned"],
  body.hub-page table.equip-sheet td[data-label="Equipped"] {
    white-space: nowrap;
    overflow-wrap: normal;
  }

  body.hub-page table.equip-sheet td[data-label="Territory"] {
    overflow-wrap: normal;
    word-break: normal;
    hyphens: manual;
    -webkit-hyphens: manual;
    /* The side padding is the spare pixel Southwest was missing
       near 450 and 400. The column shares stay put, so Item Name,
       the +/− buttons, and SELL do not get narrower. */
    padding-left: 0;
    padding-right: 0;
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-level,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equipped,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equip,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-unequip {
    font-size: 0;
    line-height: 0;
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-level::after,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equipped::after,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equip::after,
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-unequip::after {
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1.25;
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-level::after {
    content: "Lvl";
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equipped::after {
    content: "Eq";
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-equip::after {
    content: "+";
  }

  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-unequip::after {
    content: "\2212";
  }
}

/* From 436px through 418px, Rarity and Territory are wider than
   "uncommon" and "Southwest". Those spare parts move to Item Name so
   "Buntline" and "Special" each stay one word. Item Type stays wide
   enough for buckle, chaps, charm, and boots. The name header drops
   its side padding so "Name" stays one word. Sum is 640. */
@media (max-width: 436px) {
  body.hub-page table.equip-sheet col.equip-col-type { width: calc(73 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-name { width: calc(78 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-level { width: calc(38 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-rarity { width: calc(107 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-terr { width: calc(99 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-owned { width: calc(52 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equipped { width: calc(33 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-equip { width: calc(40 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-unequip { width: calc(40 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-sell { width: calc(80 / 640 * 100%) !important; }

  body.hub-page table.equip-sheet td[data-label="Item Name"],
  body.hub-page table.equip-sheet tr.hub-cols th.equip-h-name {
    padding-left: 0;
    padding-right: 0;
  }
}

/* At 417px one set of tracks cannot hold Buntline, uncommon, and
   Southwest together. Rarity and Territory take their width back so
   uncommon stays one line through 398px and Southwest still fits.
   Item Type holds buckle. Item Name is the column that gives. */
@media (max-width: 417px) {
  body.hub-page table.equip-sheet col.equip-col-type { width: calc(78 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-name { width: calc(61 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-rarity { width: calc(112 / 640 * 100%) !important; }
  body.hub-page table.equip-sheet col.equip-col-terr { width: calc(106 / 640 * 100%) !important; }
}

/* "uncommon" still fits at 398px. At 397px nowrap would paint 1px
   into Territory, so the word wraps from there down. */
@media (max-width: 397px) {
  body.hub-page table.equip-sheet td[data-label="Rarity"] {
    white-space: normal;
  }
}

/* ---------- See more / see less (CollapsibleText) ----------
   Same rules as the inline style from CollapsibleTextAssets(). Kept here
   for pages that load a fresh site.css. The toggle still works when this
   file is stale: the button is a real button, and [hidden] is native. */

.gf-collapse-full[hidden],
.gf-collapse-short[hidden] {
  display: none !important;
}

body:not(.duel-page) button.gf-collapse-btn {
  display: inline-block !important;
  width: auto !important;
  min-height: 0 !important;
  margin: 0 0 0 0.35em !important;
  padding: 0 0.4em !important;
  border: 1px solid var(--site-border);
  border-radius: 3px;
  background: linear-gradient(var(--site-btn-top), var(--site-btn-bot));
  color: var(--site-ink);
  font-size: 0.72rem !important;
  font-weight: 600;
  line-height: 1.45 !important;
  vertical-align: baseline;
  white-space: nowrap !important;
  box-shadow: none;
}

@media (max-width: 800px) {
  body:not(.duel-page) button.gf-collapse-btn {
    font-size: 0.85rem !important;
    padding: 2px 0.55em !important;
    line-height: 1.6 !important;
  }
}

/* Retirement is a full page. Keep "Return to Main Dashboard" in the upper
   right. On a phone the title and the link wrap, with the link on the right
   and fully inside the panel. */
@media (max-width: 800px) {
  body.hub-page.retire-page table:not(.footer):not(.site-footer) tr.hub-banner > th {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    box-sizing: border-box;
  }

  body.retire-page .hub-return {
    float: none;
    display: block;
    width: auto;
    max-width: 100%;
    position: static;
    margin: 0 0 0 auto;
    padding: 0;
    background: transparent;
  }

  body.retire-page .hub-return > a {
    width: auto;
    max-width: 100%;
    min-height: 44px;
    white-space: normal;
  }
}
