/* ============================================================================
   BitcoinPuzzles — global shell
   Header, navigation, footer, flash, auth, FAQ, level badges.
   Depends on tokens.css and base.css. Nothing here hardcodes a colour.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   Header
   Flat fill and a hairline. The old gradient bar plus drop shadow read as a
   floating slab; at this density the page wants a rule, not a shelf.
   ------------------------------------------------------------------------ */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--ink-900);
  border-bottom: 1px solid var(--line);
}

.header-container {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  min-height: var(--h-header);
  padding-inline: var(--sp-4);
}

/* Wordmark: emphasis from weight and colour, never a gradient fill. */
.site-header .brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: none;
  text-decoration: none;
  font-size: var(--fs-md);
}

.brand-name {
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink-050);
}

.brand-accent {
  color: var(--accent);
}

.site-header .brand:hover .brand-name {
  color: var(--ink-050);
}

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

.menu-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  /* 44px, not 40: this is the drawer's only control and it is hit with a
     thumb, both to open and — as the X, over the open drawer — to close. */
  width: 44px;
  height: 44px;
  margin-inline-start: auto;
  background: transparent;
  border: none;
  border-radius: var(--r-md);
  cursor: pointer;
  padding: 0;
  z-index: var(--z-nav);
  color: var(--ink-100);
}

.menu-toggle:hover {
  background: var(--ink-800);
}

.menu-icon,
.menu-icon::before,
.menu-icon::after {
  display: block;
  width: 18px;
  height: 1.5px;
  background: currentColor;
  border-radius: var(--r-full);
  transition: transform var(--dur-2) var(--ease-out),
    opacity var(--dur-1) var(--ease), top var(--dur-2) var(--ease-out);
}

.menu-icon {
  position: relative;
}

.menu-icon::before,
.menu-icon::after {
  content: "";
  position: absolute;
  left: 0;
}

.menu-icon::before {
  top: -6px;
}
.menu-icon::after {
  top: 6px;
}

.menu-toggle.is-active .menu-icon {
  background: transparent;
}

.menu-toggle.is-active .menu-icon::before {
  top: 0;
  transform: rotate(45deg);
}

.menu-toggle.is-active .menu-icon::after {
  top: 0;
  transform: rotate(-45deg);
}

/* --- Navigation --------------------------------------------------------- */

.nav {
  position: fixed;
  inset-block: 0;
  inset-inline-end: 0;
  width: 100%;
  max-width: 320px;
  /* Between the scrim and the toggle. The toggle is the X, so it has to stay
     tappable over the drawer it closes. */
  z-index: calc(var(--z-nav) - 1);
  background: var(--ink-900);
  border-inline-start: 1px solid var(--line);
  padding-block: var(--h-header) max(var(--sp-4), env(safe-area-inset-bottom));
  padding-inline: var(--sp-3);
  /* Direction lives in a custom property, not in a direction-specific
     transform. A `[dir="rtl"] .nav` transform would outrank the desktop
     `.nav { transform: none }` — media queries add no specificity — and
     leave the desktop RTL nav translated off-screen. */
  --nav-hidden: 100%;
  transform: translateX(var(--nav-hidden));
  /* Hidden rather than merely off-screen, so the closed drawer's links stay
     out of the tab order instead of being focusable but invisible. */
  visibility: hidden;
  transition: transform var(--dur-3) var(--ease-out),
    visibility 0s linear var(--dur-3);
  display: flex;
  flex-direction: column;
  box-shadow: var(--sh-4);
}

[dir="rtl"] .nav {
  --nav-hidden: -100%;
}

/* Declared after the RTL rule so it wins the specificity tie in both
   directions. */
.nav.is-open {
  --nav-hidden: 0%;
  visibility: visible;
  transition-delay: 0s;
}

/* A real element rather than a `body::before`, because the scrim's job on a
   phone is to be tapped and a pseudo-element cannot carry a listener. It fades
   with the drawer instead of cutting in. */
.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: rgba(0, 0, 0, 0.6);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-3) var(--ease),
    visibility 0s linear var(--dur-3);
}

.nav-scrim.is-open {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

.nav-links {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-link {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink-300);
  text-decoration: none;
  padding: var(--sp-3);
  font-size: var(--fs-base);
  font-weight: 500;
  border-radius: var(--r-md);
  transition: color var(--dur-1) var(--ease),
    background-color var(--dur-1) var(--ease);
}

.nav-link:hover {
  color: var(--ink-050);
  background: var(--ink-800);
}

/* --- Dropdowns ---------------------------------------------------------- */

.nav-dropdown {
  position: relative;
}

.dropdown-toggle {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  width: 100%;
  text-align: start;
}

.dropdown-arrow {
  transition: transform var(--dur-2) var(--ease-out);
  flex: none;
  opacity: 0.7;
}

.nav-dropdown.is-open .dropdown-arrow {
  transform: rotate(180deg);
}

.dropdown-menu {
  display: none;
  flex-direction: column;
}

.nav-dropdown.is-open .dropdown-menu {
  display: flex;
}

.dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink-400);
  text-decoration: none;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: 500;
  border-radius: var(--r-sm);
  transition: color var(--dur-1) var(--ease),
    background-color var(--dur-1) var(--ease);
}

