/* ===========================================================================
   PROFILE and ADMIN.
   ---------------------------------------------------------------------------
   `main` is already centred at --measure (1240px), and for ENCODE that is
   right: it is a dense workspace that wants the whole width. An account page
   inside the same container reads as an orphaned left column, because its
   content is nowhere near wide enough to fill it.

   So these two workspaces get a column of their own — a narrower max-width,
   centred with auto margins. The centring is structural: remove every
   text-align in this file and the layout is still centred, because it is the
   BOX that is centred, not the text inside it.

   Every colour, font and easing below is an existing token from cipher.css.
   Nothing here introduces a second design language.
   =========================================================================== */

/* --------------------------------------------------------------- the column */

.acct {
  /* Wide enough for a label and a value on one line, narrow enough that the
     eye does not have to travel. */
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 8px 0 40px;
}

/* The admin surface carries tables of users and content, so it is allowed
   more room — still a centred column, just a wider one. */
.acct-wide { max-width: 880px; }

/* ----------------------------------------------------------------- heading */

.acct-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 26px 0 22px;
  border-bottom: 1px solid var(--line-hair);
  margin: 0 0 26px;
}

/*
 * The mark: one small light, the same idea as the favicon.
 *
 * A radial gradient rather than an image or an icon font — it costs no
 * request, scales without assets, and matches the single-hue identity the
 * favicon already establishes.
 */
.acct-mark {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%,
      var(--accent-hot) 0%,
      var(--green-2) 34%,
      rgba(86, 217, 138, .10) 62%,
      transparent 72%);
}

/* Administration is the one place a different hue earns its keep: it marks a
   mode with consequences rather than decorating a page. */
.acct-mark-admin {
  background:
    radial-gradient(circle at 50% 50%,
      #e8c877 0%,
      var(--amber) 34%,
      rgba(200, 162, 74, .12) 62%,
      transparent 72%);
}

.acct-title {
  margin: 0;
  font-family: var(--f-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .34em;
  /* The letter-spacing pushes the whole word right of centre; half of it back
     as padding puts the optical centre where the geometric one is. */
  padding-left: .34em;
  text-transform: uppercase;
  color: var(--fg);
}

.acct-sub {
  margin: 0;
  font-family: var(--f-mono);
  font-size: 11.5px;
  color: var(--fg-3);
  overflow-wrap: anywhere;
  text-align: center;
}

/* ------------------------------------------------------------------ counts */

.statrow {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  list-style: none;
  margin: 0 0 26px;
  padding: 0;
  background: var(--line-hair);
  border: 1px solid var(--line-hair);
}

.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 13px 6px;
  background: var(--bg-1);
}

.stat-n {
  font-family: var(--f-mono);
  font-size: 17px;
  line-height: 1;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}

.stat-k {
  font-family: var(--f-sans);
  font-size: 8.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--fg-3);
  text-align: center;
}

/* ------------------------------------------------------------------- cards */

.card {
  border: 1px solid var(--line-soft);
  background: var(--bg-1);
  padding: 20px 22px 22px;
  margin: 0 0 18px;
}

.card-title {
  margin: 0 0 18px;
  padding: 0 0 12px;
  border-bottom: 1px solid var(--line-hair);
  font-family: var(--f-sans);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--fg-2);
}

/* The one card whose consequences differ gets a hairline to say so. */
.card-admin { border-color: rgba(200, 162, 74, .34); }
.card-admin .card-title { color: var(--amber); }

.card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 18px 0 0;
}

.act-key { border-color: var(--amber); color: var(--amber); }
.act-key:hover,
.act-key:focus-visible {
  color: #e8c877;
  border-color: #e8c877;
  background: rgba(200, 162, 74, .08);
}

/* ------------------------------------------------------------------ fields */

.field { margin: 0 0 16px; }

/* The last field in a card sits flush with the card's bottom edge.

   This stylesheet loads after cipher.css and is not scoped to the account
   panels, so the rule reaches every page — and in a control row, which is a
   flex line aligned to its bottom edge, `:last-of-type` picks out the last
   <label> and drops only that one. In the Encode row that was Voicing: no
   bottom margin, so it sat sixteen pixels below Cipher, Root and Reduction.

   Control rows are excluded rather than the rule being scoped to .acct.
   Scoping it was tried and measured: the rule has been shaping spacing on
   Ciphers, Practice and Progressions for long enough that removing it moved
   fields there by up to 32px. Excluding only the row children fixes the
   misalignment and leaves every other page exactly as it was. */
