/* person.css — the person page (#/user/:id) only: the masthead, the presence
 * sentence, the tab bands, and each tab's composition — the groups list and
 * its drawer, profile history, partners, and the rhythm sentence.
 *
 * Composition only; every value is a token. The tab strip is the system's
 * `.seg` (controls.css), each tab is a `.band` (layout.css), charts are
 * viz.css's `.year-*` / `.week-*` / `.bartrack` / `.sheet-grid`, lists are
 * data.css's `.ranked` and `.claim`. Prefixes: `psn-` for this page,
 * `gsheet-` for the group drawer (views/user/room-sheet.js), `tab-` for the
 * panels js/tabs.js builds.
 */

/* ============================================================ masthead */

.psn-mast-grid {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(15rem, 19rem);
  grid-template-areas:
    'back back     back'
    'face id       figs'
    '.    presence presence';
  column-gap: var(--s-6);
  row-gap: var(--s-5);
  align-items: start;
}
.band > .psn-mast-grid > * + * { margin-top: 0; }

.psn-back     { grid-area: back; }
.psn-portrait { grid-area: face; }
.psn-id       { grid-area: id; display: grid; gap: var(--s-3); min-width: 0; }
.psn-figs     { grid-area: figs; }
.psn-presence-slot { grid-area: presence; min-width: 0; }

.psn-portrait .avatar { box-shadow: 0 0 0 1px var(--rule-soft); }

.psn-kicker {
  justify-self: start;
  padding-top: var(--s-2);
  border-top: var(--edge-strong);
}
.psn-name {
  overflow-wrap: anywhere;
  text-wrap: balance;
}
.psn-badges { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.psn-badges:empty { display: none; }

.psn-quiet { margin-top: 0; font-size: var(--t-sm); color: var(--ink-3); }
.psn-quiet strong { color: var(--ink-2); font-weight: 600; }

.psn-bio {
  max-width: var(--w-text);
  color: var(--ink-2);
  line-height: var(--lh-normal);
  white-space: pre-line;
}
.psn-retired { max-width: var(--w-text); }

/* ---- ban tag (views/user/bans.js, ADR-0002). --danger, as on .alert-danger:
 * this is a state of the account, not a measurement, so it takes none of the
 * frozen --live / --warn / --void meanings. A hairline rule on the left, not a
 * filled box: the reason beside it is the content, the flag only labels it. */
.psn-ban-tag {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1) var(--s-3);
  max-width: var(--w-text);
  padding-left: var(--s-3);
  border-left: var(--bw-2) solid var(--danger);
}
.psn-ban-flag { color: var(--danger); font-weight: 650; }
.psn-ban-tag .psn-ban-reason { color: var(--ink-2); font-size: var(--t-sm); overflow-wrap: anywhere; }

/* The figures stack in the right-hand column, each on its own hairline. */
.psn-figs .stat-run { flex-direction: column; gap: var(--s-4); }
.psn-figs .stat-run[data-rule='1'] > .stat + .stat {
  padding-left: 0;
  border-left: 0;
  margin-left: 0;
  padding-top: var(--s-4);
  border-top: var(--edge-soft);
}
.psn-figs .stat-run > .stat { flex: none; }

@media (max-width: 68.6875rem) {
  .psn-mast-grid {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      'back     back'
      'face     id'
      'presence presence'
      'figs     figs';
  }
  .psn-figs .stat-run { flex-direction: row; }
  .psn-figs .stat-run[data-rule='1'] > .stat + .stat {
    padding-top: 0;
    border-top: 0;
    padding-left: var(--s-5);
    border-left: var(--edge-soft);
  }
  .psn-figs .stat-run > .stat { flex: 1 1 8rem; }
}

/* Phone: every stat run on this page becomes a two-column grid. As a
 * wrapping flex row, the tile that wrapped to a new line kept its left
 * hairline and indent and looked orphaned (seen at 360px). */