.dropdown-item:hover {
  color: var(--ink-050);
  background: var(--ink-750);
}

.dropdown-item .icon {
  color: var(--ink-500);
}

.dropdown-item:hover .icon {
  color: var(--accent);
}

/* The leaving-the-site mark is a fact about the destination, not an
   affordance, so it stays ink where the row's other icons take the accent on
   hover — the same treatment the footer gives the same outbound link. Rest
   state comes from `.dropdown-item .icon` above. */
.dropdown-item:hover .dropdown-item__out {
  color: var(--ink-300);
}

.dropdown-item.logout {
  color: var(--ink-400);
}

.dropdown-item.logout:hover {
  color: var(--danger);
}

.dropdown-item.logout:hover .icon {
  color: var(--danger);
}

/* Locale switcher: the code is the mark, the name is the confirmation. */
.locale-code {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 2px 5px;
  border-radius: var(--r-xs);
  background: var(--ink-800);
  border: 1px solid var(--line);
  color: var(--ink-200);
  line-height: 1.4;
  flex: none;
}

.dropdown-item--locale.is-current {
  color: var(--ink-050);
  background: var(--ink-800);
}

.dropdown-item--locale.is-current .locale-code {
  background: var(--accent-wash);
  border-color: var(--accent-dim);
  color: var(--accent);
}

/* --- Header actions ----------------------------------------------------- */

.nav-actions {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding-top: var(--sp-5);
  margin-top: var(--sp-5);
  border-top: 1px solid var(--line);
}

.user-cacatais {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--accent-wash);
  border: 1px solid var(--accent-dim);
  border-radius: var(--r-md);
  color: var(--accent);
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease);
}

.user-cacatais:hover {
  background: var(--accent-wash-hi);
  color: var(--accent);
}

.cacatais-amount {
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--accent);
}

.cacatais-unit {
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.75;
}

.btn-login,
.btn-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-base);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  transition: background-color var(--dur-1) var(--ease),
    border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}

.btn-login {
  color: var(--ink-100);
  border-color: var(--line-hi);
}

.btn-login:hover {
  background: var(--ink-800);
  border-color: var(--line-hard);
  color: var(--ink-050);
}

.btn-cta {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--sh-1);
}

.btn-cta:hover {
  background: var(--accent-hi);
  color: var(--accent-ink);
}

/* --- Profile trigger ---------------------------------------------------- */

.profile-trigger {
  display: flex;
  align-items: center;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  border-radius: var(--r-full);
}

.profile-trigger-image,
.profile-trigger-placeholder {
  width: 30px;
  height: 30px;
  border-radius: var(--r-full);
  object-fit: cover;
  border: 1px solid var(--line-hi);
  transition: border-color var(--dur-1) var(--ease);
}

.profile-trigger-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ink-750);
  color: var(--ink-100);
  font-weight: 700;
  font-size: var(--fs-sm);
  line-height: 1;
}

.profile-trigger:hover .profile-trigger-image,
.profile-trigger:hover .profile-trigger-placeholder {
  border-color: var(--accent);
}

.dropdown-identity {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  align-items: flex-start;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  margin-bottom: var(--sp-1);
  border-bottom: 1px solid var(--line);
}

.dropdown-identity__name {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-050);
}

/* --- Desktop ------------------------------------------------------------ */

