/* ============================================================================
   Public profile — someone else's account, read as a record.

   Three registers, the same three the account page and the ledger use: who
   this person is, what their position in the economy is, then the challenges
   themselves. The strip is the system's ledger strip; the challenges are the
   system's own `.pz-card` in the system's `.puzzles-grid`; the rail above them
   is the system's `.tabs-nav`.

   This sheet is namespaced under `.pub-*` and `.tfr-*` on purpose. The version
   it replaces declared `.empty-state`, `.stat-card`, `.stat-number`,
   `.stat-label`, `.puzzles-grid`, `.avatar-placeholder`, `.tab-button` and a
   dozen `.puzzle-card-*` selectors at the top level. This sheet loads after
   puzzles.css and ranking.css, so those definitions were silently overriding
   the challenge grid on `/puzzles`, `/puzzles/solved` and the homepage, and
   the empty state on the ranking. None of them survive here.

   What is also gone: the radial orange bloom behind the header, the gradient
   page and hero grounds, the pulsing accent ring around the avatar, the
   gradient button fills and their coloured glows, and green used as a button
   fill. Green is a state in this system, and glows are the exact tell of the
   crypto hero the design refuses.
   ========================================================================= */

.pub {
  padding-block: var(--sp-8) var(--sp-16);
}

/* ---------------------------------------------------------------------------
   Identity — avatar, name, level, handle, and the two ways to reach them.
   ------------------------------------------------------------------------ */

.pub-id {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-5);
  margin-block-end: var(--sp-6);
}

.pub-id__who {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-width: 0;
}

.pub-id__avatar {
  --avatar-size: 72px;
}

.pub-id__names {
  min-width: 0;
}

.pub-id__nameline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
}

.pub-id__name {
  font-size: var(--fs-2xl);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-display);
  color: var(--ink-050);
  margin: 0;
  /* display_name allows 50 characters with no space in them. */
  overflow-wrap: anywhere;
}

.pub-id__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-sm);
  color: var(--ink-400);
}

.pub-id__handle {
  font-size: var(--fs-sm);
  color: var(--ink-300);
  overflow-wrap: anywhere;
}

.pub-id__dot {
  flex: none;
  width: 3px;
  height: 3px;
  border-radius: var(--r-full);
  background: var(--ink-600);
}

.pub-id__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

/* A wrapped meta row leaves the separator dot stranded at the end of the first
   line. Stack instead and drop the dot — it only has a job while the two facts
   share a line. */
@media (max-width: 767.98px) {
  .pub-id__meta {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-1);
  }

  .pub-id__dot {
    display: none;
  }

  /* `1 1 0` rather than `1 1 auto`: the two labels are different lengths, and
     growing from their own content basis leaves the pair visibly uneven. */
  .pub-id__actions .btn {
    flex: 1 1 0;
  }
}

@media (min-width: 768px) {
  .pub-id__name {
    font-size: var(--fs-3xl);
  }
}

/* ---------------------------------------------------------------------------
   The position — the system's ledger strip. Cells are separated by a 1px gap
   over a rule-coloured ground so the separators resolve on both axes and in
   both writing directions, rather than by per-cell borders that double up.
   ------------------------------------------------------------------------ */

.pub-position {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-block-end: var(--sp-8);
}

.pub-position__cell {
  background: var(--ink-900);
  padding: var(--sp-3) var(--sp-4);
  min-width: 0;
}

.pub-position__label {
  margin: 0 0 var(--sp-1);
}

.pub-position__value {
  font-size: var(--fs-lg);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
  color: var(--ink-100);
  margin: 0;
}

.pub-position__value--accent {
  color: var(--accent);
}

.pub-position__value--locked {
  color: var(--locked);
}

/* ---------------------------------------------------------------------------
   The challenges — the system's tab rail, built from buttons here because
   these tabs swap panels in place instead of navigating.
   ------------------------------------------------------------------------ */

