/*
  Circles — component styles
  Requires tokens.css to be loaded first. Every rule reads a token, never a
  literal color/size, so re-theming means editing tokens.css only.
  All classes are prefixed `circles-` so this can drop into an existing
  codebase without colliding with other styles.

  The circle is the brand's one shape, and it moves: popovers grow out of the
  button that opened them as an expanding circle, the drawer opens as a circle
  from its corner, presses spread a ripple, checks and knobs pop on a spring,
  "online" breathes. Every one of those motions switches off under
  prefers-reduced-motion (see the end of this file).
*/

/* ---------- Button ---------- */
/* Pill-shaped: a button is a stretched circle. The ::after is the press ripple
   (circles.js positions it where you pressed). */
.circles-btn {
  appearance: none; -webkit-appearance: none;
  border: none; cursor: pointer;
  font-family: var(--font-sans);
  border-radius: var(--radius-pill);
  /* A fixed height from the tokens (border included), the label centred in it. */
  box-sizing: border-box; height: var(--btn-height);
  padding: 0 var(--space-4);
  display: inline-flex; align-items: center; gap: var(--space-2);
  white-space: nowrap; /* a pill never wraps onto two lines */
  position: relative; overflow: hidden; isolation: isolate;
  box-shadow: var(--shadow-sm);
  transition:
    box-shadow var(--duration-fast) ease,
    background-color var(--duration-fast) ease,
    transform var(--duration-pop) var(--easing-spring),
    opacity var(--duration-fast) ease;
}
.circles-btn:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.circles-btn:active { transform: scale(0.96); transition-duration: var(--duration-fast); }
.circles-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.circles-btn:disabled { opacity: 0.4; box-shadow: none; cursor: not-allowed; pointer-events: none; transform: none; }
.circles-btn--primary {
  background-color: var(--brand); color: var(--brand-on);
  /* a soft highlight in the top-left, like light on a sphere */
  background-image: radial-gradient(circle at 25% -20%, color-mix(in srgb, var(--ink) 35%, transparent), transparent 60%);
}
.circles-btn--secondary { background: var(--surface-200); color: var(--ink); border: 1px solid var(--border); box-shadow: none; }
.circles-btn--secondary:hover { box-shadow: var(--shadow-sm); border-color: var(--ink-muted); }
.circles-btn--ghost { background: transparent; color: var(--ink); box-shadow: none; }
.circles-btn--ghost:hover { background: var(--brand-muted); box-shadow: none; }
.circles-btn--danger { background: var(--danger); color: var(--danger-on); }
/* A destructive action that isn't the confirmation itself ("Delete circle"):
   outlined in danger, filled only on hover. Pair it with the trash icon. */
.circles-btn--danger-outline { background: transparent; color: var(--danger); border: 1px solid var(--danger); box-shadow: none; }
.circles-btn--danger-outline:hover { background: color-mix(in srgb, var(--danger) 14%, transparent); box-shadow: none; }
/* Tonal: a quiet but visible fill — a translucent wash of --ink, so it shows as
   a shape on any surface (cards, tinted or darkened ones too) without competing
   with a primary. Made for icon-only circles on content ("Mute", "Delete").
   --tonal-danger is the same at rest and turns danger on hover, for a
   destructive action that sits among calm ones (pair it with the trash icon). */
.circles-btn--tonal,
.circles-btn--tonal-danger { background: color-mix(in srgb, var(--ink) 10%, transparent); color: var(--ink); box-shadow: none; }
.circles-btn--tonal:hover { background: color-mix(in srgb, var(--ink) 16%, transparent); box-shadow: none; }
.circles-btn--tonal-danger:hover { background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); box-shadow: none; }
.circles-btn--sm { height: var(--btn-height-sm); padding: 0 var(--space-3); }
.circles-btn--lg { height: var(--btn-height-lg); padding: 0 var(--space-5); }
/* Icon-only: a true circle. Always give it an aria-label. */
.circles-btn--icon { width: 40px; height: 40px; padding: 0; justify-content: center; flex: none; }
.circles-btn--icon.circles-btn--sm { width: 32px; height: 32px; }
.circles-btn--icon.circles-btn--lg { width: 48px; height: 48px; }

/* ---------- Ripple (any .circles-btn, or anything with data-circles-ripple) ---------- */
.circles-btn::after,
[data-circles-ripple]::after {
  content: "";
  position: absolute; z-index: -1;
  left: var(--circles-ripple-x, 50%); top: var(--circles-ripple-y, 50%);
  width: var(--circles-ripple-size, 0px); height: var(--circles-ripple-size, 0px);
  border-radius: 50%;
  background: currentColor;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0);
  pointer-events: none;
}
[data-circles-ripple] { position: relative; overflow: hidden; isolation: isolate; }
.circles-rippling::after { animation: circles-ripple var(--duration-ripple) var(--easing-out); }
@keyframes circles-ripple {
  from { opacity: 0.28; transform: translate(-50%, -50%) scale(0); }
  to   { opacity: 0;    transform: translate(-50%, -50%) scale(1); }
}

