@layer pfa-app {
/* ---------- Secondary details ---------- */
/* A CARD THAT OPENS IS STILL A CARD: ONE EDGE, ONE INK.
 *
 * This summary padded itself horizontally on top of whatever already held it
 * - the card's own 24px for "Data & settings", the card BODY's for the
 * nested "Add an asset or debt" - so both sat 24px right of every other card
 * heading on their tab, the same double inset `.card-compact` had. The
 * container owns the horizontal edge; the summary owns only its height.
 *
 * The ink was `--dim` while every other card heading is `--fg`, so the one
 * card on the tab that opens onto everything read as the least important
 * thing on it. Its label now sits in a `.card-title`, which carries the
 * shared heading treatment and puts its icon in the shared glyph column. */
.secondary > summary,
.secondary.pfa-inline-disclosure .pfa-inline-disclosure-trigger {
  list-style: none;
  cursor: pointer;
  padding: var(--s4) 0;
  font-weight: 640;
  color: var(--fg);
  display: flex;
  align-items: center;
  gap: 8px;
}
.secondary > summary::-webkit-details-marker {
  display: none;
}
/* The head's own summary, in the right-hand slot every collapsible card uses.
   The chevron's `margin-left: auto` below would otherwise SHARE the free
   space with this one, parking the summary mid-row, so where a summary is
   present it claims the space and the chevron simply follows it. */
.secondary > summary > .card-disclosure-note {
  margin-left: auto;
  text-align: right;
}
.secondary > summary:has(> .card-disclosure-note)::after {
  margin-left: 0;
}
.secondary[open] > summary,
.secondary.pfa-inline-disclosure .pfa-inline-disclosure-trigger[data-state="open"] {
  border-bottom: 1px solid var(--edge);
  color: var(--fg);
}
/* The same rotating chevron details.explainer already has, added here so every
   closed-by-default disclosure in the app - "Data & settings", "How this is
   worked out", "More filters" - shares one visual language for "this is
   closed, but clearly openable". The disclosure itself already worked via the
   native <details> element; this only adds the missing visual cue. */
.secondary > summary::after,
.secondary.pfa-inline-disclosure .pfa-inline-disclosure-trigger::after {
  content: '';
  margin-left: auto;
  width: 8px;
  height: 8px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s;
  opacity: 0.55;
}
.secondary[open] > summary::after,
.secondary.pfa-inline-disclosure .pfa-inline-disclosure-trigger[data-state="open"]::after {
  transform: rotate(225deg);
}
.sec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  padding: var(--s4) var(--card-pad-x);
}
.sec-value {
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums lining-nums;
}
.sec-label {
  margin-top: 2px;
}
.secondary p {
  padding: 0 var(--card-pad-x) var(--s4);
  margin: 0;
}
/* The manage-data actions now live inside the "Data & settings" details as a
   labelled section (Recommendation 3: one tail card, not two). .sec-section
   carries the card's horizontal padding so the buttons never touch the edge;
   the (0,2,1) `.secondary .sec-section p` rule beats the broad `.secondary p`
   so its paragraphs are not double-indented. */
.secondary .sec-section {
  padding: var(--s4) var(--card-pad-x);
}
.secondary .sec-section p {
  padding: 0;
  margin: 0 0 12px;
}
/* .sec-manage-wrap's own box (display + padding) is declared once, in
   feature-additions.css's "DATA & SETTINGS ALIGNMENT RULE" block - the
   descendant selectors above match a nested .sec-section regardless of the
   wrap's display value, so nothing here depends on it being display:contents.
   A second declaration here previously fell out of sync with that one and
   silently reintroduced the double-indent it exists to prevent. */
.secondary .sec-section + .sec-section,
.secondary .sec-section + .sec-manage-wrap > .sec-section:first-child {
  border-top: 1px solid var(--edge);
}
.sec-subhead {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 640;
  color: var(--fg);
  margin: 14px 0 10px;
}
/* A card-level sub-heading aligns its label to the same left inset as the
   .recurring-row content beneath it (rows carry 10px inner padding for their
   hover pill), so heading and rows share one left edge. Scoped to a direct
   card child so Data & settings sub-heads inside .sec-section are unaffected. */
.card > .sec-subhead {
  padding-left: 10px;
}
.sec-subhead svg {
  width: var(--icon-inline);
  height: var(--icon-inline);
  color: var(--accent-text);
  shape-rendering: geometricPrecision;
}
/* Title left, the figure the section reports right - the shape a collapsed
   card already uses, so a sub-section inside one reads as part of it. */