@media (min-width: 1024px) {
  .menu-toggle,
  .nav-scrim,
  .nav-account,
  .nav-account-links,
  .nav-drawer-cta,
  .nav-section-label {
    display: none;
  }

  /* The drawer's scroll region has no job up here: the bar is one row and
     nothing in it scrolls. `display: contents` dissolves the wrapper so
     `.nav-links` is a direct flex child of `.nav` again, which is what the
     `margin-inline-start: auto` on `.nav-actions` is measuring against. */
  .nav-scroll {
    display: contents;
  }

  /* Marks belong to the drawer. Four of them in a 200px hover popover are
     noise, and the popover is already scanned by reading three or four rows. */
  .nav-row__mark {
    display: none;
  }

  .nav {
    position: static;
    width: auto;
    max-width: none;
    z-index: auto;
    background: transparent;
    border: none;
    padding: 0;
    transform: none;
    visibility: visible;
    transition: none;
    overflow: visible;
    box-shadow: none;
    flex-direction: row;
    align-items: center;
    flex: 1;
    gap: var(--sp-4);
  }

  .nav-links {
    flex-direction: row;
    align-items: center;
    gap: 2px;
  }

  .nav-link {
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-sm);
    white-space: nowrap;
  }

  .dropdown-toggle {
    width: auto;
  }

  .dropdown-menu {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-start: 0;
    min-width: 200px;
    padding: var(--sp-1);
    background: var(--ink-850);
    border: 1px solid var(--line-hi);
    border-radius: var(--r-md);
    box-shadow: var(--sh-3);
    z-index: var(--z-nav);
  }

  .dropdown-menu.profile-dropdown-menu {
    inset-inline-start: auto;
    inset-inline-end: 0;
  }

  .dropdown-item {
    padding: var(--sp-2) var(--sp-3);
    white-space: nowrap;
  }

  .nav-actions {
    flex-direction: row;
    align-items: center;
    margin: 0;
    margin-inline-start: auto;
    padding-top: 0;
    border-top: none;
    gap: var(--sp-3);
  }

  .btn-login,
  .btn-cta {
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-sm);
  }

  .nav-dropdown--locale .locale-name {
    display: none;
  }
}

/* --- The drawer ---------------------------------------------------------
   Below 1024px the same markup is a full-height panel a thumb works through,
   so it is composed as its own surface rather than as a narrow copy of the
   header bar.

   Head, body, footing — the shape of a sheet, not of a list. The head holds
   who you are and what you hold; the body holds where you can go, ruled into
   four named bands with a drawn mark on every row; the footing holds the way
   in or out. Only the body scrolls, so neither the balance nor the exit can be
   pushed off the drawer by the length of the list between them.

   The desktop header's icon-only controls do not survive the trip — a bare
   bell and a 30px avatar stranded in a 320px column are orphans, and opening a
   dropdown inside a scrolling drawer is a menu inside a menu.
   ---------------------------------------------------------------------- */

