/* ============================================================================
   Orbit Axis — You, Appearance, saved charts, and chart identity
   ----------------------------------------------------------------------------
   The account surface used to be a two-column grid of cards, each with its own
   heading, its own paragraph, and a row of buttons at the bottom. That reads as
   five little brochures when what a person wants is a list of things they can
   change.

   It is now grouped inset lists — `.o-group` from the component library — with
   one thing per row and the section name above the group. Scannable top to
   bottom, one tap per action, and the same shape as every other list in the
   app.
   ========================================================================== */

.more-stack { display: grid; gap: var(--space-xl); }
.more-section { display: grid; gap: 0; }

/* ── The account offer ───────────────────────────────────────────────────────
   Stands where the signed-in account rows do, for visitors who have not made
   one. It leads by saying what already works without an account, because the
   panel is titled "Account" and arriving at a sales pitch under that heading
   is what makes a free app feel like a trial. */
.account-offer {
  display: grid;
  gap: var(--space-sm);
  justify-items: start;
  padding: var(--space-md);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.account-offer[hidden] { display: none; }
.account-offer__lead { margin: 0; color: var(--color-primary); line-height: var(--lead-body); }
.account-offer__sub {
  margin: 0;
  color: var(--color-secondary);
  font-size: var(--type-fine);
  line-height: 1.55;
}
.more-details { margin-top: var(--space-sm); }
.more-disclaimer { text-align: center; padding: 0 var(--space-md); }
.more-note { color: var(--color-secondary); font-size: var(--type-caption); line-height: var(--lead-body); }

/* The settings groups hold rows that are label-plus-control rather than
   label-plus-chevron, so they get padding instead of the row component. */
.settings-group { padding: var(--space-xs) var(--space-md); }

/* ── Settings rows ────────────────────────────────────────────────────────
   Label and control stack on a phone so neither is squeezed, and sit side by
   side once there is room. */
.setting-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-md);
  padding: var(--space-sm) 0;
  border-bottom: var(--border-width) solid var(--color-border);
}
.setting-row:last-of-type { border-bottom: none; }
.setting-row__label { color: var(--color-primary); font-size: var(--type-body); letter-spacing: var(--track-body); }
.setting-help {
  margin: 0;
  padding-bottom: var(--space-sm);
  color: var(--color-muted);
  font-size: var(--type-caption);
  letter-spacing: var(--track-caption);
}

/* The theme control carries an icon and a word. The word is what is announced
   and what survives forced-colors; the icon only speeds up recognition. */
.o-segment--theme button { display: inline-flex; align-items: center; gap: var(--space-xxs); min-height: 44px; }
.o-segment--theme .segment-icon { width: 16px; height: 16px; flex: none; display: inline-grid; place-items: center; }
.o-segment--theme .segment-icon svg { width: 16px; height: 16px; }

@media (max-width: 480px) {
  /* Three 44px targets will not fit one phone row beside a label, so the
     control takes the full width below it rather than shrinking the targets. */
  .setting-row { flex-direction: column; align-items: stretch; }
  .setting-row .o-segment { width: 100%; }
  .setting-row .o-segment button { flex: 1; justify-content: center; }
}

/* ── Saved charts ────────────────────────────────────────────────────────── */
.saved-charts-list { display: grid; gap: var(--space-sm); }

.saved-chart-card {
  display: grid;
  gap: var(--space-sm);
  padding: var(--space-md);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface-sunken);
}
/* The active chart is stated by a border and by the "Active" pill inside the
   card, never by the border alone. */
.saved-chart-card[data-active="true"] { border-color: var(--color-accent); }

.saved-chart-card__top,
.saved-chart-card__badges,
.saved-chart-card__actions {
  display: flex; align-items: center; gap: var(--space-xs); flex-wrap: wrap;
}
.saved-chart-card__lead { display: flex; align-items: center; gap: var(--space-sm); min-width: 0; margin-right: auto; }
.saved-chart-card__name {
  color: var(--color-primary);
  font-weight: var(--weight-semibold);
  font-size: var(--type-body);
  letter-spacing: var(--track-body);
}
.saved-chart-card__lead .saved-chart-card__name { margin-right: 0; }
.saved-chart-card__meta,
.saved-chart-card__summary { color: var(--color-muted); font-size: var(--type-caption); letter-spacing: var(--track-caption); }