/* ---------- Card ---------- */
.circles-card {
  background: var(--surface-200);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.circles-card__well {
  background: var(--surface-300);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  color: var(--ink);
  margin-top: var(--space-2);
}
.circles-card--clickable {
  cursor: pointer;
  transition: box-shadow var(--duration-base) ease, transform var(--duration-pop) var(--easing-spring);
}
.circles-card--clickable:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); }
.circles-card--clickable:active { transform: scale(0.99); }
.circles-card--clickable:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.circles-card__badge {
  align-self: flex-start;
  background: var(--brand); color: var(--brand-on);
  border-radius: var(--radius-pill);
  padding: 2px var(--space-2);
}
/* Orbs: two soft spectrum circles drifting in the card's top-right corner —
   for a hero moment (a profile header, onboarding), not for every card. */
.circles-card--orbs { position: relative; overflow: hidden; isolation: isolate; }
.circles-card--orbs::before,
.circles-card--orbs::after {
  content: ""; position: absolute; z-index: -1; border-radius: 50%; pointer-events: none;
}
.circles-card--orbs::before {
  width: 180px; height: 180px; top: -90px; right: -50px;
  background: radial-gradient(circle, color-mix(in srgb, var(--spectrum-1) 45%, transparent), transparent 70%);
  animation: circles-drift 14s ease-in-out infinite alternate;
}
.circles-card--orbs::after {
  width: 120px; height: 120px; top: -30px; right: 70px;
  background: radial-gradient(circle, color-mix(in srgb, var(--spectrum-2) 35%, transparent), transparent 70%);
  animation: circles-drift 11s ease-in-out -4s infinite alternate-reverse;
}

/* ---------- Field (Input / Select shared chrome) ---------- */
.circles-field { display: flex; flex-direction: column; gap: var(--space-2); }
.circles-field > label { color: var(--ink); }
.circles-input,
.circles-select {
  font-family: var(--font-sans);
  font-size: 15px;
  width: 100%;
  background: var(--surface-300);
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
  padding: var(--input-padding-y) var(--space-4);
  color: var(--ink);
  outline: none;
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease, outline-offset var(--duration-pop) var(--easing-spring);
}
.circles-input::placeholder { color: var(--ink-muted); }
.circles-input:hover,
.circles-select:hover { border-color: var(--ink-muted); }
.circles-input:focus,
.circles-select:focus {
  background: var(--surface-200);
  border-color: transparent;
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.circles-input:disabled,
.circles-select:disabled { opacity: 0.4; border-color: transparent; }
.circles-field__hint { color: var(--ink-muted); }
.circles-field--error .circles-input,
.circles-field--error .circles-select { border-color: var(--danger); }
.circles-field__error { color: var(--danger); display: flex; align-items: center; gap: 4px; }

.circles-select-wrap { position: relative; }
.circles-select {
  appearance: none; -webkit-appearance: none;
  cursor: pointer;
  padding-right: var(--space-6);
}
.circles-select-wrap::after {
  content: "";
  position: absolute; right: var(--space-4); top: 50%;
  width: 8px; height: 8px;
  border: solid var(--ink-muted);
  border-width: 0 2px 2px 0;
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

/* ---------- Combobox ---------- */
.circles-combobox { position: relative; display: flex; flex-direction: column; gap: var(--space-1); }
.circles-combobox > label { color: var(--ink); }
.circles-combobox__input { /* extends .circles-input visually via the same rule set above */ }
.circles-combobox__listbox {
  position: absolute; top: calc(100% + var(--space-1)); left: 0; right: 0;
  background: var(--surface-200);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
  margin: 0; list-style: none;
  max-height: 220px; overflow-y: auto;
  z-index: 10;
  transform-origin: top center;
  animation: circles-drop-in var(--duration-pop) var(--easing-spring);
}
.circles-combobox__listbox[hidden] { display: none; }
.circles-combobox__option {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  color: var(--ink);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  cursor: pointer;
}
.circles-combobox__option--active { background: var(--brand-muted); font-weight: 600; }
.circles-combobox__check { color: var(--brand); flex: none; visibility: hidden; }
.circles-combobox__option--selected .circles-combobox__check { visibility: visible; }
.circles-combobox__empty { color: var(--ink-muted); padding: var(--space-2) var(--space-3); }
@keyframes circles-drop-in { from { opacity: 0; transform: translateY(-4px) scale(0.96); } }

/* ---------- Checkbox ---------- */
/* The check mark always exists, scaled to nothing, so checking it can POP on a
   spring (a transition, not an animation — re-rendering an already-checked box
   doesn't replay it). */
.circles-checkbox {
  appearance: none; -webkit-appearance: none;
  width: 20px; height: 20px; flex: none; margin: 0;
  border-radius: 7px;
  border: 1.5px solid var(--ink-muted);
  background: var(--surface-200);
  cursor: pointer;
  display: grid; place-content: center;
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease, transform var(--duration-pop) var(--easing-spring);
}
.circles-checkbox:active { transform: scale(0.88); }
.circles-checkbox:checked,
.circles-checkbox:indeterminate { background: var(--brand); border-color: var(--brand); }
.circles-checkbox::after {
  content: "";
  width: 5px; height: 9px;
  border: solid var(--brand-on);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px) scale(0);
  transition: transform var(--duration-pop) var(--easing-spring);
}
.circles-checkbox:checked::after { transform: rotate(45deg) translate(-1px, -1px) scale(1); }
.circles-checkbox:indeterminate::after { width: 10px; height: 2px; border: none; background: var(--brand-on); transform: none; }
.circles-checkbox:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.circles-checkbox:disabled { opacity: 0.4; cursor: not-allowed; }
/* Room around every box: 12px to its label, 4px above and below the row. */
.circles-check-row { display: flex; align-items: center; gap: var(--space-3); padding-block: var(--space-1); }
.circles-check-row > label { color: var(--ink); cursor: pointer; }

/* ---------- Radio ---------- */
.circles-radio-group { border: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.circles-radio-group > legend { color: var(--ink); padding: 0 0 var(--space-2) 0; }
.circles-radio {
  appearance: none; -webkit-appearance: none;
  width: 20px; height: 20px; flex: none; margin: 0;
  border-radius: var(--radius-pill);
  border: 1.5px solid var(--ink-muted);
  background: var(--surface-200);
  cursor: pointer;
  display: grid; place-content: center;
  box-shadow: inset 0 0 0 0 var(--brand);
  transition: box-shadow var(--duration-pop) var(--easing-spring), border-color var(--duration-fast) ease, transform var(--duration-pop) var(--easing-spring);
}
.circles-radio:active { transform: scale(0.88); }
/* a circle inside a circle: the selected dot grows from the middle */
.circles-radio:checked { border-color: var(--brand); box-shadow: inset 0 0 0 5px var(--brand); }
.circles-radio:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.circles-radio:disabled { cursor: not-allowed; opacity: 0.4; }

/* ---------- Toggle (switch) ---------- */
.circles-switch { position: relative; display: inline-block; width: 44px; height: 26px; flex: none; }
.circles-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; z-index: 1; }
.circles-switch__track {
  position: absolute; inset: 0;
  background: var(--surface-300);
  border: 1px solid var(--ink-muted);
  border-radius: var(--radius-pill);
  transition: background var(--duration-base) ease, border-color var(--duration-base) ease;
}
.circles-switch__knob {
  position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px;
  background: var(--ink-muted);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration-pop) var(--easing-spring), width var(--duration-pop) var(--easing-spring), background var(--duration-base) ease;
}
/* Pressing squishes the knob into a pill; it springs back round when released. */
.circles-switch input:active ~ .circles-switch__knob { width: 26px; }
.circles-switch input:checked ~ .circles-switch__track { background: var(--brand); border-color: var(--brand); }
.circles-switch input:checked ~ .circles-switch__knob { transform: translateX(18px); background: var(--brand-on); }
.circles-switch input:checked:active ~ .circles-switch__knob { transform: translateX(12px); }
.circles-switch input:focus-visible ~ .circles-switch__track { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.circles-switch input:disabled ~ .circles-switch__track,
.circles-switch input:disabled ~ .circles-switch__knob { opacity: 0.4; }
.circles-switch-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }

/* ---------- Accordion ---------- */
.circles-accordion { background: var(--surface-200); border-radius: var(--radius-lg); overflow: hidden; }
.circles-accordion > details { border-bottom: 1px solid var(--border); }
.circles-accordion > details:last-child { border-bottom: none; }
.circles-accordion summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-4);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  color: var(--ink);
}
.circles-accordion summary::-webkit-details-marker { display: none; }
.circles-accordion summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; border-radius: var(--radius-md); }
.circles-accordion__chev { flex: none; transition: transform var(--duration-pop) var(--easing-spring); }
.circles-accordion details[open] .circles-accordion__chev { transform: rotate(180deg); }
.circles-accordion__panel { padding: 0 var(--space-4) var(--space-4); color: var(--ink-muted); }
/* Expand/collapse glides where the browser can animate to height: auto
   (::details-content + interpolate-size); elsewhere it simply opens. */
@supports (interpolate-size: allow-keywords) and selector(::details-content) {
  .circles-accordion { interpolate-size: allow-keywords; }
  .circles-accordion details::details-content {
    block-size: 0; overflow: clip; opacity: 0;
    transition:
      block-size var(--duration-expand) var(--easing-out),
      opacity var(--duration-expand) var(--easing-out),
      content-visibility var(--duration-expand) allow-discrete;
  }
  .circles-accordion details[open]::details-content { block-size: auto; opacity: 1; }
  @media (prefers-reduced-motion: reduce) { .circles-accordion details::details-content { transition: none; } }
}

/* ---------- Badge ---------- */
.circles-badge { display: inline-flex; align-items: center; border-radius: var(--radius-pill); padding: var(--space-1) var(--space-2); }
.circles-badge--neutral { background: var(--surface-300); color: var(--ink); }
.circles-badge--brand { background: var(--brand-muted); color: var(--brand); }
.circles-badge--success { background: var(--success); color: var(--success-on); }
.circles-badge--warning { background: var(--warning); color: var(--warning-on); }
.circles-badge--danger { background: var(--danger); color: var(--danger-on); }
.circles-badge--info { background: var(--info); color: var(--info-on); }

/* ---------- Status indicator (dot + label) ---------- */
/* A small state marker: a filled dot for a definite state, a HOLLOW dot for
   "nothing yet" (waiting, offline, unset). Colour is never the only signal —
   always keep the label, or use --dot-only, which hides it visually but
   keeps it for screen readers. No JS required. Add --pulse for something
   LIVE (someone online): a ring breathes out of the dot. */
