/* entity.css — people, groups, and the marks that describe a group.
 *
 * The second half of this file is the group-attribute family, which is new.
 * The reader is a learner deciding where to practise, so a group's language
 * pair, level, mic policy, lock state and capacity are not metadata — they are
 * the decision. They used to be five ad-hoc chips on one page and nothing
 * anywhere else.
 */

/* -------------------------------------------------------------- avatars
 *
 * A transparent <img> revealed on `load` over an initials fallback: a blocked
 * or slow remote host degrades to the initials instead of a broken-image glyph,
 * with zero layout shift either way. */
.avatar {
  --avatar-size: 32px;
  position: relative; flex: none;
  display: inline-grid; place-items: center;
  width: var(--avatar-size); height: var(--avatar-size);
  border-radius: var(--r-full); overflow: hidden;
  background: var(--surface-2); color: var(--ink-3);
  font-size: calc(var(--avatar-size) * 0.4); font-weight: 600;
  line-height: 1;
  user-select: none;
}
.avatar-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; outline: none;
  transition: opacity 200ms var(--ease-out);
}
.avatar-img.is-loaded { opacity: 1; }

/* ONE face pile. There used to be two different components called .face-more —
 * a 24px overflow counter and a 40px dashed button — and whichever sheet
 * loaded last won everywhere. */
.face-pile { display: flex; align-items: center; }
.face-pile .avatar { margin-left: -6px; box-shadow: 0 0 0 2px var(--bg); }
.face-pile .avatar:first-child { margin-left: 0; }

.face-more {
  margin-left: -6px;
  display: inline-grid; place-items: center;
  width: var(--avatar-size, 24px); height: var(--avatar-size, 24px);
  border-radius: var(--r-full);
  background: var(--surface-2); box-shadow: 0 0 0 2px var(--bg);
  color: var(--ink-3);
  font-size: var(--t-3xs);
  font-variant-numeric: tabular-nums;
}
a.face-more:hover { background: var(--ink-wash-2); color: var(--ink); }

/* --------------------------------------------------------------- entity */

.entity { display: inline-flex; align-items: center; gap: var(--s-2); min-width: 0; }
.entity-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color var(--dur-1);
}
a.entity:hover .entity-name { color: var(--accent); }

/* The baseline-aligned variant, for a name inside running text. */
.entity-inline {
  display: inline; align-items: baseline;
  color: var(--accent);
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--accent-line);
}
.entity-inline:hover { text-decoration-color: currentColor; }

.group-link { color: inherit; }
.group-link:hover { color: var(--accent); }

/* -------------------------------------------------------- badge and chip */

.badge {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: 1px 5px;
  border: var(--edge-hair); border-radius: var(--r-1);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--t-3xs); font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}
.badge[data-tone='live'] { color: var(--live); border-color: var(--live-quiet); }
.badge[data-tone='warn'] { color: var(--warn); border-color: var(--warn-quiet); }
.badge[data-tone='void'] { color: var(--void); font-style: italic; }

.chip {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: 2px 7px;
  border: var(--edge-soft); border-radius: var(--r-1);
  color: var(--ink-2);
  font-size: var(--t-2xs);
  line-height: 1.6;
  white-space: nowrap;
}
.chip[data-tone='live'] { color: var(--live); border-color: var(--live-quiet); }
.chip[data-tone='warn'] { color: var(--warn); border-color: var(--warn-quiet); }

/* --------------------------------------------------------- presence dots
 *
 * One component, one scoped selector. The old sheet had an unscoped `.is-online`
 * sibling rule that flung a dot 1,100px across the page when an unrelated
 * element happened to carry the class. */
.presence-dot {
  flex: none;
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: var(--r-full);
  background: var(--ink-3);
  vertical-align: baseline;
}
.presence-dot[data-size='md'] { width: 9px; height: 9px; }
.presence-dot[data-state='online'] {
  background: var(--live);
  animation: pulse 2.4s ease-out infinite;
}
.presence-dot[data-state='offline'] { background: var(--rule); }
.presence-dot[data-state='unknown'] {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--void);
}