/* `.tabs-nav__btn` is authored for anchors on the puzzle detail page, so the
   UA button chrome has to come off before it can carry a real <button>. */
.pub-tabs .tabs-nav__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  background: transparent;
  border-block-start: 0;
  border-inline: 0;
  font-family: var(--font-ui);
  cursor: pointer;
}

.pub-tabs__count {
  padding: 0.0625rem 0.375rem;
  border-radius: var(--r-sm);
  background: var(--ink-800);
  color: var(--ink-300);
  font-size: var(--fs-2xs);
  font-weight: 700;
  line-height: 1.4;
  transition: background-color var(--dur-1) var(--ease),
    color var(--dur-1) var(--ease);
}

.pub-tabs .tabs-nav__btn.active .pub-tabs__count {
  background: var(--accent-wash);
  color: var(--accent);
}

/* A focus ring on a tab must not be clipped by the rail's own scroll box. */
.pub-tabs .tabs-nav__btn:focus-visible {
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}

/* ---------------------------------------------------------------------------
   Transfer dialog — the one money control on this surface.
   ------------------------------------------------------------------------ */

.tfr {
  position: fixed;
  inset: 0;
  z-index: var(--z-flash);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  background: rgba(0, 0, 0, 0.9);
  opacity: 0;
  visibility: hidden;
  /* Visibility is a discrete property: transitioning it means the element is
     still `hidden` when the open handler runs, so focus() silently fails and
     the dialog opens with focus stranded behind the overlay. Flip it
     instantly on open and delay it only on close. */
  transition: opacity var(--dur-3) var(--ease), visibility 0s linear var(--dur-3);
}

.tfr.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-3) var(--ease);
}

.tfr__dialog {
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - var(--sp-8));
  overflow-y: auto;
  box-shadow: var(--sh-4);
}

.tfr__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: none;
  margin-inline-end: calc(var(--sp-2) * -1);
  background: transparent;
  border: 0;
  border-radius: var(--r-md);
  color: var(--ink-400);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease),
    color var(--dur-1) var(--ease);
}

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

.tfr__section {
  padding: var(--sp-5);
}

.tfr__section + .tfr__section {
  border-block-start: 1px solid var(--line);
}

.tfr__to {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  margin-block-start: var(--sp-2);
}

.tfr__to .avatar {
  --avatar-size: 36px;
}

.tfr__to-name {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink-050);
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The balance is the ceiling on the field below it, so it takes the accent
   wash band the system reserves for money and sits directly above the input. */
.tfr__balance {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-5);
  background: var(--accent-wash);
  border-block: 1px solid var(--accent-dim);
}

.tfr__balance-amount {
  font-size: var(--fs-xl);
  font-weight: 700;
  color: var(--accent);
}

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

.tfr__control {
  position: relative;
  display: flex;
  align-items: center;
}

.tfr__input {
  padding-inline-end: 3rem;
  font-size: var(--fs-xl);
  font-weight: 600;
}

/* The stepper arrows fight a field that already states its own ceiling. */
.tfr__input::-webkit-outer-spin-button,
.tfr__input::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}

.tfr__input[type="number"] {
  appearance: textfield;
}

.tfr__suffix {
  position: absolute;
  inset-inline-end: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-400);
  pointer-events: none;
}

/* `.field input:focus` outranks `.field--invalid .input` by one element, so a
   field that is both invalid and focused draws its border in the accent — the
   one moment the visitor most needs to see red. Restated here at a higher
   specificity, scoped to this dialog rather than patched into base.css. */
.tfr .field--invalid .input:focus {
  border-color: var(--danger);
}

/* The error line reserves its own row so naming the problem does not shove
   the buttons down the dialog. */
.tfr__error {
  min-height: 1.2em;
}

.tfr__error:empty {
  min-height: 0;
}

.tfr__actions {
  display: flex;
  gap: var(--sp-3);
  margin-block-start: var(--sp-5);
}

.tfr__actions .btn {
  flex: 1;
}