.circles-status { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--ink); }
.circles-status__dot {
  width: 10px; height: 10px; flex: none;
  border-radius: var(--radius-pill);
  background: var(--ink-muted);
  box-sizing: border-box;
  position: relative;
}
.circles-status--success .circles-status__dot { background: var(--success); }
.circles-status--warning .circles-status__dot { background: var(--warning); }
.circles-status--danger  .circles-status__dot { background: var(--danger); }
.circles-status--info    .circles-status__dot { background: var(--info); }
.circles-status--brand   .circles-status__dot { background: var(--brand); }
/* Hollow: outline only, in --ink-muted so it clears contrast on every surface. */
.circles-status--hollow  .circles-status__dot { background: transparent; border: 1.5px solid var(--ink-muted); }
.circles-status--pulse .circles-status__dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: inherit;
  animation: circles-pulse 2.4s var(--easing-out) infinite;
  pointer-events: none;
}
@keyframes circles-pulse {
  0%   { transform: scale(1); opacity: 0.6; }
  70%  { transform: scale(2.6); opacity: 0; }
  100% { transform: scale(2.6); opacity: 0; }
}
.circles-status--dot-only .circles-status__label {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* ---------- Drawer ---------- */
/* A panel anchored to the right edge, for notifications, filters, or any
   secondary content that shouldn't leave the current page. It opens as a
   circle expanding from the top-right corner (where its trigger usually is)
   and closes back into it. Needs circles.js (Circles.createDrawer) — see
   snippets/drawer.html. Closed by default: invisible AND non-interactive (not
   just transparent), so it can't be tabbed into or clicked through while shut. */
.circles-drawer {
  position: fixed;
  inset: 0;
  z-index: 100;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear var(--duration-reveal); /* stay visible while the close plays */
}
.circles-drawer__backdrop {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  opacity: 0;
  transition: opacity var(--duration-reveal) ease;
}
.circles-drawer__panel {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(380px, 100vw);
  background: var(--surface-200);
  box-shadow: var(--shadow-lg);
  border-radius: 0; /* square: it's a full-height edge of the screen (rounded corners removed 2026-10-06) */
  display: flex;
  flex-direction: column;
  clip-path: circle(0% at 100% 0%);
  transform: translateX(24px);
  transition: clip-path var(--duration-reveal) var(--easing-in-out), transform var(--duration-reveal) var(--easing-out);
  outline: none; /* focused programmatically on open — no visible ring needed on the panel itself */
}
.circles-drawer--open { visibility: visible; pointer-events: auto; transition-delay: 0s; }
.circles-drawer--open .circles-drawer__backdrop { opacity: 1; }
.circles-drawer--open .circles-drawer__panel { clip-path: circle(160% at 100% 0%); transform: translateX(0); }
/* Once the reveal has finished nothing is clipped (the shadow shows whole). */
.circles-drawer--settled .circles-drawer__panel { clip-path: none; }
.circles-drawer__header {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border);
}
.circles-drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.circles-drawer__footer {
  flex: none;
  padding: var(--space-4);
  border-top: 1px solid var(--border);
}

/* ---------- Popover ---------- */
/* A small floating panel anchored to the control that opened it — for short
   forms and pickers ("New circle", a filter) that shouldn't take over the
   page the way the Drawer does. It GROWS OUT OF ITS BUTTON as an expanding
   circle (circles.js puts the circle's centre on the anchor) and shrinks back
   into it on close. The rest of the page is dimmed and BLURRED behind it so
   attention stays on the panel. Needs circles.js (Circles.createPopover) — see
   snippets/popover.html, which also positions it next to the anchor and keeps
   keyboard focus inside while open. Closed by default: invisible AND
   non-interactive. */
.circles-popover {
  position: fixed;
  inset: 0;
  z-index: 110; /* above the Drawer (100): a popover can be opened from inside one */
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear var(--duration-reveal);
}
/* Opening is quiet (2026-10-20: the circle reveal and the blur were choppy on low-end
   phones): the panel fades in, rising a few pixels and settling to full size, over a dimmed
   page. Only opacity and transforms move. The app's Popover.tsx does the same. */
.circles-popover__backdrop {
  position: absolute;
  inset: 0;
  background: var(--scrim); /* with no blur behind, a touch stronger (the drawer's dim) */
  opacity: 0;
  transition: opacity var(--duration-expand) var(--easing-out);
}
.circles-popover__panel {
  --circles-reveal-at: 24px 0px; /* set to the anchor's centre by circles.js */
  position: fixed; /* left/top are set by Circles.createPopover from the anchor */
  top: 0; left: 0;
  width: min(320px, calc(100vw - 32px));
  max-height: min(72vh, 480px);
  display: flex;
  flex-direction: column;
  background: var(--surface-200);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(8px) scale(0.98);
  transform-origin: var(--circles-reveal-at);
  transition:
    transform var(--duration-expand) var(--easing-out),
    opacity var(--duration-expand) var(--easing-out);
  outline: none; /* focused programmatically on open */
}
.circles-popover--open { visibility: visible; pointer-events: auto; transition-delay: 0s; }
.circles-popover--open .circles-popover__backdrop { opacity: 1; }
.circles-popover--open .circles-popover__panel { opacity: 1; transform: none; }
.circles-popover__header {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.circles-popover__header > p { margin: 0; }
.circles-popover__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.circles-popover__footer {
  flex: none;
  display: flex; justify-content: flex-end; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border);
}
/* Modal: the same component, centered in the window instead of hung from an
   anchor, and optionally wide. It grows from its own centre. Add
   circles-popover--center (Circles.createPopover does this for you with
   { center: true }) and, for more room, --wide. */