@media (max-width: 1023px) {
  /* Three regions, not one scrolling column: who you are, seated on the top
     edge; where you can go, which scrolls; and the way in or out, seated on the
     bottom edge. The column used to scroll as one, and with fourteen
     destinations plus four account rows in it the sign-out — signed out, both
     sign-in buttons — sat below the fold on every phone. The drawer's only
     irreversible action was also its least reachable one. Now the scroll is
     handed to the middle region alone and the two edges hold still.

     The padding moves to the regions with it: a scrolling child cannot inherit
     its parent's padding as a gutter without the fill scrolling out of it. */
  .nav {
    padding-block: var(--h-header) 0;
    padding-inline: 0;
    overflow: hidden;
  }

  .nav-scroll {
    flex: 1 1 auto;
    /* Without this a flex item refuses to shrink below its content height and
       the region never scrolls — it pushes the footing off the drawer. */
    min-block-size: 0;
    overflow-y: auto;
    /* Scrolling past either end must not hand the gesture to the page
       underneath, which is locked but still reachable by a chained fling. */
    overscroll-behavior: contain;
    padding: var(--sp-4) var(--sp-3) var(--sp-5);
  }

  .nav-account,
  .nav-account-links,
  .nav-drawer-cta {
    display: flex;
    flex-direction: column;
  }

  /* Identity and balance: the two facts, held above the scroll. */
  .nav-account {
    flex: none;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3) var(--sp-3);
    border-block-end: 1px solid var(--line);
  }

  .nav-account-links {
    gap: 2px;
  }

  /* The exit sits on the drawer's bottom edge, in thumb reach, however long or
     short the list above it runs. It carries the ground colour because the
     destinations now scroll behind it. */
  .nav-drawer-cta {
    flex: none;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-3)
      max(var(--sp-3), env(safe-area-inset-bottom));
    border-block-start: 1px solid var(--line);
    background: var(--ink-900);
  }

  /* --- Identity ---------------------------------------------------------- */

  .nav-identity {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--r-md);
    text-decoration: none;
    transition: background-color var(--dur-1) var(--ease);
  }

  .nav-identity:hover {
    background: var(--ink-800);
  }

  .nav-identity__avatar {
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: var(--r-full);
    object-fit: cover;
    border: 1px solid var(--line-hi);
  }

  .nav-identity__avatar--placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ink-750);
    color: var(--ink-100);
    font-weight: 700;
    font-size: var(--fs-md);
    line-height: 1;
  }

  .nav-identity__text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-1);
    flex: 1 1 auto;
    min-width: 0;
  }

  /* The same mark the balance row carries. Both rows are links to a page; the
     name and the level badge alone read as a caption, not as somewhere to go. */
  .nav-identity__go {
    flex: none;
    color: var(--ink-500);
  }

  [dir="rtl"] .nav-identity__go {
    transform: scaleX(-1);
  }

  .nav-identity__name {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--ink-050);
    line-height: var(--lh-snug);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* --- Balance ----------------------------------------------------------
     The one orange thing in the drawer, because it is the one thing here that
     is money. Everything else earns its weight from the ink ramp. */

  .nav-balance {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 44px;
    padding: var(--sp-2) var(--sp-3);
    background: var(--accent-wash);
    border: 1px solid var(--accent-dim);
    border-radius: var(--r-md);
    color: var(--accent);
    text-decoration: none;
    transition: background-color var(--dur-1) var(--ease);
  }

  .nav-balance:hover {
    background: var(--accent-wash-hi);
    color: var(--accent);
  }

  .nav-balance .cacatais-amount {
    font-size: var(--fs-base);
  }

  .nav-balance__go {
    margin-inline-start: auto;
    flex: none;
    opacity: 0.7;
  }

  [dir="rtl"] .nav-balance__go {
    transform: scaleX(-1);
  }

  /* --- Section bands ------------------------------------------------------
     Both carriers of a section name — the standalone label and the four group
     heads dropdown_controller has turned into headings — are drawn once, here.
     A label plus a hairline running off the drawer's edge, and more space above
     it than below, so it binds to the rows it names instead of floating between
     two groups. Eighteen rows at one weight was the drawer's real problem: with
     the sections ruled, the list reads as four short lists. */
  .nav-section-label,
  .nav-dropdown--group .dropdown-toggle {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 0;
    margin-block: var(--sp-5) var(--sp-2);
    padding-block: 0;
    padding-inline: var(--sp-3) 0;
    font-size: var(--fs-2xs);
    font-weight: 600;
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
    color: var(--ink-400);
  }

  /* Runs to the drawer's own edge, not to the rows' inset: a rule that crosses
     reads as structure, one that stops short reads as decoration. */
  .nav-section-label::after,
  .nav-dropdown--group .dropdown-toggle::after {
    content: "";
    flex: 1 1 auto;
    block-size: 1px;
    background: var(--line);
    margin-inline-end: calc(var(--sp-3) * -1);
  }

  /* The scroll region's own padding already stands above the first band. Only
     the first one: a label that is merely first inside its own region — the
     account block's — still needs the full step above it. */
  .nav-links > .nav-dropdown--group:first-child .dropdown-toggle {
    margin-block-start: 0;
  }

  /* A nav row is a touch target first. 44px floor, and the fill spans the
     drawer so the whole row is the target, not just its label. The gap is the
     mark's gutter, and it is the same on every row in the drawer — account
     rows carried an icon before this pass, destinations did not, and the two
     have to hang off one vertical. */
  .nav-link,
  .dropdown-item {
    min-height: 44px;
    gap: var(--sp-3);
    padding-block: var(--sp-2);
    padding-inline: var(--sp-3);
  }

  /* These rows are the primary navigation on a phone, not a secondary menu
     hanging off a hover, so every one of them — the twelve destinations and the
     three account rows alike — takes the strong secondary ink rather than the
     desktop popover's tertiary. It is the step that separates a destination
     from the band label above it. */
  .nav-link,
  .dropdown-item {
    color: var(--ink-200);
  }

  .dropdown-item {
    font-size: var(--fs-base);
  }

  .nav-link:hover,
  .dropdown-item:hover {
    background: var(--ink-750);
  }

  /* The mark is a wayfinding aid, not a state. It never takes the accent —
     in this drawer the orange is spent on the balance and nothing else, which
     is why the desktop popover's `.dropdown-item:hover .icon` is overridden
     here rather than inherited. */
  .dropdown-item .nav-row__mark {
    color: var(--ink-400);
    flex: none;
  }

  .dropdown-item:hover .nav-row__mark {
    color: var(--ink-200);
  }

  .dropdown-item[aria-current="page"] .nav-row__mark {
    color: var(--ink-200);
  }

  /* One step brighter than the desktop dropdown's `--ink-500`: these rows are
     the primary navigation on a phone, not a secondary menu, and 2.9:1 ink
     disappears against the drawer at arm's length. */
  .nav-link .icon {
    color: var(--ink-400);
    flex: none;
  }

  /* Where you are. `aria-current` is the carrier, so the row cannot be drawn
     as current without also announcing itself as current. Ink, not orange —
     the accent is spent on the balance. */
  .nav-link[aria-current="page"],
  .dropdown-item[aria-current="page"] {
    background: var(--ink-800);
    color: var(--ink-050);
    font-weight: 600;
  }

  .nav-link[aria-current="page"] .icon {
    color: var(--ink-200);
  }

  /* The chevron belongs at the end of the row it opens, not tucked against the
     label with the rest of the row left empty. Pushed by the arrow's own
     margin rather than by `space-between`, which would also blow the locale
     row's code and name apart. */
  .dropdown-toggle .dropdown-arrow {
    margin-inline-start: auto;
  }

  /* Sub-items hang off a hairline rail, so an open group reads as a branch of
     its parent rather than as four more top-level rows. */
  .dropdown-menu {
    padding-block: var(--sp-1);
    padding-inline-start: var(--sp-3);
    margin-inline-start: calc(var(--sp-3) + 7px);
    border-inline-start: 1px solid var(--line);
    gap: 2px;
  }

  /* --- Groups -----------------------------------------------------------
     The four destination groups do not fold here. A drawer is already a list,
     and a collapsed group inside it charges a tap for nothing: hamburger,
     group, destination, three taps to reach a page the desktop bar reaches in
     one hover. So they are always open and their heads become what they now
     do — the label for the rows below, set as the language section's label is,
     because they name a region of the drawer rather than open one.

     The locale picker keeps its fold (`--locale`, not `--group`): it is a
     setting with six near-identical rows, and unfolded it would outweigh the
     destinations it sits under. dropdown_controller stops the tap and takes
     these heads out of the tab ring — CSS can do neither. */
  .nav-dropdown--group .dropdown-menu {
    display: flex;
    /* No rail. These rows are sections of the drawer, not a branch hanging off
       an opened parent, so they sit flush with every other row in it. */
    padding-block: 0;
    padding-inline-start: 0;
    margin-inline-start: 0;
    border-inline-start: none;
    gap: 2px;
  }

  /* A head that opens nothing is not a control. Its look comes from the band
     rule above, which it shares with `.nav-section-label`. */
  .nav-dropdown--group .dropdown-toggle {
    cursor: default;
  }

  /* Nothing opens, so nothing points down, and nothing lights up under a
     thumb — the row fill belongs to rows that go somewhere. */
  .nav-dropdown--group .dropdown-arrow {
    display: none;
  }

  .nav-dropdown--group .dropdown-toggle:hover {
    color: var(--ink-400);
    background: none;
  }

  .nav-count {
    margin-inline-start: auto;
    flex: none;
    padding: 0.125rem 0.375rem;
    border-radius: var(--r-sm);
    background: var(--accent-wash);
    border: 1px solid var(--accent-dim);
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 1.4;
  }

  .nav-link--logout {
    color: var(--ink-300);
  }

  .nav-link--logout:hover,
  .nav-link--logout:hover .icon {
    color: var(--danger);
  }

  .nav-link--logout:hover {
    background: var(--danger-wash);
  }

  /* The header's own action cluster is a desktop composition; the drawer
     re-states all of it above, in rows. */
  .nav-actions {
    display: none;
  }

  .btn-login,
  .btn-cta {
    min-height: 44px;
  }

  /* The code is the mark on desktop, where the row is two letters in a bar.
     Down here the globe holds the gutter and the name carries the meaning, so
     the code on the *toggle* would be a third way of saying the same thing at
     the one width that breaks the column. It stays on the rows inside the open
     menu, where it is the thing being chosen between. */
  .nav-dropdown--locale .dropdown-toggle .locale-code {
    display: none;
  }
}

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

   The shell's footing: the site's own taxonomy restated as four named columns,
   then the rooms the community talks in and the language it reads, then the
   record's close. Three bands, each hairline-separated and each quieter than the
   one above it — a ledger strip, not a megafooter. There is no pitch here and
   nothing that is not a destination or a fact.

   What pins it: `body` is a flex column at `100dvh` (see base.css) and this is
   its last item, so `margin-block-start: auto` eats whatever height the page
   left over. On a page taller than the viewport the free space is negative, the
   auto margin resolves to 0, and the footer simply follows the content.

   Which is why the 6rem breath above the band is this element's own *padding*
   and the fill sits on an inner band: a margin here is the thing `auto`
   replaces, and it cannot be both. The padding is outside the band, so it shows
   page ground exactly as the old `margin-top: var(--sp-24)` did.
   ------------------------------------------------------------------------ */