.field:last-of-type:not(.ctl-row > *) { margin-bottom: 0; }

.field-k {
  display: block;
  margin: 0 0 6px;
  font-family: var(--f-sans);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.field-in {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 11px;
  border: 1px solid var(--line-soft);
  background: var(--bg);
  color: var(--fg);
  font-family: var(--f-mono);
  font-size: 12.5px;
  transition: border-color .14s var(--ease);
}

.field-in::placeholder { color: var(--fg-3); opacity: .6; }
.field-in:focus { outline: none; border-color: var(--green-2); }

.field-in-code {
  font-size: 17px;
  letter-spacing: .42em;
  text-align: center;
  padding-left: .42em;
}

.field-note {
  margin: 6px 0 0;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--fg-3);
}

/* --------------------------------------------------------------- read-outs */

.readout { margin: 18px 0 0; }

.readout-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 9px 0;
  border-top: 1px solid var(--line-hair);
}
/* The Plan card's renewal row is the first .readout-row ever given `hidden`
   — without this, the explicit `display: flex` above beats the UA [hidden]
   rule at equal specificity and the row stays visible. */
.readout-row[hidden] { display: none; }

.readout-row dt {
  font-family: var(--f-sans);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-3);
  white-space: nowrap;
}

.readout-row dd {
  margin: 0;
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--fg);
  overflow-wrap: anywhere;
  text-align: right;
}

/* -------------------------------------------------------------- messages */

.pf-msg {
  min-height: 1.4em;
  margin: 0 0 16px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--fg-2);
}

.pf-msg:empty { min-height: 0; margin: 0; }
.pf-msg[data-tone="bad"]  { color: var(--red); }
.pf-msg[data-tone="good"] { color: var(--green); }

/* ------------------------------------------------------------------ lists */

.pf-sec {
  margin: 22px 0 8px;
  font-family: var(--f-sans);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.pf-sec:first-of-type { margin-top: 0; }

/* What a PRO subscription actually includes, shown only to a PRO account.
   Marked off by a rule rather than a box: it is a continuation of the Plan
   card, not a second card inside it. The amber edge matches PRO's accent
   everywhere else it appears. */
.pf-benefits {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  border-left: 2px solid var(--amber-2, #a8842f);
  padding-left: 14px;
}
/* An explicit `display` is not set above, so [hidden] would already work —
   this is here so that adding one later cannot silently reveal the section
   to every free account, which is the exact failure this project has hit
   once before with .pub-get and .readout-row. */
.pf-benefits[hidden] { display: none; }

.pf-list { list-style: none; margin: 0; padding: 0; }

/* A saved item that can be reopened. The name becomes the control, so the
   target is the words somebody was already reading rather than a separate
   icon they have to find — and it stays a real <button>, so it is reachable
   by keyboard and announced as actionable. */
.pf-item-open {
  flex: 1 1 auto;
  min-width: 0;
  text-align: left;
  background: none;
  border: 0;
  padding: 0;
  font-family: var(--f-sans);
  font-size: 13.5px;
  color: var(--fg);
  cursor: pointer;
  transition: color .14s var(--ease);
}
.pf-item-open::after {
  /* An arrow rather than a word: the row already carries two text buttons,
     and a third would read as a list of options instead of a title. */
  content: " ↗";
  font-size: 11px;
  color: var(--fg-3);
}
.pf-item-open:hover,
.pf-item-open:focus-visible { color: var(--green); }
.pf-item-open:hover::after,
.pf-item-open:focus-visible::after { color: var(--green); }

/* The whole row lifts, so the affordance is visible before the pointer
   reaches the words themselves. */
.pf-item[data-openable="true"] { transition: background .14s var(--ease); }
.pf-item[data-openable="true"]:hover { background: var(--bg-2); }

@media (prefers-reduced-motion: reduce) {
  .pf-item-open,
  .pf-item[data-openable="true"] { transition: none; }
}

.pf-item {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  padding: 9px 0;
  border-top: 1px solid var(--line-hair);
}

.pf-item-name {
  flex: 1 1 12ch;
  font-family: var(--f-mono);
  font-size: 12.5px;
  color: var(--fg);
  overflow-wrap: anywhere;
}

.pf-item-meta,
.pf-item-state {
  font-family: var(--f-sans);
  font-size: 8.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* Shared is the exception, so it carries the colour. Private is the resting
   state and should look like it. */
.pf-item-state[data-shared="true"] { color: var(--amber); }

.pf-empty {
  margin: 0;
  padding: 8px 0 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--fg-3);
}

li.pf-empty { padding: 9px 0; border-top: 1px solid var(--line-hair); }

/* -------------------------------------------------------------- two-factor */

.mfa-setup {
  margin: 18px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--line-hair);
}

.pf-qr {
  display: block;
  margin: 0 auto 12px;
  /* The QR is dark on transparent, so it needs a light ground of its own to
     be scannable against this background. */
  background: #fff;
  padding: 9px;
  border: 1px solid var(--line-soft);
}

.pf-secret {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--green);
  overflow-wrap: anywhere;
  user-select: all;
}