.circles-popover--center .circles-popover__panel {
  --circles-reveal-at: 50% 50%;
  top: 50%; left: 50%;
  transform: translate(-50%, calc(-50% + 8px)) scale(0.98);
  transform-origin: center;
}
.circles-popover--center.circles-popover--open .circles-popover__panel { transform: translate(-50%, -50%); }
.circles-popover--wide .circles-popover__panel {
  width: min(600px, calc(100vw - 32px));
  max-height: min(84vh, 680px);
}

/* ---------- Icon (inherits text colour) ---------- */
/* The files in icons/ have a fixed light stroke, which is invisible or wrong
   on a coloured surface (an active tab, a primary button). This draws the same
   icon as a MASK filled with currentColor, so it always matches the text next
   to it. Pick the glyph with a modifier; each resolves relative to THIS file. */
.circles-icon {
  display: inline-block;
  flex: none;
  width: 16px; height: 16px;
  background: currentColor;
  -webkit-mask: var(--circles-icon) center / contain no-repeat;
  mask: var(--circles-icon) center / contain no-repeat;
}
.circles-icon--sm { width: 14px; height: 14px; }
.circles-icon--lg { width: 24px; height: 24px; }
.circles-icon--circle         { --circles-icon: url("icons/circle.svg"); }
.circles-icon--circles        { --circles-icon: url("icons/circles.svg"); } /* three overlapping rings, after the logomark */
.circles-icon--users          { --circles-icon: url("icons/users.svg"); }
.circles-icon--user           { --circles-icon: url("icons/user.svg"); }
.circles-icon--plus           { --circles-icon: url("icons/plus.svg"); }
.circles-icon--image          { --circles-icon: url("icons/image.svg"); } /* a photo: add one to a post, comment or message */
.circles-icon--user-plus      { --circles-icon: url("icons/user-plus.svg"); } /* a person with a + : add people */
.circles-icon--bug            { --circles-icon: url("icons/bug.svg"); } /* report a bug */
.circles-icon--minus          { --circles-icon: url("icons/minus.svg"); }
.circles-icon--settings       { --circles-icon: url("icons/settings.svg"); }
.circles-icon--chevron-up     { --circles-icon: url("icons/chevron-up.svg"); }
.circles-icon--chevron-down   { --circles-icon: url("icons/chevron-down.svg"); }
.circles-icon--chevron-left   { --circles-icon: url("icons/chevron-left.svg"); }
.circles-icon--chevron-right  { --circles-icon: url("icons/chevron-right.svg"); }
.circles-icon--arrow-left     { --circles-icon: url("icons/arrow-left.svg"); }
.circles-icon--arrow-right    { --circles-icon: url("icons/arrow-right.svg"); }
.circles-icon--arrow-down     { --circles-icon: url("icons/arrow-down.svg"); } /* down into: e.g. a new post going into the feed */
.circles-icon--x              { --circles-icon: url("icons/x.svg"); }
.circles-icon--check          { --circles-icon: url("icons/check.svg"); }
.circles-icon--edit           { --circles-icon: url("icons/edit.svg"); }
.circles-icon--trash          { --circles-icon: url("icons/trash.svg"); }
.circles-icon--grip           { --circles-icon: url("icons/grip.svg"); } /* drag handle: "grab here to reorder" */
.circles-icon--home           { --circles-icon: url("icons/home.svg"); }
.circles-icon--search         { --circles-icon: url("icons/search.svg"); }
.circles-icon--filter         { --circles-icon: url("icons/filter.svg"); }
.circles-icon--menu           { --circles-icon: url("icons/menu.svg"); }
.circles-icon--mail           { --circles-icon: url("icons/mail.svg"); }
.circles-icon--chat           { --circles-icon: url("icons/chat.svg"); }
.circles-icon--bell           { --circles-icon: url("icons/bell.svg"); }
.circles-icon--bell-off       { --circles-icon: url("icons/bell-off.svg"); } /* notifications off, e.g. a muted post */
.circles-icon--eye            { --circles-icon: url("icons/eye.svg"); }     /* show a password */
.circles-icon--eye-off        { --circles-icon: url("icons/eye-off.svg"); } /* hide it again */
.circles-icon--calendar       { --circles-icon: url("icons/calendar.svg"); }
.circles-icon--star           { --circles-icon: url("icons/star.svg"); }
.circles-icon--info           { --circles-icon: url("icons/info.svg"); }
.circles-icon--qr-code        { --circles-icon: url("icons/qr-code.svg"); } /* your circles code, to show in person */
.circles-icon--alert-triangle { --circles-icon: url("icons/alert-triangle.svg"); }
.circles-icon--sort           { --circles-icon: url("icons/sort.svg"); } /* change the order of a list */
.circles-icon--clock          { --circles-icon: url("icons/clock.svg"); } /* time: a clock with hands at 12 and 3 */
.circles-icon--flag           { --circles-icon: url("icons/flag.svg"); }  /* report something to the moderator */
.circles-icon--more           { --circles-icon: url("icons/more.svg"); }  /* more options: opens a menu */