@media (max-width: 44.9375rem) {
  .psn-figs .stat-run,
  .psn-act-figs .stat-run {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4) var(--s-4);
  }
  .psn-figs .stat-run[data-rule='1'] > .stat + .stat,
  .psn-act-figs .stat-run[data-rule='1'] > .stat + .stat {
    margin-left: 0;
    padding-left: var(--s-4);
    border-left: var(--edge-soft);
  }
  .psn-figs .stat-run[data-rule='1'] > .stat:nth-child(odd),
  .psn-act-figs .stat-run[data-rule='1'] > .stat:nth-child(odd) {
    padding-left: 0;
    border-left: 0;
  }
  .psn-figs .stat-run > .stat:nth-child(n + 3),
  .psn-act-figs .stat-run > .stat:nth-child(n + 3) {
    padding-top: var(--s-4);
    border-top: var(--edge-soft);
  }
}

@media (max-width: 44.9375rem) {
  .psn-mast-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'back' 'face' 'id' 'presence' 'figs';
    row-gap: var(--s-4);
  }
  .psn-portrait .avatar { width: 72px; height: 72px; font-size: 1.75rem; }
}

/* ---- the presence sentence: the answer, set in the publication's voice */
.psn-presence {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--s-5);
  row-gap: var(--s-3);
  align-items: end;
  padding-top: var(--s-4);
  border-top: var(--edge);
}
.psn-presence-main { display: grid; gap: var(--s-2); min-width: 0; }
.psn-presence-state { display: flex; align-items: center; gap: var(--s-2); }
.psn-presence[data-state='online'] .psn-presence-state { color: var(--live); }
.psn-presence-line {
  max-width: 38ch;
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-weight: 440;
  line-height: 1.3;
  letter-spacing: var(--tr-title);
  text-wrap: pretty;
  overflow-wrap: anywhere;
}
.psn-presence-line .num { font-weight: 600; }
.psn-presence-group {
  color: var(--accent);
  font-style: italic;
  text-decoration: underline;
  text-decoration-color: var(--accent-line);
  text-underline-offset: 3px;
}
.psn-presence-group:hover { text-decoration-color: currentColor; }

@media (max-width: 44.9375rem) {
  .psn-presence { grid-template-columns: minmax(0, 1fr); }
}

/* ================================================================ tabs */

/* The strip sticks under the nameplate. The Groups tab runs to twelve entries
 * with drawers; switching tabs from the bottom of it should not mean scrolling
 * back past the masthead first. Same opaque sheet + grain as the topbar, so it
 * reads as part of the masthead rather than a floating toolbar. */
.psn-tabs {
  position: sticky; top: var(--topbar-h); z-index: 30;
  padding-block: var(--s-4) 0;
  background-color: var(--bg);
  background-image: var(--grain);
}
.band-lede + .psn-tabs { border-top: 0; }

/* On a phone the last tabs sit past the edge, and a strip with no visible
 * scrollbar gives no hint they exist. The right edge fades while there is more
 * to scroll and clears once the reader reaches the end — driven by the strip's
 * own scroll, so it only ships where scroll timelines exist; elsewhere the
 * strip is unchanged rather than permanently fading its last tab. */
@supports (animation-timeline: scroll()) {
  .psn-tabs .tab-strip {
    animation: psn-strip-edge linear both;
    animation-timeline: scroll(self inline);
  }
}
@keyframes psn-strip-edge {
  from { mask-image: linear-gradient(90deg, #000 calc(100% - 3rem), transparent); }
  99%  { mask-image: linear-gradient(90deg, #000 calc(100% - 3rem), transparent); }
  to   { mask-image: none; }
}

/* The panels are bands of their own, laid straight onto the canvas grid. */
.tab-panels { display: contents; }
.tab-panels > .band { border-top: 0; }
.tab-band { padding-top: var(--s-6); }

/* ========================================================= groups tab */

.psn-groups { border-top: var(--edge); }
.psn-groups > li { border-bottom: var(--edge-soft); }
.psn-group { display: grid; gap: var(--s-3); padding-block: var(--s-4); min-width: 0; }

.psn-group-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-3);
}
.psn-group-title {
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-weight: 520;
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-title);
}
.psn-group-title .is-untitled { font-style: italic; font-weight: 420; color: var(--ink-2); }
.psn-group-title .group-link:hover { color: var(--accent); }

.psn-faces { display: flex; flex-wrap: wrap; align-items: center; gap: 0; }
.psn-face {
  display: inline-grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: var(--r-full);
}
.psn-face:hover .avatar { box-shadow: 0 0 0 2px var(--accent); }
/* Only set when the page may speak in the present tense (rooms-tab.js). */
.psn-face.is-here .avatar { box-shadow: 0 0 0 2px var(--live); }
.psn-face-more {
  display: inline-grid; place-items: center;
  min-width: 44px; height: 44px;
  padding: 0 var(--s-2);
  border-radius: var(--r-full);
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-2xs);
}
.psn-face-more:hover { background: var(--ink-wash-2); color: var(--ink); }

