.panel {
  --panel-width: 50ch;
  --panel-padding: 1em;
  --panel-shadow:
    0 0 0 1px oklch(var(--lch-always-black) / 0.02),
    0 .2em 1.6em -0.8em oklch(var(--lch-always-black) / 0.2),
    0 .4em 2.4em -1em oklch(var(--lch-always-black) / 0.3),
    0 .4em .8em -1.2em oklch(var(--lch-always-black) / 0.4),
    0 .8em 1.2em -1.6em oklch(var(--lch-always-black) / 0.5),
    0 1.2em 1.6em -2em oklch(var(--lch-always-black) / 0.6);

  inline-size: var(--panel-width);
  margin: var(--block-space-double) auto var(--block-space);
  max-inline-size: 100vw;
  padding: var(--panel-padding);

  @media (min-width: 1024px) {
    --panel-padding: 2em;

    box-shadow: var(--panel-shadow);
    background-color: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 0.8em;
  }
}

/* Room-edit tabs ride the shared segmented-control look */
/* Room settings — an in-shell column: tab pill, then the settings cards */
body.room-settings #main-content {
  justify-content: flex-start;
}

/* The settings stack: labelled cards down one column. The room settings page
   and the room info panel are the same surface at two widths, so they share it
   and each adds only what its own frame costs. */
.room-settings-page,
.roster {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 16px 18px 40px;

  [role="tabpanel"]:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  /* The stack's gap does the spacing between labelled cards; the label hugs
     its own card instead */
  .settings-label {
    margin-block-end: -10px;
  }
}

/* The roster panel reuses the thread-panel header above a body inset by 18px,
   so its header matches that inset (instead of the panel default) to keep the
   room name flush over the meta line, description and cards beneath it. */
.thread-panel__header[data-panel-kind="roster"] {
  padding-inline: 18px;
}

.room-settings-page {
  max-inline-size: 676px;
}

/* The name/description card (labels + inputs bring their own rhythm, so no
   per-child hairlines here) */
.room-settings-card {
  padding-block: 18px;

  > * {
    padding-block: 0;
  }

  > * + * {
    border-block-start: none;
  }
}

.room-settings-form__submit {
  margin-block-start: 18px;
}

.room-settings-form__name {
  font-size: 1.1875rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}

.setting-row--disabled {
  opacity: 0.55;
}

.room-tabs {
  align-items: center;
  align-self: flex-start;
  background-color: var(--color-border);
  border-radius: var(--border-radius-pill);
  display: inline-flex;
  gap: 2px;
  padding: 3px;
}

.room-tabs [role="tab"] {
  background: none;
  border: none;
  border-radius: var(--border-radius-pill);
  color: var(--color-text-muted);
  cursor: pointer;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.2;
  padding: 5px 14px;
  text-align: center;
  white-space: nowrap;
}

.room-tabs [role="tab"][aria-selected="true"] {
  background-color: var(--color-bg);
  box-shadow: var(--shadow-raise);
  color: var(--color-text);
}

.room-tabs [role="tab"]:hover:not([aria-selected="true"]) {
  color: var(--color-text);
}

.panel--wide {
  --panel-width: 80ch;
}

.panel--centered {
  --panel-width: 28rem;
  --panel-padding: 1.5em;
  max-inline-size: calc(100vw - 2rem);
  margin: 0;
  box-shadow: var(--panel-shadow);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 0.8em;
}

.panel--centered fieldset {
  min-inline-size: 0;
}

@media (max-width: 480px) {
  .panel--centered .txt-large {
    font-size: 1.1rem;
  }
}

/* Members directory: small-caps heading above a bordered card of list rows.
   The card keeps the {role}s_section id — Turbo Streams append rows to it. */
/* Members directory — one bordered roster card. Role groups are sunk header
   bands inside it, not separate cards, so the page reads as a single list. */
.members-roster {
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  overflow: hidden;
}

/* Groups with no one in them are omitted rather than shown empty. */
.members-roster__group:not(:has(turbo-frame[id^="user_"])) {
  display: none;
}