/* ---------- Tab bar ---------- */
/* The app's main destinations: a floating, frosted pill near the bottom of
   the screen — the web stand-in for the iOS tab bar and Android's bottom
   navigation bar (the native apps use the real ones, see MOBILE.md). 3 to 5
   items, never more. The current one gets aria-current="page": its icon and
   label turn bright (the label bold), and a short brand bar grows in under the
   label. A count goes
   in __badge; a passive "something's new" marker goes in __dot. Pages using it
   need bottom padding (--circles-tabbar-height + space-2 + the safe area) so
   the bar never covers content. See snippets/tabbar.html. */
.circles-tabbar {
  --circles-tabbar-height: 56px;
  position: fixed; z-index: 50;
  left: var(--space-3); right: var(--space-3);
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--space-2));
  max-width: var(--circles-tabbar-max-width, 560px);
  margin: 0 auto;
  padding: 0 var(--space-2);
  background: var(--surface-200);
  background: color-mix(in srgb, var(--surface-200) 84%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
}
.circles-tabbar--static { position: static; margin: 0 auto; } /* for demos and previews */
.circles-tabbar__items {
  display: flex;
  margin: 0 auto;
}
.circles-tabbar__item {
  appearance: none; -webkit-appearance: none;
  flex: 1 1 0; min-width: 0;
  min-height: var(--circles-tabbar-height);
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  padding: 2px 0 9px; /* room below the label for the current tab's bar */
  background: none; border: none; cursor: pointer;
  color: var(--ink-muted);
  /* Native tab labels are 10–12pt; 10px keeps "Conversations" whole down to
     360px-wide phones. */
  font-family: var(--font-sans); font-size: 10px; line-height: 13px; font-weight: 500; letter-spacing: 0;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--duration-fast) ease;
}
.circles-tabbar__item:hover { color: var(--ink); }
/* The current tab stands out clearly (2026-10-06): its icon and bold label in bright ink, and a short
   brand bar under the label that grows out from its middle. (A circle bubble behind the icon was tried
   first; it came too close to the bar's top edge.) The icon follows the label's colour (a CSS mask). */
.circles-tabbar__item[aria-current="page"] { color: var(--ink); font-weight: 700; }
.circles-tabbar__item::after {
  content: "";
  position: absolute; left: 50%; bottom: 4px;
  width: 24px; height: 3px; margin-left: -12px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  transform: scaleX(0);
  transition: transform var(--duration-pop) var(--easing-spring);
}
.circles-tabbar__item[aria-current="page"]::after { transform: scaleX(1); }
.circles-tabbar__item:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -4px; border-radius: var(--radius-pill); }
.circles-tabbar__icon {
  position: relative; display: flex;
  transition: transform var(--duration-pop) var(--easing-spring);
}
.circles-tabbar__item:active .circles-tabbar__icon { transform: scale(0.88); transition-duration: var(--duration-fast); }
.circles-tabbar__label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.circles-tabbar__badge {
  position: absolute; top: -4px; left: calc(100% - 6px);
  min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
  border-radius: var(--radius-pill);
  background: var(--danger); color: var(--danger-on);
  box-shadow: 0 0 0 2px var(--surface-200);
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
  animation: circles-pop-in var(--duration-pop) var(--easing-spring);
}
.circles-tabbar__dot {
  position: absolute; top: -1px; left: calc(100% - 3px);
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--surface-200);
  animation: circles-pop-in var(--duration-pop) var(--easing-spring);
}
.circles-tabbar__avatar {
  width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--circles-hue, var(--spectrum-1)); color: var(--spectrum-on);
  font-size: 10px; font-weight: 700;
  box-sizing: border-box;
  transition: box-shadow var(--duration-pop) var(--easing-spring);
}
.circles-tabbar__item[aria-current="page"] .circles-tabbar__avatar { box-shadow: 0 0 0 2px var(--surface-200), 0 0 0 4px var(--brand); }
@keyframes circles-pop-in { from { transform: scale(0); } }

/* ---------- Segmented control ---------- */
/* Choose exactly one of a few options, all visible at once — real radio
   inputs underneath, so arrow keys, form submission and screen readers just
   work. No JavaScript. See snippets/segmented.html. */