.psn-group-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--s-3);
}
.psn-group-foot .claim { margin-top: 0; }

.psn-drawer {
  padding: var(--s-4);
  background: var(--bg-sunk);
  border-left: var(--edge-strong);
  box-shadow: inset 0 1px 0 var(--halo);
}

/* ---- the group drawer (views/user/room-sheet.js) */
.gsheet { display: grid; gap: var(--s-4); min-width: 0; }
.gsheet-line { margin-top: 0; font-size: var(--t-sm); }
.gsheet-line strong { color: var(--ink); }

.gsheet-scroll {
  max-height: 28rem;
  overflow: auto;
  scrollbar-gutter: stable;
  border-block: var(--edge-hair);
}
.gsheet-scroll > * { min-width: 32rem; }
.gsheet-axis {
  position: sticky; top: 0; z-index: 1;
  padding-block: var(--s-1);
  background: var(--bg-sunk);
  border-bottom: var(--edge-hair);
}
.gsheet-ticks { height: 1.1rem; }
.gsheet-tick {
  position: absolute; top: 0;
  transform: translateX(-50%);
  white-space: nowrap;
  font-family: var(--viz-font);
  font-size: var(--viz-tick-size);
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.gsheet-rows > li { padding-block: var(--s-2); border-bottom: var(--edge-hair); }
.gsheet-rows > li:last-child { border-bottom: 0; }
.gsheet-who { display: grid; gap: 2px; min-width: 0; }
.gsheet-who .entity-name { font-size: var(--t-sm); }
.gsheet-times {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-3xs);
  color: var(--ink-3);
}
.gsheet-still { color: var(--ink-2); font-weight: 600; font-family: var(--font-text); }
.gsheet-bar { background: var(--data-4); }
/* No observed end: the bar fades rather than asserting a departure. */
.gsheet-bar.is-fading {
  background: linear-gradient(90deg, var(--data-4) 0%, var(--data-4) 72%, transparent 100%);
}
.gsheet-bar.is-approx { opacity: 0.55; box-shadow: inset 0 0 0 1px var(--rule); }
.gsheet-row.is-here .gsheet-bar:last-child { background: var(--viz-emph); }
.gsheet-row.is-here .gsheet-bar.is-fading:last-child {
  background: linear-gradient(90deg, var(--viz-emph) 0%, var(--viz-emph) 72%, transparent 100%);
}
.gsheet-dur { font-size: var(--t-xs); text-align: right; }

.gsheet-facts { display: grid; gap: var(--s-3); }
.gsheet-dl .meta-row { grid-template-columns: minmax(6.5rem, 8rem) minmax(0, 1fr); }
.gsheet-dl a { color: var(--accent); }
.gsheet-changes > summary {
  display: flex; align-items: center;
  min-height: 44px;
  color: var(--accent);
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
}
.gsheet-changes > .table-wrap { margin-top: var(--s-2); }

/* ====================================================== ban history */

/* The disclosure follows .grp-visit-log: accent summary, chevron, 44px target. */
.psn-bans { padding-block: var(--s-3); }
.psn-ban-summary {
  display: flex; align-items: center; gap: var(--s-2);
  min-height: 44px;
  color: var(--accent);
  font-weight: 600;
  font-size: var(--t-sm);
  cursor: pointer;
  list-style: none;
}
.psn-ban-summary::-webkit-details-marker { display: none; }
.psn-ban-summary::before {
  content: '';
  width: 0.45em; height: 0.45em;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--dur-2) var(--ease-both);
}
.psn-ban-log[open] > .psn-ban-summary::before { transform: rotate(45deg); }
.psn-ban-log > .note { margin-top: var(--s-3); max-width: var(--w-text); }