.live-dot {
  flex: none;
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: var(--r-full);
  background: var(--live);
  animation: pulse 2.4s ease-out infinite;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in oklab, var(--live) 55%, transparent); }
  70%  { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ==========================================================================
 * GROUP ATTRIBUTES — ACHROMATIC BY RULE
 *
 * A group property is not a warning, a measurement or a liveness state, so it
 * may not borrow --warn, --live or --void. It earns attention through weight,
 * a drawn glyph and position. The old `.chip-lock { color: var(--warn) }` spent
 * the stale-data hue on "this group is locked", which is exactly the kind of
 * collision the colour contract exists to prevent.
 *
 * Canonical order, left to right, EVERYWHERE:
 *   language pair · level · mic · locked · capacity
 * A reader who learns the order on the group page can scan it on a search
 * result, which is the entire point of a designed run.
 * ======================================================================== */

/* A language PAIR is one relationship, not two labels. 38% of groups are a
 * pair, and the pairing is the proposition of a practice group. */
.lang-pair {
  display: inline-flex; align-items: baseline; flex-wrap: wrap;
  gap: 0;
  min-width: 0;
  color: var(--ink);
  font-family: var(--font-text);
  line-height: var(--lh-snug);
}
.lang-pair-lang { white-space: nowrap; }
.lang-pair-join {
  color: var(--ink-3);
  font-size: 0.85em;
  margin-inline: var(--s-1);
}
.lang-pair[data-size='lg'] { font-size: var(--t-lg); font-weight: 550; }
.lang-pair[data-size='md'] { font-size: var(--t-base); }
.lang-pair[data-size='sm'] { font-size: var(--t-sm); }
.lang-pair.is-unknown { color: var(--ink-3); }

/* Level is ORDINAL, so it is drawn as ordinal: six ticks, n filled. A word chip
 * threw that away — six ordered values presented with no order, at the same
 * weight as the language beside them. */
.level-mark { display: inline-flex; align-items: center; gap: var(--s-1); flex: none; }
.level-ticks { flex: none; }
.level-tick-on  { fill: var(--attr-fill); }
.level-tick-off { fill: none; stroke: var(--attr-line); stroke-width: 1; }
.level-word { color: var(--attr-ink); font-size: var(--t-2xs); white-space: nowrap; }

/* One mark: a glyph plus its label. */
.attr {
  display: inline-flex; align-items: center; gap: var(--s-1);
  flex: none;
  color: var(--attr-ink);
  font-size: var(--t-2xs);
  line-height: var(--lh-snug);
  white-space: nowrap;
}
/* The one decisive mark on a row. `no_mic` is on a quarter of all groups and
 * "can I speak here" is the single most decisive bit for a learner, so it gets
 * the strong ink and the extra weight — and still no hue. */
.attr.is-decisive { color: var(--attr-ink-strong); font-weight: 600; }

.attr-glyph {
  flex: none;
  width: var(--attr-glyph); height: var(--attr-glyph);
  stroke: currentColor;
  stroke-width: var(--attr-glyph-w);
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  overflow: visible;
}

.attr-cap { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.attr-cap-sep, .attr-cap-max { color: var(--ink-3); }

/* The run. Marks wrap as a unit, separated by a 2px dot that is a real node —
 * never a literal space inside a string, because a joined string cannot be
 * restyled, re-ordered or hidden per breakpoint. */
.attr-run {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--attr-gap);
  min-width: 0;
}
.attr-run[data-size='md'] { font-size: var(--t-xs); --attr-glyph: 14px; }
.attr-run[data-size='lg'] { font-size: var(--t-sm); --attr-glyph: 16px; }
.attr-run[data-size='lg'] .attr, .attr-run[data-size='md'] .attr { font-size: inherit; }

.attr-sep {
  flex: none;
  width: 2px; height: 2px;
  border-radius: var(--r-full);
  background: var(--rule);
}

/* Compact drops the LABELS, never the marks: a glyph without its word still
 * carries the fact, and the fact is still announced, because the label moves to
 * .sr-only rather than being removed. Never compact on a detail page, and never
 * as the only rendering of the mic policy. */
.attr-run.is-compact { gap: var(--s-2); }
.attr-run.is-compact .attr { gap: 0; }

/* ----------------------------------------------------------- group title */

.group-title {
  font-family: var(--font-display);
  font-weight: 540;
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-title);
  overflow-wrap: anywhere;
}
.group-title-topic { display: inline; }
/* 69% of topics are blank, so for two groups in three the language pair IS the
 * title. A list of "Untitled group" rows is the failure mode designed out here. */