/* ------------------------------------------------------------------- foot */

.acct-foot {
  display: flex;
  justify-content: center;
  padding: 6px 0 0;
}

/* ------------------------------------------------------------------ admin */

.ad-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  margin: 0 0 22px;
  padding: 4px;
  border: 1px solid var(--line-hair);
  background: var(--bg-1);
}

.ad-nav .linkish {
  padding: 8px 14px;
  font-size: 9.5px;
  letter-spacing: .14em;
  transition: color .14s var(--ease), background .14s var(--ease);
}

.ad-nav .linkish[data-active="true"] {
  color: var(--green);
  background: rgba(86, 217, 138, .08);
}

/* Flex, not grid, and the reason is the last row.

   This was `grid-template-columns: repeat(3, 1fr)` back when the overview was
   exactly six cells and 3x2 came out even. It is now five groups of four to
   eight, and any fixed column count leaves the last row short — which, with
   the hairline showing through the gaps, draws an empty box the eye reads as
   a missing figure. Auto-fit has the same problem: it collapses empty tracks
   but not the hole after the last item in a part-filled row.

   Flex-wrap has no such row: the items on the last line grow to fill it. */
.ad-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  margin: 0 0 4px;
  background: var(--line-hair);
  border: 1px solid var(--line-hair);
}

.ad-grid .ad-cell { flex: 1 1 11rem; }

/* A group heading after a grid needs air above it; the first one does not. */
.ad-grid + .ad-sec { margin-top: 22px; }

.ad-cell {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 16px 18px;
  background: var(--bg-1);
}

.ad-cell-key {
  font-family: var(--f-sans);
  font-size: 8.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.ad-cell-val {
  font-family: var(--f-mono);
  font-size: 24px;
  line-height: 1;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}

.ad-note {
  margin: 16px 0 0;
  font-size: 10.5px;
  line-height: 1.55;
  color: var(--fg-3);
}

.ad-empty {
  margin: 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--fg-2);
}

/* The raw failure, under the readable one.

   Quiet on purpose — it is for the administrator who has to fix the thing,
   not the headline. Monospace because half of it is a SQLSTATE and a function
   name, and both are things somebody will retype into a SQL editor. */
.ad-detail {
  margin: 10px 0 0;
  font-family: var(--f-mono);
  font-size: 10.5px;
  line-height: 1.6;
  color: var(--fg-3);
  overflow-wrap: anywhere;
}

.ad-detail-key {
  display: inline-block;
  margin-right: 2px;
  padding: 1px 5px;
  border: 1px solid var(--line-soft);
  font-family: var(--f-sans);
  font-size: 8px;
  letter-spacing: .16em;
  color: var(--amber);
}

/* --------------------------------------------------- Content · Studio ---- */

.ad-sec {
  margin: 0 0 6px;
  font-family: var(--f-sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.ad-studio {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 16px 0 14px;
}

/* The native colour input, sized to read as a swatch rather than a control
   with a colour in it. Chrome and Firefox draw the inside differently, so
   the padding is zeroed and the border comes from us. */
.ad-swatch {
  width: 46px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--line);
  background: none;
  cursor: pointer;
}
.ad-swatch::-webkit-color-swatch-wrapper { padding: 2px; }
.ad-swatch::-webkit-color-swatch { border: 0; }
.ad-swatch::-moz-color-swatch { border: 0; }

.ad-hex {
  width: 11ch;
  flex: 0 0 auto;
  font-family: var(--f-mono);
  text-transform: uppercase;
}

.ad-preview {
  flex: 1 1 12rem;
  min-width: 10rem;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line-soft);
}

/* Dark on the preview whatever the colour: chroma-key green is bright, and
   light text on it is unreadable. */
.ad-preview-label {
  font-family: var(--f-mono);
  font-size: 9.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, .62);
}

.ad-searchbar { margin: 0 0 16px; }

.ad-search {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 11px;
  border: 1px solid var(--line-soft);
  background: var(--bg);
  color: var(--fg);
  font-family: var(--f-mono);
  font-size: 12px;
}