.site-footer {
  margin-block-start: auto;
  padding-block-start: var(--sp-24);
}

.site-footer__band {
  background: var(--ink-900);
  border-block-start: 1px solid var(--line);
}

/* --- Band 1: the columns ------------------------------------------------
   Two columns on a phone, four from 768px. The four mirror the header's own
   groups: a visitor who learned the taxonomy up there does not have to learn a
   second one down here. */

.footer-ledger {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-8) var(--sp-6);
  padding-block: var(--sp-10);
}

@media (min-width: 768px) {
  .footer-ledger {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.footer-col__head {
  margin-block-end: var(--sp-3);
  color: var(--ink-300);
}

.footer-col__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Full-width, not inline: the row's target is the whole column track, and 13px
   text on sp-2 padding clears 36px without the list looking like a stack of
   buttons. Flex rather than block so the one row carrying an
   opens-in-a-new-tab glyph keeps it on the label's line — the base reset makes
   every `svg` a block, which drops a bare inline icon to a line of its own. */
.footer-link {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  padding-block: var(--sp-2);
  color: var(--ink-300);
  font-size: var(--fs-sm);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease);
}

.footer-link:hover {
  color: var(--ink-050);
}

/* Where you are, drawn off the same attribute the header's rows use — ink and
   weight, never the accent, which is spent on money. */
.footer-link[aria-current="page"] {
  color: var(--ink-050);
  font-weight: 600;
}

.footer-link__out {
  color: var(--ink-400);
}

.footer-link:hover .footer-link__out {
  color: var(--ink-300);
}

/* --- Band 2: channels and language -------------------------------------- */

.footer-channels {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-block: var(--sp-3);
  border-block-start: 1px solid var(--line);
}

.footer-social {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  list-style: none;
  margin: 0;
  padding: 0;
  /* The 18px mark sits in a 38px cell, so it carries 10px of its own inset.
     Pulling the row back by that much lands the first mark on the container's
     text edge instead of 10px inside it. */
  margin-inline: -10px;
}

.footer-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* A bare 18px glyph is not a touch target. The cell is, and its hover fill is
     what makes the row read as a row of stamps rather than a run of loose
     glyphs. */
  width: 38px;
  height: 38px;
  border-radius: var(--r-sm);
  color: var(--ink-400);
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}