.psn-ban-list { list-style: none; margin: 0; padding: 0; border-top: var(--edge-soft); }
.psn-ban-item {
  display: grid;
  grid-template-columns: minmax(8rem, 11rem) minmax(0, 1fr);
  column-gap: var(--s-5);
  row-gap: var(--s-1);
  padding-block: var(--s-3);
  border-bottom: var(--edge-soft);
}
.psn-ban-when { font-size: var(--t-xs); color: var(--ink-2); }
.psn-ban-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  margin: 0; font-size: var(--t-sm); color: var(--ink-2);
}
.psn-ban-body > .psn-ban-reason { margin-top: var(--s-1); overflow-wrap: anywhere; }
.psn-ban-approx { font-size: var(--t-xs); }

@media (max-width: 40rem) {
  .psn-ban-item { grid-template-columns: minmax(0, 1fr); }
}

/* ====================================================== history tab */

.psn-history { border-top: var(--edge); }
.psn-history-item {
  display: grid;
  grid-template-columns: minmax(8rem, 11rem) minmax(0, 1fr);
  column-gap: var(--s-5);
  row-gap: var(--s-2);
  padding-block: var(--s-4);
  border-bottom: var(--edge-soft);
}
.psn-history-when { font-size: var(--t-xs); color: var(--ink-2); }
.psn-history-body { display: grid; gap: var(--s-3); min-width: 0; }

.psn-counters { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-6); }
.psn-counter { display: grid; gap: var(--s-1); }
.psn-counter-steps { text-transform: none; letter-spacing: 0; font-family: var(--font-text); }
.psn-counter-value { display: flex; align-items: baseline; gap: var(--s-2); }
.psn-counter-value > .num { font-size: var(--t-fig); font-weight: 600; }
.psn-counter.is-idle dd { font-size: var(--t-sm); }

.psn-edits { display: grid; gap: var(--s-2); }
.psn-edit { display: grid; gap: 2px; min-width: 0; }
.psn-edit-field { font-size: var(--t-sm); font-weight: 600; }
.psn-edit-pair {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s-1) var(--s-2);
}
.psn-edit-arrow { color: var(--ink-3); }

@media (max-width: 29.9375rem) {
  .psn-history-item { grid-template-columns: minmax(0, 1fr); }
}

/* ===================================================== activity tab */

.psn-year { margin-top: var(--s-5); }
.psn-yr-note { margin-top: var(--s-3); }

/* ---- the year chart (views/user/year-chart.js)
 *
 * Caption left, Squares / Bars right, both sitting on one hairline: the
 * switch's active rule IS that hairline, thickened under the chosen view. */
.psn-yr { gap: var(--s-3); }
.psn-yr-bar {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 0 var(--s-4);
  border-bottom: var(--edge-soft);
}
.psn-yr-caption {
  margin: 0;
  padding-block: var(--s-2);
  color: var(--ink-2);
  font-size: var(--t-sm);
  text-wrap: pretty;
}
/* Pulled right by its own item padding so the last label meets the edge. */
.psn-yr-switch { border-bottom: 0; margin-right: calc(-1 * var(--s-3)); }
.psn-yr-opt { gap: var(--s-2); }
.psn-yr-glyph {
  flex: none;
  fill: currentColor;
  opacity: 0.45;
  transition: opacity var(--dur-2) var(--ease-both), transform var(--dur-2) var(--ease-out);
}
.psn-yr-opt:hover .psn-yr-glyph { opacity: 0.8; transform: translateY(-1px); }
.psn-yr-opt[aria-pressed='true'] .psn-yr-glyph { opacity: 1; transform: none; }
.psn-yr-opt[aria-pressed='true'] { cursor: default; }
.psn-yr-opt:active:not([aria-pressed='true']) { color: var(--accent); }

/* The stage scrolls on a phone and fills the column on a desktop — but never
 * past 1.3x, because a young database is three weeks wide and three weeks
 * blown up to 1200px is three giant blocks, not a chart. --yr-w is the
 * drawing's natural width, set inline by the renderer. */