.group-title .lang-pair { font: inherit; letter-spacing: inherit; }
.group-title .lang-pair-join { font-size: 0.8em; font-weight: 400; }

.occ {
  display: inline-flex; align-items: baseline; gap: 1px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-sm);
  color: var(--ink);
}
.occ-sep, .occ-cap { color: var(--ink-3); }
.occ.is-empty { color: var(--ink-3); }

/* ==========================================================================
 * THE GROUP CARD — one open group, as a broadsheet entry
 *
 * The unit of "where should I go right now". Built by groupCard() in
 * js/ui/group.js and laid out by .gboard. Left: the head count, big, in the
 * measurement face — it is the one number a learner reads first. Right: the
 * topic in Fraunces (or, for the 69% of groups with no topic, the language
 * pair in italic — never "Untitled"), then the attribute run.
 *
 * The whole card is ONE link. It lifts on hover and on keyboard focus; it never
 * shrinks when pressed, because it is a record, not a control.
 * ======================================================================== */

.gboard {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
  column-gap: var(--s-5);
  /* The cards carry their own inline padding so the lift has room to show;
   * the board pulls them back so the text lines up with the band head. */
  margin-inline: calc(-1 * var(--s-3));
}
.gboard > li { min-width: 0; }

.gcard {
  --gcard-n-w: 3.5rem;
  position: relative;
  display: grid;
  grid-template-columns: var(--gcard-n-w) minmax(0, 1fr);
  grid-template-areas:
    'n title'
    'n marks';
  column-gap: var(--s-3);
  row-gap: var(--s-2);
  align-items: start;
  height: 100%;
  padding: var(--s-4) var(--s-3);
  border-top: var(--edge-soft);
  color: var(--ink);
  transition:
    transform var(--dur-3) var(--ease-spring),
    box-shadow var(--dur-3) var(--ease-out),
    background-color var(--dur-2) var(--ease-out);
}
.gcard:hover, .gcard:focus-visible {
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-hover);
  transform: translateY(var(--lift));
}
.gcard:focus-visible { outline-offset: 0; }

.gcard-n {
  grid-area: n;
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 3px;
  padding-top: 2px;
}
.gcard-n-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-fig-lg);
  font-weight: 520;
  line-height: var(--lh-flat);
  letter-spacing: var(--tr-fig);
}
.gcard-n-unit {
  color: var(--ink-3);
  font-size: var(--t-2xs);
  line-height: 1;
}

.gcard-title {
  grid-area: title;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: 520;
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-title);
  overflow-wrap: anywhere;
  transition: color var(--dur-2) var(--ease-out);
}
.gcard-title.is-untitled { font-style: italic; font-weight: 420; color: var(--ink-2); }
.gcard:hover .gcard-title, .gcard:focus-visible .gcard-title { color: var(--accent); }
.gcard-marks { grid-area: marks; min-width: 0; }

/* Dark: the card is a surface step, not a rule. */
:root[data-theme='dark'] .gcard:hover,
:root[data-theme='dark'] .gcard:focus-visible { background: var(--surface-2); }