.footer-social a:hover {
  background: var(--ink-800);
  color: var(--ink-050);
}

.footer-locales {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-inline: -1px;
}

.footer-locale {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  min-height: 38px;
  padding-inline: var(--sp-1);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--ink-400);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}

.footer-locale:hover {
  background: var(--ink-800);
  color: var(--ink-050);
}

/* The language you are reading. It takes the same fill as hover plus a
   hairline, so the current locale stays distinguishable from the one under the
   pointer. The border is transparent at rest rather than absent, so marking it
   moves nothing. */
.footer-locale[aria-current] {
  background: var(--ink-800);
  border-color: var(--line-hi);
  color: var(--ink-050);
}

/* --- Band 3: the footing ------------------------------------------------ */

.footer-footing {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-4);
  padding-block: var(--sp-4);
  border-block-start: 1px solid var(--line);
  color: var(--ink-400);
  font-size: var(--fs-xs);
}

.footer-footing p {
  margin: 0;
}

.footer-credit {
  color: var(--ink-400);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease);
}

.footer-credit:hover {
  color: var(--ink-200);
  text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Flash messages
   ------------------------------------------------------------------------ */

.flash-container {
  position: fixed;
  top: calc(var(--h-header) + var(--sp-4));
  inset-inline-end: var(--sp-4);
  z-index: var(--z-flash);
  max-width: 380px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.flash {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid transparent;
  font-size: var(--fs-base);
  box-shadow: var(--sh-3);
  animation: flash-in var(--dur-3) var(--ease-out);
}

.flash-alert {
  background: var(--ink-850);
  border-color: rgba(242, 85, 90, 0.4);
  color: var(--danger);
}

.flash-notice {
  background: var(--ink-850);
  border-color: rgba(91, 196, 122, 0.4);
  color: var(--solved);
}

@keyframes flash-in {
  from {
    transform: translateY(-8px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

@media (max-width: 768px) {
  .flash-container {
    inset-inline: var(--sp-4);
    max-width: none;
  }
}

/* --- Field errors ------------------------------------------------------- */

.field-errors {
  background: var(--danger-wash);
  border: 1px solid rgba(242, 85, 90, 0.35);
  border-radius: var(--r-md);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
}

.field-errors h2,
.field-errors h3 {
  color: var(--danger);
  font-size: var(--fs-sm);
  font-weight: 600;
  margin: 0 0 var(--sp-2);
}

.field-errors ul {
  margin: 0;
  padding-inline-start: var(--sp-5);
  color: var(--danger);
  font-size: var(--fs-sm);
}

.field-errors li {
  margin: var(--sp-1) 0;
}

/* ---------------------------------------------------------------------------
   Auth pages

   Four cards on one chassis: sign in, sign up, request a reset, set a new
   password. Two of them compose differently depending on whether typing a
   credential is a way into this deployment at all — in production it is not
   (config.x.password_credentials), so the card is a brand mark, a title, one
   Google button and a line explaining that the same button registers. The card
   owns its vertical rhythm through `gap`, so a region can be absent without
   leaving the margin it would have collapsed against.
   ------------------------------------------------------------------------ */

.auth-page {
  /* dvh, not vh: on iOS the URL bar makes 100vh taller than the visible
     viewport, which pushed the centred card off-centre downward. */
  min-height: calc(100dvh - var(--h-header));
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: var(--sp-12);
  padding-inline: var(--sp-4);
  background: var(--ink-950);
}

.auth-card {
  /* One custom property for the card's inset, so the full-bleed footer seam
     below stays in register with it without repeating the value or adding a
     breakpoint the system does not have. At 390px this resolves to sp-6. */
  --auth-card-pad: clamp(var(--sp-6), 5vw, var(--sp-8));

  width: 100%;
  max-width: 400px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  background: var(--ink-850);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-4);
  padding: var(--auth-card-pad);
}

.auth-header {
  text-align: center;
}

.auth-header .brand-name {
  font-size: var(--fs-lg);
  font-weight: 800;
  color: var(--ink-050);
}

.auth-header .brand-accent {
  color: var(--accent);
}

.auth-header h1 {
  font-size: var(--fs-xl);
  font-weight: 700;
  margin: var(--sp-5) 0 var(--sp-2);
  text-wrap: balance;
}

.auth-header p {
  color: var(--ink-300);
  font-size: var(--fs-base);
  margin: 0;
  text-wrap: pretty;
}

/* The form and the sentence that belongs to it, held closer to each other than
   the card's own rhythm holds them to the divider below. */
.auth-credentials {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

/* The separation between one field and the next. It used to be supplied by
   `admin.css`, which declared `.form-group` at the top level and loaded last on
   every page in the site — so an admin sheet was silently setting the rhythm of
   the sign-in, sign-up, password and create-challenge forms. The rule was real
   work, so it moved here, to the sheet that already owns `.form-group`, rather
   than being deleted with the sheet that had no business holding it. Logical,
   so it survives the RTL flip. */
.form-group {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-block-end: var(--sp-6);
}

.form-group label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-200);
}

.form-group input {
  width: 100%;
  padding: 0.625rem var(--sp-3);
  font-size: var(--fs-base);
  border: 1px solid var(--line-hi);
  border-radius: var(--r-md);
  background: var(--ink-800);
  color: var(--ink-050);
  transition: border-color var(--dur-1) var(--ease);
}

.form-group input:hover {
  border-color: var(--line-hard);
}

.form-group input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.form-group input::placeholder {
  color: var(--ink-400);
}

/* `.form-group` carries a bottom margin for the forms elsewhere in the app that
   stack it inside a plain block. Here the flex `gap` already sets the rhythm,
   and the two together were spacing the password card's single field 40px from
   its button while the sign-in card's fields sat 24px apart — the same
   component, two different rhythms, depending on which element the class
   landed on. */
.auth-form .form-group {
  margin-block-end: 0;
}

/* A label and the escape hatch that belongs to it on one baseline. */
.form-group__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
}

.form-group__aside {
  color: var(--accent);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  transition: color var(--dur-1) var(--ease);
}

.form-group__aside:hover {
  color: var(--accent-hi);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* 16px, not the 15px UI body: iOS Safari force-zooms a focused input below
   16px, which on a 390px viewport leaves the visitor zoomed into a card they
   then have to pinch back out of. */
.auth-form .form-group input {
  font-size: var(--fs-md);
}

.auth-form .form-group input:focus {
  background: var(--ink-850);
}

/* --ink-400 is 5.0:1 on the page ground but only 4.31:1 on the input fill these
   sit on, and both password views ship placeholders. */
.auth-form .form-group input::placeholder {
  color: var(--ink-300);
}

.auth-submit {
  width: 100%;
  padding: 0.6875rem var(--sp-6);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: none;
  border-radius: var(--r-md);
  cursor: pointer;
  box-shadow: var(--sh-1);
  margin-block-start: var(--sp-1);
  transition: background-color var(--dur-1) var(--ease),
    transform var(--dur-1) var(--ease);
}

.auth-submit:hover {
  background: var(--accent-hi);
}

.auth-submit:active {
  background: var(--accent-lo);
  transform: translateY(1px);
}

/* The sentence under a form that points at the other card. It had no rule at
   all: the paragraph inherited body ink and the anchor inherited `a`'s
   --ink-050, two neighbouring greys, so the link did not read as one. */
.auth-link {
  margin: 0;
  text-align: center;
  color: var(--ink-300);
  font-size: var(--fs-sm);
}

.auth-link a,
.auth-footer a {
  color: var(--accent);
  font-weight: 600;
  /* Underlined at rest, not on hover: this is a link inside a sentence, where
     colour alone is the one cue a colour-blind reader does not get. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--accent-dim);
  text-underline-offset: 0.2em;
}

.auth-link a:hover,
.auth-footer a:hover {
  color: var(--accent-hi);
  text-decoration-color: currentColor;
}

/* A seated sunken band, not a rule floating inside the card's padding — the
   same anatomy the challenge card uses (body, then an ink-900 footer). It is
   what gives the production card a bottom edge to sit on instead of ending in
   air below a lone button. The radius is the card's less its 1px border. */
.auth-footer {
  margin-inline: calc(var(--auth-card-pad) * -1);
  margin-block-end: calc(var(--auth-card-pad) * -1);
  padding: var(--sp-5) var(--auth-card-pad);
  background: var(--ink-900);
  border-block-start: 1px solid var(--line);
  border-end-start-radius: calc(var(--r-lg) - 1px);
  border-end-end-radius: calc(var(--r-lg) - 1px);
  text-align: center;
}

.auth-footer p {
  color: var(--ink-300);
  font-size: var(--fs-sm);
  margin: 0;
  text-wrap: pretty;
}

.auth-social {
  display: flex;
  flex-direction: column;
}

/* Sized as the primary action, because on the production card it is the only
   one. It keeps the neutral fill rather than taking --accent: the orange rule
   governs the system's own controls, and this button is carrying Google's mark,
   which does not sit on an orange plate. Weight comes from scale and the seat
   shadow instead. */
.google-signin-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: 0.75rem var(--sp-5);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink-050);
  background: var(--ink-800);
  /* The hard hairline at rest, not the 12% one the secondary button wears: an
     ink-800 fill on an ink-850 card is a 4-step difference nobody sees, and on
     the production card this is the only thing to click. The edge is what
     separates it from an input — and a dark surface with a visible 1px border
     is Google's own dark-theme spec for this button. */
  border: 1px solid var(--line-hard);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease),
    border-color var(--dur-1) var(--ease),
    transform var(--dur-1) var(--ease);
}

.google-signin-btn:hover {
  background: var(--ink-750);
  border-color: var(--ink-500);
}

.google-signin-btn:active {
  background: var(--ink-700);
  transform: translateY(1px);
}

/* The fill changes rather than fading. A 50% opacity pass put the label at
   roughly 2.5:1 and dimmed the Google mark into mud while the popup was open —
   which is exactly when the visitor is looking at it to know something is
   happening. */
.google-signin-btn:disabled {
  background: var(--ink-750);
  border-color: var(--line);
  color: var(--ink-400);
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
}

.google-signin-btn:disabled svg {
  opacity: 0.45;
}

.google-signin-btn svg {
  flex-shrink: 0;
}

.auth-error {
  margin-block-start: var(--sp-3);
  padding: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--danger);
  background: var(--danger-wash);
  border: 1px solid rgba(242, 85, 90, 0.3);
  border-radius: var(--r-sm);
  text-align: center;
}