.psn-yr-stage { padding-block: var(--s-2) var(--s-1); border-radius: var(--r-1); }
.psn-yr-stage .year-svg {
  width: 100%;
  min-width: var(--yr-w);
  max-width: calc(var(--yr-w) * 1.3);
}

.psn-bar { fill: var(--data-4); transition: fill var(--dur-1) var(--ease-both); }
.psn-bar.is-partial { fill: transparent; stroke: var(--data-4); stroke-width: 1; }
.psn-bar.is-zero { fill: var(--rule); }
/* The one mark the chart's accessible summary is about: the busiest week. */
.psn-bar.is-peak { fill: var(--viz-emph); }
.psn-bar:hover { fill: var(--ink); stroke: var(--ink); }

.psn-yr-key { width: 12px; height: 12px; border-radius: var(--r-1); }
.psn-yr-key.is-full { background: var(--data-4); }
.psn-yr-key.is-partial { box-shadow: inset 0 0 0 1px var(--data-4); }

/* The switch is a morph, column for column: each week's seven squares give
 * way to the bar standing in the same place, swept left to right. Only a
 * switch the reader made carries data-enter; first paint stays still. */
@media (prefers-reduced-motion: no-preference) {
  .psn-bars[data-enter] .psn-bar,
  .year-svg[data-enter='squares'] .year-cell {
    transform-box: fill-box;
    transform-origin: 50% 100%;
    animation: psn-rise var(--dur-4) var(--ease-out) both;
    animation-delay: calc(var(--col, 0) * 7ms);
  }
  .year-svg[data-enter='squares'] .year-cell {
    transform-origin: 50% 50%;
    animation-name: psn-settle;
    animation-duration: var(--dur-3);
  }
}
@keyframes psn-rise { from { transform: scaleY(0); } }
@keyframes psn-settle { from { opacity: 0; transform: scale(0.4); } }

/* ===================================================== partners tab */

.psn-partner-name { color: var(--ink); }
.psn-partner-name:hover { color: var(--accent); }
.psn-partner-compare {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--accent-line);
  text-underline-offset: 2px;
}
.psn-partner-compare:hover { text-decoration-color: currentColor; }

.psn-follow {
  display: grid;
  gap: var(--s-3);
  margin-top: var(--s-6);
  padding-top: var(--s-4);
  border-top: var(--edge-strong);
}
.psn-follow:empty { display: none; }
.psn-follow-group { display: grid; gap: var(--s-2); margin-top: var(--s-3); }
.psn-follow-list { border-top: var(--edge-soft); }
.psn-follow-row {
  display: grid;
  gap: var(--s-1);
  padding-block: var(--s-3);
  border-bottom: var(--edge-hair);
}
.psn-follow-row .claim { margin-top: 0; padding-left: calc(28px + var(--s-2)); }

@media (max-width: 29.9375rem) {
  .psn-follow-row .claim { padding-left: 0; }
}

/* ====================================================== rhythm tab
 *
 * The SENTENCE is the product; the grid under it is its footnote. */

.psn-catch-line {
  max-width: 30ch;
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  font-weight: 460;
  line-height: 1.2;
  letter-spacing: var(--tr-title);
  text-wrap: balance;
}
.psn-catch-line strong { font-weight: 650; }
.psn-catch-detail { color: var(--ink-3); font-size: var(--t-sm); }
.psn-rhythm .week { margin-top: var(--s-5); }
.psn-rhythm-facts { display: grid; gap: var(--s-3); }
.psn-rhythm-facts > * + * { padding-top: var(--s-3); border-top: var(--edge-soft); }
.psn-rhythm-fact { color: var(--ink-2); font-size: var(--t-sm); line-height: var(--lh-relax); text-wrap: pretty; }

/* Legend swatches for the week grid's three cell states and its outline. */
.legend-key[data-swatch='seen'] .legend-swatch { background: var(--data-5); }
.legend-key[data-swatch='empty'] .legend-swatch {
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--rule-soft);
}
.legend-key[data-swatch='window'] .legend-swatch {
  background: none;
  box-shadow: inset 0 0 0 1.25px var(--ink);
}