.members-roster__band {
  align-items: baseline;
  background-color: var(--color-bg-sunk);
  border-block: 1px solid var(--color-border);
  display: flex;
  gap: 8px;
  padding: 9px 15px;
}

/* The first band rides the card's own top border rather than doubling it. */
.members-roster > :first-child .members-roster__band,
.members-roster > .members-roster__band:first-child {
  border-block-start: 0;
}

.members-roster__band-label {
  color: var(--color-text-muted);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.members-roster__band-count {
  color: var(--color-text-subtle);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}

/* Rows sit directly on the card; the band carries the separation. */
.role-section {
  border: 0;
  border-radius: 0;
}

/* Deactivated and banned rows gray the NAME only — the avatar stays at full
   opacity so a dimmed row never reads as still loading. */
.is-deactivated .list-row__title strong,
.is-banned .list-row__title strong {
  color: var(--color-text-subtle);
}

.status-chip {
  border-radius: 4px;
  flex: none;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  text-transform: uppercase;
}

.status-chip--deactivated {
  background-color: var(--color-bg-sunk);
  color: var(--color-text-subtle);
}

.status-chip--banned {
  background-color: var(--color-bg-negative);
  color: var(--color-negative);
}

/* Member row extras on the shared .list-row rhythm */
.member-row__avatar {
  position: relative;
}

.member-row__dot {
  border: 2px solid var(--color-bg);
  block-size: 10px;
  inline-size: 10px;
  inset-block-end: -2px;
  inset-inline-end: -2px;
  position: absolute;
}

.member-badge {
  --badge-color: var(--color-accent-brand);

  background-color: color-mix(in srgb, var(--badge-color) 14%, transparent);
  border-radius: 4px;
  color: var(--badge-color);
  flex: none;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  text-transform: uppercase;
}

/* A role is a system fact, not a community-chosen badge: neutral, never the
   accent, so it can sit beside a coloured badge without reading as one. */
.member-badge--role {
  --badge-color: var(--color-text-muted);

  align-items: center;
  display: inline-flex;
  gap: 3px;
}

/* Room settings → Members tab: the roster/search results as a bordered, scrolling
   card of .list-row rows. */
.member-tab-list {
  border: 1px solid var(--color-border);
  border-radius: 12px;
  max-block-size: 60vh;
  overflow-y: auto;
}

.member-tab-group-label {
  padding: 10px 15px 4px;
}

/* Quiet outlined chip marking the room creator in the members roster. */
.roster-chip {
  border: 1px solid var(--color-border);
  border-radius: 4px;
  color: var(--color-text-subtle);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  text-transform: uppercase;
}

/* Members directory page — an in-shell column: toolbar row (search + status
   tabs), then the role-section cards */
body.members #main-content {
  justify-content: flex-start;
}

.members-page {
  /* No horizontal padding: the settings shell's inner already insets the
     column, and adding more here pushes the toolbar and roster out of line
     with the settings-header above them. */
  padding: 0 0 28px;
}

.members-toolbar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding-block: 14px 12px;
}

.members-toolbar__form {
  flex: 1;
  max-inline-size: 340px;
  min-inline-size: 220px;
}

.members-toolbar__search {
  inline-size: 100%;
}

.members-list {
  max-inline-size: 820px;
}

.members-page .member-row {
  flex-wrap: nowrap;
}

/* Role control lives in a fixed column so every caret lines up on the right. */
.member-controls {
  align-items: center;
  display: flex;
  flex: none;
  gap: 8px;
  inline-size: 130px;
  justify-content: flex-end;
}

/* Ghost button that opens the manage-member modal — border and fill appear on
   hover only. */
.member-role__control {
  align-items: center;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--color-text-muted);
  cursor: pointer;
  display: inline-flex;
  font-size: 0.78125rem;
  font-weight: 600;
  gap: 6px;
  padding: 5px 10px;
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .member-role__control:hover {
    background-color: var(--color-bg-hover);
    border-color: var(--color-border);
  }
}