.ad-search:focus { outline: none; border-color: var(--green-2); }

.ad-list { list-style: none; margin: 0; padding: 0; }

.ad-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12px;
  padding: 11px 0;
  border-top: 1px solid var(--line-hair);
}

.ad-row:first-child { border-top: 0; }

.ad-row-main {
  flex: 1 1 14ch;
  font-family: var(--f-mono);
  font-size: 12.5px;
  color: var(--fg);
  overflow-wrap: anywhere;
}

.ad-row-sub {
  font-family: var(--f-mono);
  font-size: 10.5px;
  color: var(--fg-3);
  overflow-wrap: anywhere;
}

.ad-role {
  font-family: var(--f-sans);
  font-size: 8.5px;
  letter-spacing: .14em;
  color: var(--fg-3);
}

.ad-role[data-role="admin"] { color: var(--amber); }

/* ------------------------------------------------- admin · members table --

   The Users screen is a table because it is read down a column — "who joined
   recently", "who is suspended", "who has saved nothing" are all questions
   asked of one column at a time, and a list of stacked rows answers none of
   them. It scrolls sideways inside its own wrapper rather than making the
   page do it: six columns of monospace cannot honestly be squeezed onto a
   phone, and hiding the column somebody came looking for is worse than
   asking them to scroll to it.
   ------------------------------------------------------------------------ */

.ad-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
}

/* The search box takes the slack; the filter chips and the sort keep their
   intrinsic width, so they never get squeezed to unreadable. */
.ad-controls .ad-searchbar {
  flex: 1 1 16rem;
  min-width: 12rem;
  margin: 0;
}

.ad-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.ad-chip {
  padding: 7px 11px;
  border: 1px solid var(--line-soft);
  background: none;
  color: var(--fg-3);
  font-family: var(--f-sans);
  font-size: 9px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
}

.ad-chip:hover { color: var(--fg); border-color: var(--line); }
.ad-chip:focus-visible { outline: 1px solid var(--green-2); outline-offset: 1px; }

/* The selected filter is the one piece of state on this bar, so it carries
   both a colour and a border — never colour alone. */
.ad-chip[data-active="true"] {
  color: var(--green);
  border-color: var(--green-2);
}

.ad-sort {
  display: flex;
  align-items: center;
  gap: 7px;
}

.ad-sort-key {
  font-family: var(--f-sans);
  font-size: 9px;
  letter-spacing: .18em;
  color: var(--fg-3);
}

.ad-select {
  padding: 7px 9px;
  border: 1px solid var(--line-soft);
  background: var(--bg);
  color: var(--fg);
  font-family: var(--f-mono);
  font-size: 11px;
}

.ad-select:focus { outline: none; border-color: var(--green-2); }

/* The wrapper scrolls, not the page. */
.ad-tablewrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.ad-table {
  width: 100%;
  min-width: 46rem;
  border-collapse: collapse;
  font-family: var(--f-mono);
  font-size: 11.5px;
}

.ad-table th {
  padding: 0 12px 8px 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  font-family: var(--f-sans);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--fg-3);
  white-space: nowrap;
}

.ad-table td {
  padding: 11px 12px 11px 0;
  border-top: 1px solid var(--line-hair);
  vertical-align: top;
  color: var(--fg-2);
}

.ad-table tbody tr:first-child td { border-top: 0; }
.ad-table th:last-child, .ad-table td:last-child { padding-right: 0; }

/* Dates and counts line up when their digits do. */
.ad-col-num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ad-td-member { min-width: 16rem; }

/* Subs (5) holds the longest pill, PRO · ADMIN ROLE. Status (6) holds one
   word. Both are nowrap-ed by the pill rule above; these widths stop the
   column collapsing under the header and re-wrapping on every redraw. */
.ad-table td:nth-child(5) { min-width: 9.5rem; }
.ad-table td:nth-child(6) { min-width: 6rem; }

/* A report count that is not zero is worth noticing. */
.ad-col-warn { color: var(--amber); }

/* Somebody else's words, in a column beside structured data. Given room to
   wrap rather than a nowrap that would push every other column off screen,
   and capped so one long complaint cannot set the width of the whole table. */
.ad-report-reason {
  max-width: 28rem;
  min-width: 12rem;
  white-space: normal;
  overflow-wrap: anywhere;
  color: var(--fg-2);
}

/* "Last active" is two words that mean one thing. Wrapping "Online now" onto
   two lines makes a row look like it holds two facts. */