.sec-subhead-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.sec-subhead:has(> .sec-subhead-note),
.sec-subhead:has(> .sec-subhead-actions) {
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 2px 12px;
}
.sec-subhead-note {
  font-weight: 450;
  margin-right: auto;
}
/* The section's controls, on the heading's own line WHEN THEY FIT.
   When they do not, the row's own `space-between` puts them at the start of
   the next line - directly under the heading, at the heading's left edge,
   which is where a control that has left its label belongs. That is true at
   every width, so it is not a phone rule: a narrow desktop window wraps the
   same way and reads the same way. (`margin-left: auto` was tried here and
   held them against the right edge on the wrapped line, which is exactly what
   made these rows look offset rather than placed.) */
.sec-subhead-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-weight: 450;
}
.sec-subhead-actions > .manage-actions {
  margin: 0;
}
/* IN THIS SLOT A FIELD STATES A WIDTH, NOT A FLEX.
 *
 * The slot's width is DERIVED from what is in it, so its contents have to be
 * determinate. "Your name" declared `flex: 1 1 160px` - grow to fill whatever
 * line you land on - and the group's derived width came out at 256px, below
 * the 359px its own contents need, so "Save name" wrapped to a second line
 * while 590px of the row sat empty beside the heading. Measured: a
 * `flex-basis` with shrink still derives 256px; a real `width` derives 359px.
 *
 * So: no growing, and a definite width. It may still shrink, and when the
 * group as a whole no longer fits, the ROW wraps it under the heading -
 * which is the one behaviour a reader can predict. Verified at 846px (one
 * row, flush right) and squeezed to 300px (its own line, no overflow). */
.sec-subhead-actions .manage-actions > * {
  flex-grow: 0;
}
.sec-subhead-actions .name-field {
  flex-basis: auto;
  width: 260px;
  max-width: 100%;
}
details.explainer,
.explainer.pfa-inline-disclosure {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--edge);
}
details.explainer > summary,
.explainer.pfa-inline-disclosure .pfa-inline-disclosure-trigger {
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  list-style: none;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--dim);
}
details.explainer > summary::-webkit-details-marker {
  display: none;
}
details.explainer > summary::before,
.explainer.pfa-inline-disclosure .pfa-inline-disclosure-trigger::before {
  content: '';
  display: inline-flex;
  width: 14px;
  height: 14px;
  flex: none;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9' fill='none' stroke='%23000' stroke-width='1.8'/%3E%3Ccircle cx='12' cy='8' r='.6' fill='%23000'/%3E%3Cpath d='M11 11h1.4v5.4' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E")
    center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9' fill='none' stroke='%23000' stroke-width='1.8'/%3E%3Ccircle cx='12' cy='8' r='.6' fill='%23000'/%3E%3Cpath d='M11 11h1.4v5.4' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E")
    center / contain no-repeat;
  opacity: 0.8;
}
details.explainer > summary::after,
.explainer.pfa-inline-disclosure .pfa-inline-disclosure-trigger::after {
  content: '';
  margin-left: auto;
  width: 8px;
  height: 8px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s;
  opacity: 0.6;
}
details.explainer[open] > summary::after,
.explainer.pfa-inline-disclosure .pfa-inline-disclosure-trigger[data-state="open"]::after {
  transform: rotate(225deg);
}
.explainer-body {
  padding-top: var(--s3);
}
.fact-value + .muted {
  margin-top: var(--s2);
}

details.explainer > summary:focus-visible,
.explainer.pfa-inline-disclosure .pfa-inline-disclosure-trigger:focus-visible,
.secondary > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ---------- Empty state ---------- */
.empty {
  text-align: center;
  padding: 42px 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.empty-icon {
  color: var(--accent-text);
  display: grid;
  place-items: center;
  width: 68px;
  height: 68px;
  border-radius: 18px;
  background: color-mix(in srgb, var(--accent) 10%, var(--panel-2));
}
.empty h2 {
  font-size: 22px;
}
.empty p {
  max-width: 460px;
  color: var(--dim);
}
.app-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 64px 24px;
  color: var(--dim);
  font-size: 13.5px;
}

.pfa-sonner {
  --pfa-sonner-bottom: calc(var(--dock-edge) + max(var(--dock-bottom, 0), env(safe-area-inset-bottom)));
}
.pfa-sonner [data-sonner-toast] {
  width: max-content;
  min-width: min(280px, 92vw);
  max-width: min(92vw, 560px);
  padding: 11px 16px;
  box-shadow: var(--shadow-lg);
  font: inherit;
  font-size: 13.5px;
}
.pfa-sonner [data-sonner-toast] [data-content] {
  flex: 1;
  min-width: 0;
}
.pfa-sonner [data-sonner-toast] [data-button] {
  border: 0;
  background: transparent;
  color: color-mix(in srgb, var(--accent) 60%, var(--tip-fg));
  font-weight: 700;
  cursor: pointer;
}