.saved-chart-card__actions button {
  min-height: 44px;
  padding: var(--space-xs) var(--space-md);
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-primary);
  background: var(--color-surface-ghost);
  font-family: inherit;
  font-size: var(--type-caption);
  cursor: pointer;
  transition: transform var(--duration-instant) var(--ease-standard);
}
.saved-chart-card__actions button:active:not(:disabled) { transform: scale(var(--press-scale)); }
.saved-chart-card__actions button:disabled { opacity: 0.45; cursor: default; }

.compat-entry { margin-top: var(--space-md); border: var(--border-width) solid var(--color-border); border-radius: var(--radius-lg); }

/* ── Danger zone ───────────────────────────────────────────────────────────
   Permanent account deletion. It is its own group, at the end, with nothing
   harmless next to it — so "Delete your account" is never adjacent to something
   a person might have been reaching for. */
.o-modal__panel--danger { border-top: 3px solid var(--color-error); }

.delete-list {
  margin: 0 0 var(--space-sm);
  padding-left: 1.15rem;
  font-size: var(--type-caption);
  line-height: 1.6;
  color: var(--color-secondary);
}

#delete-account-confirm {
  width: 100%;
  margin-top: var(--space-xxs);
  /* Monospace so a stray space or a lowercase letter is visible, rather than
     something to squint at while already anxious. */
  font-family: var(--font-mono);
  letter-spacing: 0.08em;
}

.field-hint { margin: var(--space-xxs) 0 0; font-size: var(--type-fine); color: var(--color-muted); }

/* A disabled destructive button must look unavailable, not merely dimmed —
   this is the state it sits in until the confirmation is typed correctly. */
.o-btn--danger:disabled { opacity: 0.45; cursor: not-allowed; }

/* Links to the public information pages, from inside the app. */
.legal-links { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-xxs) var(--space-md); }
/* Flex items, not inline text — so the WCAG 2.5.8 exemption for links inside a
   block of prose does not cover these, and they need real height. */
.legal-links a {
  display: inline-flex; align-items: center; min-height: 44px;
  text-decoration: underline; text-underline-offset: 2px;
}

#account-export-message { margin-top: var(--space-xs); text-align: left; }

/* ══ Chart identity ══════════════════════════════════════════════════════════
   The avatar component and the identity editor.

   The fallback face is DETERMINISTIC BY OMISSION: one accent treatment for
   every chart, initials from chart-identity.js, nothing random and nothing
   hashed. The system keeps one accent on purpose, and identity is carried by
   the initials plus the adjacent name and relationship text — never by a colour
   assignment. Identical input renders identically, forever. */

.chart-avatar {
  --avatar-size: 40px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--avatar-size);
  height: var(--avatar-size);
  flex: 0 0 auto;
  border-radius: var(--radius-full);
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-weight: var(--weight-semibold);
  font-size: calc(var(--avatar-size) * 0.36);
  letter-spacing: 0.02em;
  overflow: hidden;
  user-select: none;
}
.chart-avatar--sm { --avatar-size: 30px; }
.chart-avatar--lg { --avatar-size: 72px; }

/* The uploaded picture sits over the initials; if it fails to load it is
   removed and the fallback beneath is already painted — no layout shift. */
.chart-avatar__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

@media (forced-colors: active) {
  .chart-avatar { border: 1px solid CanvasText; background: Canvas; color: CanvasText; forced-color-adjust: none; }
}

/* ── Identity editor ── */
.identity-avatar-row {
  display: flex; align-items: flex-start; gap: var(--space-md);
  margin: var(--space-sm) 0 var(--space-md);
}
.identity-avatar-actions { display: grid; gap: var(--space-xs); min-width: 0; flex: 1 1 auto; }
/* A native file input stays keyboard-reachable and announces itself; it is
   styled, not replaced. */
#identity-file, #cm-avatar-file { max-width: 100%; color: var(--color-secondary); font-size: var(--type-caption); }
#identity-file::file-selector-button,
#cm-avatar-file::file-selector-button {
  min-height: 44px;
  margin-right: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--color-surface-ghost);
  color: var(--color-primary);
  font-family: inherit;
  cursor: pointer;
}
#identity-avatar-remove { justify-self: start; }
.identity-legacy { color: var(--color-muted); }