.ad-table td:nth-child(3) { white-space: nowrap; }

.ad-member {
  display: flex;
  align-items: flex-start;
  gap: 9px;
}

/* Nudged down to sit on the first line of the name rather than the top of the
   block. `align-items: baseline` would be the elegant way to say that, but the
   dot is an empty block with no text and therefore no baseline of its own, so
   the browser aligns its bottom edge to the names column instead — which put
   it a hundred pixels below the name it belongs to. */
.ad-member .presence-dot {
  flex: 0 0 auto;
  margin-top: 6px;
}

.ad-member-names {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* .ad-row-main and .ad-row-sub are shared with the stacked lists on the other
   admin screens, where they sit in a ROW and carry `flex: 1 1 14ch` to share
   the width. In this column they would read that basis as a HEIGHT — 14ch of
   empty vertical space per name. The typography is what is wanted here; the
   flexing is not. */
.ad-member-names > * { flex: 0 0 auto; }

.ad-flags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  margin-top: 7px;
}

/* A pill breaks the line between pills, never inside one: "PRO · ADMIN ROLE"
   split across two lines inside its own border reads as two broken labels.

   Scoped to the pill itself rather than to .ad-flags, because the Subs and
   Status columns put a single pill straight into the cell with no group
   around it — and those were exactly the ones that wrapped. */
.ad-table .ad-flag,
.ad-table .ad-role,
.ad-flags .ad-flag,
.ad-flags .ad-role { white-space: nowrap; }

/* Status and plan pills sit at the top of their own cell, so they need no
   top margin there. */
.ad-table td > .ad-flags { margin-top: 0; }

.ad-flag-ok { color: var(--green); border-color: var(--line-soft); }

.ad-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  min-width: 13rem;
}

.ad-pager {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 18px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--line-hair);
}

.ad-pager-count {
  font-family: var(--f-mono);
  font-size: 10.5px;
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}

.ad-pager .linkish[disabled] {
  opacity: .4;
  cursor: default;
}

.ad-subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0 0 18px;
}

.ad-subtabs .linkish[data-active="true"] { color: var(--green); }

/* -------------------------------------------------------------- responsive */

/* Five stat columns stop being readable long before the viewport gets small,
   so they fold to a grid rather than shrinking to illegibility. */
@media (max-width: 560px) {
  .statrow { grid-template-columns: repeat(3, 1fr); }
  .ad-grid .ad-cell { flex-basis: 8rem; }
  .ad-cell-val { font-size: 20px; }
  .card { padding: 16px 15px 18px; }
  .acct-head { padding: 20px 0 18px; margin-bottom: 20px; }
  .readout-row { flex-direction: column; align-items: flex-start; gap: 3px; }
  .readout-row dd { text-align: left; }
  .card-actions .act { flex: 1 1 auto; }
}

@media (max-width: 380px) {
  .statrow { grid-template-columns: repeat(2, 1fr); }
  .ad-grid { grid-template-columns: 1fr; }
  .acct-title { font-size: 12px; letter-spacing: .26em; padding-left: .26em; }
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  .field-in,
  .ad-nav .linkish { transition: none; }
}

/* ------------------------------------------------------------- presence */

/*
 * A status light. Colour is never the only signal — the word ONLINE or AWAY
 * sits beside it — so the row survives colour blindness and a monochrome
 * screen alike.
 */
.presence-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--fg-3);
  align-self: center;
}

.presence-dot[data-online="true"] {
  background: var(--green);
  box-shadow: 0 0 6px rgba(86, 217, 138, .55);
  animation: presence-pulse 2.8s ease-in-out infinite;
}

@keyframes presence-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .55; }
}

/* A pulsing dot is decoration; somebody who asked for less motion should get
   a steady light that still reads as online. */
@media (prefers-reduced-motion: reduce) {
  .presence-dot[data-online="true"] { animation: none; }
}

/* Small status flags in the admin user list. Booleans about an account, never
   anything about the secret behind them. */
.ad-flag {
  font-family: var(--f-sans);
  font-size: 8px;
  letter-spacing: .14em;
  padding: 2px 5px;
  border: 1px solid var(--line-soft);
  color: var(--fg-3);
}

.ad-flag-warn { color: var(--amber); border-color: rgba(200, 162, 74, .4); }
/* PRO in the Store's own amber, so a member reads the same colour in the
   admin list that they see on the Premium page. */
.ad-flag-pro {
  color: var(--amber-hot, #dbb968);
  border-color: rgba(219, 185, 104, .42);
  background: rgba(219, 185, 104, .07);
}