.circles-segmented {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--surface-300);
  border-radius: var(--radius-pill);
}
.circles-segmented__item { position: relative; display: inline-flex; }
.circles-segmented__item input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.circles-segmented__item > span {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 6px var(--space-4);
  border-radius: var(--radius-pill);
  color: var(--ink-muted);
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease, transform var(--duration-pop) var(--easing-spring);
}
.circles-segmented__item:hover > span { color: var(--ink); }
.circles-segmented__item input:active + span { transform: scale(0.94); }
.circles-segmented__item input:checked + span { background: var(--brand); color: var(--brand-on); box-shadow: var(--shadow-sm); }
.circles-segmented__item input:focus-visible + span { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ---------- Calendar (month grid) ---------- */
/* A month view: previous/next header, weekday row, and a fixed 6-week grid
   so the height never jumps between months. Needs circles.js
   (Circles.createCalendar) — see snippets/calendar.html. Each day is a real
   <button>; markers are small dots under the number. */
.circles-calendar { display: flex; flex-direction: column; gap: var(--space-3); }
.circles-calendar__header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.circles-calendar__title { flex: 1; text-align: center; margin: 0; }
/* A ruled grid: the 1px gaps between cells let the --border background show
   through as hairlines, and the outer border closes the frame. Every day is a
   full-width rectangle filling its cell. */
.circles-calendar__grid {
  display: flex; flex-direction: column; gap: 1px;
  background: var(--border); border: 1px solid var(--border);
  border-radius: var(--radius-md); overflow: hidden;
}
.circles-calendar__row { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px; }
.circles-calendar__weekday { text-align: center; color: var(--ink-muted); padding: var(--space-2) 0; background: var(--surface-300); }
.circles-calendar__cell { display: flex; background: var(--surface-200); }
.circles-calendar__cell > .circles-calendar__day { flex: 1; min-width: 0; }

.circles-calendar__day {
  appearance: none; -webkit-appearance: none;
  border: none; background: transparent; cursor: pointer;
  font-family: var(--font-sans); color: var(--ink);
  min-height: 48px; padding: var(--space-1) 0;
  border-radius: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  transition: background var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
}
.circles-calendar__day:hover { background: var(--brand-muted); }
.circles-calendar__day:active { background: var(--brand-muted); box-shadow: inset 0 0 0 1px var(--brand); }
/* Drawn inside the cell: the grid's rounded frame clips anything outside it. */
.circles-calendar__day:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.circles-calendar__day--outside { color: var(--ink-muted); opacity: 0.6; }
.circles-calendar__day--today { box-shadow: inset 0 0 0 2px var(--brand); }
.circles-calendar__day--selected { background: var(--brand); color: var(--brand-on); }
.circles-calendar__day--selected:hover { background: var(--brand); }

.circles-calendar__dots { display: flex; gap: 3px; height: 6px; align-items: center; }
.circles-calendar__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--brand); }
.circles-calendar__dot--brand   { background: var(--brand); }
.circles-calendar__dot--accent  { background: var(--accent); }
.circles-calendar__dot--success { background: var(--success); }
.circles-calendar__dot--warning { background: var(--warning); }
.circles-calendar__dot--danger  { background: var(--danger); }
.circles-calendar__dot--info    { background: var(--info); }
/* A brand-colored dot would vanish on a brand-filled selected day. */
.circles-calendar__day--selected .circles-calendar__dot--brand { background: var(--brand-on); }

/* ---------- Hue (a person's or group's circle colour) ---------- */
/* data-circles-hue="1".."5" picks one of the five spectrum colours and exposes
   it as --circles-hue; Avatar (and the tab-bar avatar) fill with it. Give the
   same person the same hue everywhere — circles.js has Circles.hueFor(text). */
[data-circles-hue="1"] { --circles-hue: var(--spectrum-1); }
[data-circles-hue="2"] { --circles-hue: var(--spectrum-2); }
[data-circles-hue="3"] { --circles-hue: var(--spectrum-3); }
[data-circles-hue="4"] { --circles-hue: var(--spectrum-4); }
[data-circles-hue="5"] { --circles-hue: var(--spectrum-5); }

/* ---------- Avatar ---------- */
/* A person (or group) is a circle. Initials in --spectrum-on on their hue.
   --ring adds a spinning spectrum ring around it, for the one avatar that
   is the subject of the page (a profile header). */
.circles-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  width: var(--avatar-md); height: var(--avatar-md);
  border-radius: 50%;
  background: var(--circles-hue, var(--brand));
  color: var(--spectrum-on);
  font-family: var(--font-display); font-weight: 600; font-size: 12px; letter-spacing: -0.02em;
  position: relative;
  user-select: none;
}
.circles-avatar--sm { width: var(--avatar-sm); height: var(--avatar-sm); font-size: 10px; }
.circles-avatar--md { width: var(--avatar-md); height: var(--avatar-md); font-size: 12px; }
.circles-avatar--lg { width: var(--avatar-lg); height: var(--avatar-lg); font-size: 14px; }
.circles-avatar--xl { width: var(--avatar-xl); height: var(--avatar-xl); font-size: 20px; }
.circles-avatar--ring { margin: 5px; isolation: isolate; }
.circles-avatar--ring::before {
  content: ""; position: absolute; inset: -5px; z-index: -1; border-radius: 50%;
  background: conic-gradient(var(--spectrum-1), var(--spectrum-2), var(--spectrum-3), var(--spectrum-4), var(--spectrum-5), var(--spectrum-1));
  /* keep only an outer band 3px thick: a ring, with a 2px gap to the avatar */
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 3px), #000 calc(100% - 2.5px));
  mask: radial-gradient(closest-side, transparent calc(100% - 3px), #000 calc(100% - 2.5px));
  animation: circles-spin 8s linear infinite;
}
.circles-avatar-stack { display: inline-flex; }
.circles-avatar-stack > .circles-avatar { box-shadow: 0 0 0 2px var(--surface-200); }
.circles-avatar-stack > .circles-avatar + .circles-avatar { margin-left: -6px; }
.circles-avatar-stack > .circles-avatar--sm + .circles-avatar--sm { margin-left: -4px; }

/* ---------- Logo mark + wordmark ---------- */
/* Three overlapping circles that gather into place when the page loads and
   turn a third of a circle when you hover or press whatever holds them.
   Markup: <span class="circles-mark" aria-hidden="true"><span></span><span></span><span></span></span> */