/* ---------- Busy overlay ---------- */
.busy {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: none;
  place-items: center;
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.busy.show {
  display: grid;
}
.busy-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px;
  border-radius: 14px;
  background: var(--panel);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-lg);
}
.busy-spin,
.spin {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2.5px solid color-mix(in srgb, var(--accent) 30%, var(--edge));
  border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
  display: inline-block;
}
svg.spin {
  border: 0;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(480deg);
  }
}

/* ---------- Notice banner (install / backup / first-run) ----------
   In the document flow, directly under the sticky header - not a floating
   overlay. As a fixed bar it covered whatever the page had scrolled to, and at
   scroll zero on the Plan tab that was the headline figure itself, sliced
   through the middle of the "Free spending" row. A notice is not a toast: it
   stays until it is answered, so it takes up room rather than borrowing it.
   In flow it pushes content down, scrolls away with the page, and can hide
   nothing at any scroll position.
   Its top margin is the page's top space, and it has no bottom margin: #app's
   top padding is the one gap between the notice and the first card. */
.install-banner {
  display: none;
  z-index: var(--z-banner);
  align-items: center;
  gap: 12px;
  margin: var(--page-top) auto 0;
  padding: 12px 16px;
  border-radius: 16px;
  width: calc(
    100% - 2 * clamp(14px, 3vw, 28px) - env(safe-area-inset-left) - env(safe-area-inset-right)
  );
  max-width: calc(
    var(--workspace-max, 1240px) - 2 * clamp(14px, 3vw, 28px) - env(safe-area-inset-left) - env(safe-area-inset-right)
  );
  background: var(--surface-quiet);
  border: 1px solid var(--hairline-soft);
  box-shadow: none;
  font-size: 13px;
}
.install-banner.show {
  display: flex;
  animation: notice-in 0.18s ease-out;
  transition:
    opacity 0.22s ease,
    transform 0.22s ease;
}
.install-banner.show:not([id='first-run-banner']):has(~ main:not([data-view='overview'])),
.install-banner.show[id='first-run-banner']:not([data-step='sample-preview']):has(~ main:not([data-view='overview'], [data-view='activity'])) {
  display: none;
}
@media (max-width: 639px) {
  .install-banner {
    max-width: calc(100vw - 24px - env(safe-area-inset-left) - env(safe-area-inset-right));
    gap: var(--s1);
    padding: var(--s1) var(--s2);
    font-size: var(--t-label);
  }
  .install-banner > span:not(.install-icon) {
    flex: 1;
    min-width: 0;
  }
  .install-banner > button {
    flex: none;
  }
  .install-banner[id='first-run-banner']:not([data-step='sample-preview']) {
    flex-wrap: wrap;
    justify-content: flex-end;
  }
  .install-banner[id='first-run-banner']:not([data-step='sample-preview']) > span:not(.install-icon) {
    flex-basis: calc(100% - var(--s6));
  }
}
@media (prefers-reduced-motion: reduce) {
  .install-banner.show {
    transition: none;
  }
}
@keyframes notice-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .install-banner.show {
    animation: none;
  }
}

/* (The bottom reservation that used to sit here is gone with the floating
   banner: a notice in the document flow occupies its own space, so there is
   nothing to reserve and nothing that can be reserved wrongly. The measured
   --banner-clear it depended on went with it.) */
.install-icon {
  color: var(--accent-text);
  display: inline-flex;
}
/* (A toast and the notice can no longer overlap: one floats, the other is in
   the flow, so they never compete for the same slot.) */
#greeting {
  box-sizing: border-box;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  -webkit-backdrop-filter: saturate(1.3) blur(16px);
  backdrop-filter: saturate(1.3) blur(16px);
  border-bottom: 1px solid color-mix(in srgb, var(--fg) 6%, transparent);
  color: var(--fg);
  transition:
    max-height 0.28s ease,
    opacity 0.28s ease;
}
#greeting.show {
  max-height: 220px;
  opacity: 1;
}
#greeting .greeting-inner {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 36px;
  align-items: center;
  gap: 14px;
  padding: 12px calc(clamp(14px, 3vw, 28px) + env(safe-area-inset-right)) 12px
    calc(clamp(14px, 3vw, 28px) + env(safe-area-inset-left));
}
#greeting .greeting-text {
  grid-column: 2;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  text-align: center;
}
#greeting .greeting-body {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  min-width: 0;
  text-align: center;
}
#greeting .greeting-heading {
  font-size: 16px;
  font-weight: 700;
}
#greeting .greeting-sub {
  margin: 6px 0 0;
}
#greeting .greeting-dismiss {
  grid-column: 3;
  justify-self: end;
  min-width: var(--tap-lg);
}