.chart-form-error { display: grid; gap: var(--space-xs); justify-items: start; }

@media (max-width: 640px) {
  .identity-avatar-row { flex-wrap: wrap; }
}

/* ── Intrinsic-width containment ─────────────────────────────────────────────
   A <select>'s minimum width follows its LONGEST option, and the chart pickers
   carry real sentences. Without min-width: 0 they refuse to shrink below that
   text, and on a phone the LAYOUT VIEWPORT expands to fit them — the whole page
   rendered 522px wide inside a 375px screen. Found at genuine 375px, not in a
   media-query review.

   The rest of that chain: the switchers are one-column GRIDs whose implicit
   `auto` track max-content-sizes to the select, and grid items keep the default
   `min-width: auto`, so one long option's intrinsic width climbed switcher →
   section head → workspace panel → workspace track and expanded the layout
   viewport itself. minmax(0, 1fr) caps the switcher track; min-width: 0 stops
   the climb at every level it passed through. */
#chart-switcher-select,
#today-chart-select,
#transits-chart-select { min-width: 0; max-width: 100%; }

.chart-switcher,
.tr-switcher { grid-template-columns: minmax(0, 1fr); }

.workspace-panel,
.o-section-head,
.o-section-head__actions,
.me-head-actions,
.chart-chip,
.chart-chip__text { min-width: 0; }

/* The last anchor was the chart-data placements TABLE, whose hard min-content
   (~490px) expanded the mobile layout viewport on its own. It now scrolls
   inside its own wrapper instead of resizing the page, and reading-grid items
   stop propagating intrinsic widths upward. */
.me-grid > * { min-width: 0; }
#chart-placements { overflow-x: auto; min-width: 0; contain: inline-size; }

/* ── Avatar crop editor ─────────────────────────────────────────────────────
   A square stage showing exactly what will be saved, with a circular mask over
   it. The circle is a mask, not a crop: the picture is saved square, and the
   ring only shows where the avatar's rounded frame will fall. */
.avatar-crop {
  display: grid;
  gap: var(--space-sm);
  margin-block: var(--space-sm);
}

.avatar-crop__stage {
  position: relative;
  inline-size: 100%;
  max-inline-size: 320px;
  aspect-ratio: 1;
  margin-inline: auto;
  border-radius: var(--radius-card, 18px);
  overflow: hidden;
  background: var(--surface-2, #161618);
  /* grab, then grabbing, so the affordance is visible before the drag and the
     state is visible during it. */
  cursor: grab;
  /* The stage handles horizontal drag itself; without this the browser claims
     the gesture for scrolling and the picture never moves on a phone. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.avatar-crop__stage:active { cursor: grabbing; }

.avatar-crop__stage canvas {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  /* The drag target is the stage. Without this the canvas swallows the pointer
     events on some engines and the picture cannot be moved. */
  pointer-events: none;
}

/* The circular guide. A ring drawn with a huge spread shadow rather than an
   overlay element, so nothing sits between the pointer and the stage. */
.avatar-crop__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  box-shadow: 0 0 0 9999px rgb(0 0 0 / 0.45);
  pointer-events: none;
}

.avatar-crop__controls { display: grid; gap: var(--space-xxs); }

/* Full width and a real touch target: a hairline range slider is unusable with
   a thumb, which is the only input this screen gets on a phone. */
.avatar-crop__controls input[type="range"] {
  inline-size: 100%;
  min-block-size: 44px;
  accent-color: var(--accent, #b9a7ff);
}

/* ── Mobile: You as divided rows, not stacked boxes ─────────────────────────
   Every settings group was another rounded rectangle; the rows inside already
   carry the structure. Section labels + hairlines between rows say everything
   the boxes said, in less height. Danger zone is exempt on purpose: its border
   is a warning, not decoration, and a consequence that different deserves
   chrome that different. */
@media (max-width: 640px) {
  .more-section .o-group {
    border: 0; background: none; border-radius: 0; padding: 0;
  }
  .more-section .o-group > * + * { border-top: var(--border-width) solid var(--color-border); }
  .more-section .o-row { padding-left: 0; padding-right: 0; }
  #danger-zone .o-group {
    border: var(--border-width) solid var(--color-error);
    border-radius: var(--radius-lg);
    padding: var(--space-sm) var(--space-md);
  }
}