.auth-divider {
  display: flex;
  align-items: center;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line-hi);
}

.auth-divider span {
  padding-inline: var(--sp-4);
  color: var(--ink-400);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
}

/* ---------------------------------------------------------------------------
   User level badges — Leigo through Mestre Caçatal.
   A six-step progression: neutral, then the state hues, arriving at the
   accent. Rank reads from colour temperature, never from decoration.
   ------------------------------------------------------------------------ */

.user-level-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.125rem 0.375rem;
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  border-radius: var(--r-xs);
  white-space: nowrap;
  border: 1px solid transparent;
  line-height: 1.4;
}

.user-level-badge.level-0 {
  background: var(--ink-800);
  color: var(--ink-400);
  border-color: var(--line);
}

.user-level-badge.level-1 {
  background: var(--solved-wash);
  color: var(--solved);
  border-color: rgba(91, 196, 122, 0.28);
}

.user-level-badge.level-2 {
  background: var(--open-wash);
  color: var(--open);
  border-color: rgba(62, 201, 192, 0.28);
}

.user-level-badge.level-3 {
  background: var(--locked-wash);
  color: var(--locked);
  border-color: rgba(155, 140, 240, 0.28);
}

.user-level-badge.level-4 {
  background: var(--accent-wash);
  color: var(--accent);
  border-color: rgba(247, 147, 26, 0.28);
}

.user-level-badge.level-5 {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent-hi);
}