/* ---------- Overlays: picker / passphrase / progress ---------- */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 18px;
  background: color-mix(in srgb, #000 42%, transparent);
}
.picker {
  width: min(460px, 96vw);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  padding: 16px;
}
.picker.wide {
  width: min(560px, 96vw);
}
.picker-head {
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 12px;
}
.picker-list {
  max-height: 240px;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  margin-bottom: 12px;
}
.picker-item {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  text-align: left;
  padding: var(--s2) var(--s3);
  border: 0;
  background: transparent;
  border-radius: 9px;
  cursor: pointer;
  font-size: 13.5px;
}
.picker-item:hover {
  background: var(--soft-hover);
}
.picker-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.picker-item.is-selected {
  background: color-mix(in srgb, var(--accent) 10%, var(--panel-2));
  font-weight: 650;
  border-color: color-mix(in srgb, var(--accent) 58%, var(--hairline));
}
.picker-item.current:not(.is-selected) {
  color: var(--accent-text);
}
.picker-actions .btn.primary:disabled {
  opacity: 1;
  background: var(--panel-2);
  color: var(--dim);
  border-color: var(--edge);
}
/* The one item in the list that MAKES something rather than choosing it. It
   takes the same row shape so it lands where the list's last row would, and
   the accent text is what tells it apart from the choices above it. */
.picker-item.picker-create {
  color: var(--accent-text);
  font-weight: 650;
  border-style: dashed;
}
.picker-item {
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
  border: 1px solid var(--hairline);
  border-radius: 10px;
}
@media (max-width: 639px) {
  .picker-item {
    min-height: var(--tap-lg);
  }
}
@media (pointer: coarse) and (min-width: 640px) {
  .picker-item {
    min-height: var(--tap-lg);
  }
}

.picker-scope,
.tag-picker-new {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--s2) var(--s0) var(--s3);
  border-top: 1px solid var(--edge);
}
.scope {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  cursor: pointer;
}
.picker-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
  padding-top: var(--s4);
  border-top: 1px solid var(--hairline-soft, var(--hairline));
}

.picker-decision,
.picker-more {
  margin: 8px 0 0;
  border-top: 1px solid var(--hairline-soft, var(--hairline));
}
.picker-decision .pfa-inline-disclosure-trigger,
.picker-more .pfa-inline-disclosure-trigger {
  min-height: 36px;
  font-size: 13px;
  font-weight: 600;
}
.picker-decision-body,
.picker-more-body {
  padding: 4px 0 2px;
}
.picker-decision .picker-scope {
  margin: 0;
  padding-bottom: var(--s2);
}
.picker-more-body {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='file']),
textarea,
select {
  font-size: var(--t-field);
}

.pass {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--edge);
  border-radius: 10px;
  background: var(--panel-2);
  color: var(--fg);
  margin-bottom: 12px;
}
.prog-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 320px;
  overflow: auto;
}
.prog-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid var(--edge);
  border-radius: 10px;
}
.prog-name {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.prog-status {
  font-size: 12.5px;
  color: var(--dim);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.prog-status .ok {
  color: var(--good-ink);
  font-weight: 650;
}
.prog-status .warnc {
  color: var(--warn-ink);
  font-weight: 650;
}

/* ---------- Tooltip ---------- */
.tip {
  position: fixed;
  z-index: 95;
  transform: translate(-50%, -100%);
  pointer-events: none;
  background: var(--tip-bg);
  color: var(--tip-fg);
  padding: 7px 10px;
  border-radius: 8px;
  font-size: 12px;
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transition: opacity 0.12s;
  display: flex;
  flex-direction: column;
  gap: 1px;
  white-space: nowrap;
}
.tip.show {
  opacity: 1;
}
.tip b {
  font-weight: 700;
}

/* ---------- Footer ---------- */
.footer {
  max-width: 1240px;
  margin: 8px auto 0;
  padding: 0 calc(clamp(14px, 3vw, 28px) + env(safe-area-inset-right)) 0
    calc(clamp(14px, 3vw, 28px) + env(safe-area-inset-left));
  color: var(--dim);
  font-size: 12px;
}
}