.member-role__caret {
  font-size: 0.5625rem;
  opacity: 0.7;
}

/* Your own row, or a viewer who can't act — the role renders as plain text. */
.member-role__readonly {
  color: var(--color-text-subtle);
  font-size: 0.78125rem;
  font-weight: 600;
  padding: 5px 10px;
}

/* Lazy-page sentinel — three placeholder rows on the real row rhythm, fading
   away, so the incoming frame swaps in where they sat instead of pushing the
   list down. The frame needs its own box (turbo-frame is display:contents by
   default) or Turbo's lazy IntersectionObserver has nothing to observe and the
   next page never loads. */
.members-skeleton {
  display: block;
}

.member-skeleton {
  align-items: center;
  border-block-end: 1px solid var(--color-border);
  display: flex;
  gap: 11px;
  opacity: calc(1 - var(--row) * 0.35);
  padding: 12px 15px;
}

.member-skeleton:last-child {
  border-block-end: 0;
}

.member-skeleton__avatar {
  background-color: var(--color-bg-sunk);
  block-size: 32px;
  border-radius: 50%;
  flex: none;
  inline-size: 32px;
}

.member-skeleton__lines {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 6px;
}

.member-skeleton__bar {
  background-color: var(--color-bg-sunk);
  block-size: 9px;
  border-radius: 5px;
}

.member-skeleton__bar--name {
  inline-size: 40%;
}

.member-skeleton__bar--sub {
  inline-size: 26%;
}

.member-skeleton__role {
  background-color: var(--color-bg-sunk);
  block-size: 24px;
  border-radius: 8px;
  flex: none;
  inline-size: 92px;
}

/* Browse rooms: an in-shell column holding a bordered card of list rows */
body.browse #main-content {
  justify-content: flex-start;
}

.browse-page {
  padding: 16px 18px 30px;
}

.browse-card {
  border: 1px solid var(--color-border);
  border-radius: 12px;

  .browse-page & {
    max-inline-size: 760px;
  }
}

.browse-room__glyph {
  align-items: center;
  display: inline-flex;
  flex: none;
  inline-size: 20px;
  justify-content: center;
}

/* Non-action state for rooms you're already in */
.browse-room__joined {
  border: 1px solid var(--color-border);
  border-radius: 8px;
  color: var(--color-text-muted);
  display: inline-block;
  font-size: 0.78125rem;
  font-weight: 600;
  padding: 6px 13px;
}

/* Size belongs to the surface, not here. This used to carry width/height in em,
   which are the same properties the surfaces set in their logical form — so with
   equal specificity the winner came down to which file the asset loader happened
   to sort first, and the identical dot rendered 8px in the inbox and 10px in the
   sidebar. Every surface names its own size now; a dot that sits inline rather
   than on an avatar uses --inline below. */
.status-dot {
  border-radius: 50%;
  flex-shrink: 0;
  align-self: center;
}

/* Dots in a text row (the group-DM participant cards) rather than pinned to an
   avatar corner: no positioning to inherit, so the size is stated outright. */
.status-dot--inline {
  block-size: 10px;
  inline-size: 10px;
}

.status--active  { background-color: var(--color-positive); }
.status--away    { background-color: var(--color-alert); }
.status--dnd     { background-color: var(--color-negative); }
.status--offline { background-color: var(--color-border-darker); }

/* Invisible is only your own dot — a hollow ring, so you stay reminded you're
   hidden rather than reading it as offline. Everyone else sees you as offline. */
.status--invisible { background-color: transparent; box-shadow: inset 0 0 0 1.5px var(--color-border-darker); }

@media (max-width: 480px) {
  /* Keep the row on one line — the compact role control sizes to its content
     rather than reserving the desktop 130px alignment column, so the name
     keeps room and nothing wraps to an awkward second line. */
  .members-page .member-row .member-controls {
    inline-size: auto;
  }
}