.circles-mark {
  position: relative; display: inline-block; flex: none;
  width: 28px; height: 28px;
  isolation: isolate;
  transform-origin: 14px 12.7px; /* the triangle's centre, so it turns in place */
  transition: transform var(--duration-reveal) var(--easing-spring);
}
.circles-mark > span {
  position: absolute; width: 16px; height: 16px; border-radius: 50%;
  mix-blend-mode: screen;
  animation: circles-gather 900ms var(--easing-spring) both;
}
.circles-mark > span:nth-child(1) { left: 0;    top: 1px; background: var(--spectrum-1); --dx: -10px; --dy: -6px; }
.circles-mark > span:nth-child(2) { left: 12px; top: 1px; background: var(--spectrum-2); --dx: 10px;  --dy: -6px; animation-delay: 90ms; }
.circles-mark > span:nth-child(3) { left: 6px;  top: 12px; background: var(--spectrum-4); --dx: 0px;  --dy: 10px; animation-delay: 180ms; }
:is(a, button, .circles-logo):hover > .circles-mark { transform: rotate(120deg); }
:is(a, button, .circles-logo):active > .circles-mark { transform: rotate(240deg) scale(0.9); }
.circles-mark--lg { width: 56px; height: 56px; transform-origin: 28px 25.4px; }
.circles-mark--lg > span { width: 32px; height: 32px; }
.circles-mark--lg > span:nth-child(1) { top: 2px; }
.circles-mark--lg > span:nth-child(2) { left: 24px; top: 2px; }
.circles-mark--lg > span:nth-child(3) { left: 12px; top: 24px; }
.circles-wordmark { font: 700 22px/28px var(--font-display); letter-spacing: -0.04em; color: var(--ink); margin: 0; }
.circles-logo { display: inline-flex; align-items: center; gap: var(--space-2); color: inherit; text-decoration: none; }
.circles-logo:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; border-radius: var(--radius-pill); }
@keyframes circles-gather {
  from { transform: translate(var(--dx), var(--dy)) scale(0); opacity: 0; }
  60%  { opacity: 1; }
}

/* ---------- Loader ---------- */
/* Three circles bouncing in turn. Give it role="status" and an aria-label
   (e.g. "Loading"). */
/* <growth-loader> (growth-loader.js): circles growing connections — the loading indicator, and the
   quiet motif of an empty state. Coloured from the spectrum; the lines are soft ink. */
growth-loader {
  --growth-core: var(--spectrum-1);
  --growth-level-1: var(--spectrum-2);
  --growth-level-2: var(--spectrum-4);
  --growth-level-3: var(--spectrum-5);
  --growth-level-4: var(--spectrum-3);
  --growth-link: color-mix(in srgb, var(--ink-muted) 55%, transparent);
}
.circles-loader { display: inline-flex; align-items: center; gap: 6px; height: 24px; }
.circles-loader > span {
  width: 10px; height: 10px; border-radius: 50%;
  animation: circles-bounce 1.1s var(--easing-spring) infinite;
}
.circles-loader > span:nth-child(1) { background: var(--spectrum-1); }
.circles-loader > span:nth-child(2) { background: var(--spectrum-2); animation-delay: 0.14s; }
.circles-loader > span:nth-child(3) { background: var(--spectrum-4); animation-delay: 0.28s; }
@keyframes circles-bounce {
  0%, 60%, 100% { transform: translateY(0) scale(1); }
  30%           { transform: translateY(-8px) scale(1.1); }
}

/* ---------- Burst (a celebration) ---------- */
/* Circles.burst(element) throws a handful of spectrum circles and one ring out
   of an element — for a moment worth a little joy (you posted, you connected).
   Fixed to the viewport and appended to <body>, so it never affects layout. */
.circles-burst { position: fixed; z-index: 1000; width: 0; height: 0; pointer-events: none; }
.circles-burst__dot,
.circles-burst__ring { position: absolute; left: 0; top: 0; border-radius: 50%; }
.circles-burst__ring { border: 2px solid var(--brand); }

/* ---------- Focus (fallback for browsers without :focus-visible on a given element) ---------- */
.circles-focus-ring:focus { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ---------- Reduced motion: everything above stands still ---------- */
@media (prefers-reduced-motion: reduce) {
  .circles-btn, .circles-card--clickable, .circles-checkbox, .circles-checkbox::after, .circles-radio,
  .circles-switch__knob, .circles-switch__track, .circles-accordion__chev,
  .circles-drawer, .circles-drawer__backdrop, .circles-drawer__panel,
  .circles-popover, .circles-popover__backdrop, .circles-popover__panel,
  .circles-tabbar__icon, .circles-tabbar__item::after, .circles-segmented__item > span,
  .circles-calendar__day, .circles-mark, .circles-input, .circles-select { transition: none; }
  .circles-btn:hover, .circles-card--clickable:hover { transform: none; }
  .circles-rippling::after, .circles-status--pulse .circles-status__dot::after,
  .circles-mark > span, .circles-avatar--ring::before, .circles-orbs::before, .circles-orbs::after,
  .circles-card--orbs::before, .circles-card--orbs::after, .circles-combobox__listbox,
  .circles-tabbar__badge, .circles-tabbar__dot { animation: none; }
  .circles-loader > span { animation-duration: 2.4s; } /* still says "working", gently */
}
