/* SPACING: reach for a token — `--card-pad`, `--card-gap`, `--sp-1`…`--sp-6` —
   not a raw rem, and not the value in the rule above. Cards on the padding
   contract are enforced by `test/layout-tokens.test.js`; the contract and the
   list of what is on it are in `docs/visual-language.md` → Tokens. Raw values
   still in this file are legacy from a 2px grid, not a precedent to follow. */
/* The component layer: one definition of each thing the app draws more than once.
   Sections lay components out; this file says what they look like. Nothing here
   positions anything — no grid, no width, no margin that belongs to a section.

   BEFORE ADDING A COMPONENT HERE, read docs/visual-language.md. Before adding one
   ANYWHERE ELSE: don't. A second pill is how the app ended up with seven.

   Colour is still 01-tokens.css's job alone. This file names no hex. */

/* One button. Size says how much room it has; rank says what it is for.
   Fill says rank (visual-language.md, "Buttons"): solid is the primary action,
   a tint or a bordered surface is a secondary, plain is a tertiary. A button is
   not an amount, so it never wears --pos or --neg except `danger`, where the red
   IS the meaning and matches delete everywhere else.

   Size is set by min-height, not by padding alone. Container-specific padding
   overrides used to fight over height; size classes replace them. */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  box-sizing:border-box;
  min-height:2.25rem;padding:0 var(--sp-4);
  border:1px solid transparent;border-radius:var(--radius-sm);
  font-family:inherit;font-size:var(--fs-body);font-weight:500;line-height:1.2;
  white-space:nowrap;cursor:pointer;
  transition:background-color var(--hover-dur) var(--hover-ease),border-color var(--hover-dur) var(--hover-ease),color var(--hover-dur) var(--hover-ease),filter var(--hover-dur) var(--hover-ease),transform .12s;
}
.btn:active{transform:scale(.97);}
.btn.sm{min-height:1.75rem;padding:0 var(--sp-3);font-size:var(--fs-label);}
.btn.xs{min-height:1.5rem;padding:0 var(--sp-2);font-size:var(--fs-meta);}

.btn.primary{background:var(--accent-bright);color:var(--accent-bright-ink);font-weight:600;}
.btn.primary:hover{filter:brightness(1.10);}
.btn.secondary{background:var(--surface2);border-color:var(--border);color:var(--text);}
.btn.secondary:hover{background:var(--surface3);}
.btn.plain{background:transparent;border-color:var(--border);color:var(--muted);}
.btn.plain:hover{border-color:var(--border-hover);color:var(--text);}
.btn.danger{background:transparent;border-color:var(--neg);color:var(--neg);}
.btn.danger:hover{background:var(--neg);color:var(--bg);}
.btn.plain.confirm{border-color:var(--pos);color:var(--pos);}
.btn.plain.confirm:hover{background:var(--pos);color:var(--bg);}
.btn.plain.destructive:hover{border-color:var(--neg);color:var(--neg);}
.btn.plain.edit:hover{border-color:var(--accent);color:var(--accent);}

.pill{
  display:inline-flex;align-items:center;gap:var(--sp-1);
  padding:var(--sp-1) var(--sp-3);
  border-radius:var(--radius-pill);
  font-size:var(--fs-meta);font-weight:600;line-height:1.35;
  white-space:nowrap;
}
.pill.sm{padding:0 var(--sp-2);font-size:var(--fs-micro);font-weight:500;}
.pill.quiet{background:var(--surface2);color:var(--muted);}
.pill.pos{background:var(--tint-green);color:var(--pos);}
.pill.neg{background:var(--tint-red);color:var(--neg);}
.pill.warn{background:var(--tint-amber);color:var(--accent);}
.pill.info{background:var(--tint-violet);color:var(--violet);}
.pill.util{background:var(--tint-teal);color:var(--teal);}
.pill.alt{background:var(--tint-orange);color:var(--orange);}
.pill.source{background:color-mix(in srgb,var(--c) 18%,transparent);color:var(--c);}
.pill.dot::before{content:"";width:var(--sp-2);height:var(--sp-2);border-radius:50%;background:currentColor;flex:0 0 auto;}
/* A choice in a row of choices (a sort order): quiet until picked, then a bordered
   capsule. It is a button, so the focus ring is the browser's. */
.chip{
  padding:var(--sp-1) var(--sp-3);
  border:1px solid transparent;border-radius:var(--radius-pill);
  background:transparent;color:var(--muted);
  font-family:inherit;font-size:var(--fs-meta);font-weight:500;line-height:1.35;
  white-space:nowrap;cursor:pointer;
  transition:background-color var(--hover-dur) var(--hover-ease),color var(--hover-dur) var(--hover-ease);
}
.chip:hover{color:var(--text);}
.chip.on{background:var(--surface2);border-color:var(--border);color:var(--text);}

/* Line-item breakdown on an expense row (day card or table). Flat chip, not a pill. */
.items-pop{
  appearance:none;
  -webkit-appearance:none;
  display:inline-flex;
  align-items:center;
  margin:0;
  padding:0 var(--sp-2);
  min-height:var(--sp-4);
  border:1px solid var(--border);
  border-radius:var(--radius-pill);
  background:transparent;
  color:var(--muted);
  font-family:inherit;
  font-size:var(--fs-micro);
  font-weight:500;
  line-height:1.2;
  white-space:nowrap;
  cursor:pointer;
  box-shadow:none;
  transition:background-color var(--hover-dur) var(--hover-ease),border-color var(--hover-dur) var(--hover-ease),color var(--hover-dur) var(--hover-ease);
}
.items-pop:hover{
  border-color:var(--border-hover);
  background:var(--surface2);
  color:var(--text);
}
.items-pop:focus-visible{
  outline:2px solid color-mix(in srgb,var(--accent) 40%,transparent);
  outline-offset:1px;
}

/* A swatch says which thing a mark belongs to. A circle is an identity — a
   category, a source, something with a name. A rounded square is a series in a
   chart — a measure. The colour always arrives as --c, never as a literal:
   from getExpCatColor() / getIncCatColor() for a category, chartColor() for a
   series (AGENTS.md, "Colour is a contract"). */
.swatch{display:inline-block;flex:0 0 auto;width:var(--sp-2);height:var(--sp-2);background:var(--c);}
.swatch.id{border-radius:50%;}
.swatch.series{border-radius:2px;}
.swatch.avg{background:var(--chart-neutral);}
.tr-legend .swatch.now{background:linear-gradient(90deg,var(--violet) 0%,var(--teal) 34%,var(--accent) 67%,var(--orange) 100%);}

/* A proportion drawn as a bar. The track is the whole, the fill is the part.
   One height; `tall` is for a measure against a limit (Budget), where the
   outline carries a boundary and needs the room. */
.meter{position:relative;height:var(--sp-2);border-radius:var(--radius-pill);background:var(--surface2);overflow:hidden;}
.meter > i{display:block;height:100%;border-radius:var(--radius-pill);background:var(--chart-neutral);}
.meter > i.avg{background:var(--chart-neutral);}
.meter.tall{height:var(--sp-3);}
.meter.track-violet{background:color-mix(in srgb,var(--violet) 55%,transparent);}
.meter.track-green{background:color-mix(in srgb,var(--chart-pos) 55%,transparent);}
.meter.track-red{background:var(--chart-neg);}
.meter > i.fill-gold{background:var(--accent);}
.meter > i.fill-violet{background:var(--violet);}
.meter > i.fill-muted{background:var(--border-hover);}
/* Certain against estimated: the fill is money already committed (recurring due,
   instalments scheduled), the track is the average one-off pattern behind it.
   Mark weights, per visual-language.md — the legend text beside them takes
   `--accent` and `--muted`, the same two hues a step lighter. */
.meter > i.fill-committed{background:var(--chart-committed);}
.meter.track-muted{background:var(--chart-neutral);}

/* Title left, at most one Level 4 caption right (visual-language.md, "The card").
   `inset` is the variant for a heading INSIDE a card — a settings form that
   groups several controls under it — and the underline is the only difference. */
.card-head{
  display:flex;justify-content:space-between;align-items:center;gap:var(--sp-2);
  margin:var(--sp-5) 0 var(--sp-2);
  font-size:var(--fs-label);font-weight:500;color:var(--text);
}
.card-head > .cap{
  font-size:var(--fs-meta);font-weight:400;color:var(--muted);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.card-head.inset{margin:0 0 var(--sp-3);padding-bottom:var(--sp-3);border-bottom:1px solid var(--border);}

/* A line of explanation or a call to act. Quiet by default; a tint only when the
   note carries a state worth the colour (visual-language.md, "Colour means one
   thing"). `act` is a note you can click. */
.note{
  background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);
  padding:var(--sp-3) var(--sp-4);
  font-size:var(--fs-label);color:var(--muted);line-height:1.6;
}
.note strong{color:var(--text);}
.note.info{background:var(--tint-violet);border-color:color-mix(in srgb,var(--violet) 45%,transparent);color:var(--violet);}
.note.warn{background:var(--tint-amber);border-color:color-mix(in srgb,var(--accent) 45%,transparent);color:var(--accent);}
.note.alert{background:var(--tint-red);border-color:color-mix(in srgb,var(--neg) 45%,transparent);color:var(--neg);}
.note.act{display:flex;align-items:center;gap:var(--sp-2);cursor:pointer;transition:filter .15s;}
.note.act:hover{filter:brightness(1.08);}
.note.act svg{width:1.125rem;height:1.125rem;flex:0 0 auto;}
.note.spaced{margin-bottom:var(--sp-3);}
.note.pre-wrap{white-space:pre-wrap;}

/* Utilities — type emphasis only; add a new one only with a reason. */
.muted{color:var(--muted);}
.text-accent{color:var(--accent);}
.text-label{font-size:var(--fs-label);}
.text-meta{font-size:var(--fs-meta);}
.text-micro{font-size:var(--fs-micro);}

/* Single-date calendar (Add expense / income, starting balance). */
.date-picker{position:relative;width:100%;}
.date-picker-trigger{
  display:flex;align-items:center;width:100%;box-sizing:border-box;
  background-color:var(--input-bg);border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius-sm);padding:0.625rem var(--sp-3);
  font-family:inherit;font-size:var(--fs-body);font-weight:400;line-height:1.2;
  text-align:start;cursor:pointer;
  transition:border-color .15s,box-shadow .15s;
}
.date-picker-trigger:hover{border-color:var(--border-hover);}
.date-picker-trigger:focus{outline:none;box-shadow:0 0 0 3px var(--tint-amber);border-color:var(--accent);}
.date-picker-text{font-variant-numeric:tabular-nums;}
.date-picker-panel,.select-menu-panel{
  padding:var(--sp-3);
  background:var(--surface3);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:0 8px 28px color-mix(in srgb,var(--bg) 55%,transparent);
}
.date-picker-panel{
  position:absolute;z-index:20;inset-inline-start:0;top:calc(100% + var(--sp-1));
  min-width:16.5rem;
}
.select-menu-panel{
  display:none;
  position:fixed;z-index:500;
  min-width:8rem;max-height:24rem;overflow-y:auto;
  -webkit-app-region:no-drag;
}
.select-menu-panel.show{display:block;}
.date-picker--up .date-picker-panel{top:auto;bottom:calc(100% + var(--sp-1));}
.date-picker-head{position:relative;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);margin-bottom:var(--sp-2);}
.date-picker-title{font-size:var(--fs-label);font-weight:600;color:var(--text);flex:1;text-align:center;}
.date-picker-nav{
  width:1.75rem;height:1.75rem;border-radius:var(--radius-sm);border:0;
  background:transparent;color:var(--muted);font-size:1.125rem;line-height:1;cursor:pointer;
}
.date-picker-nav:hover{color:var(--text);}
.date-picker-week{
  display:grid;grid-template-columns:repeat(7,1fr);gap:2px;
  margin-bottom:var(--sp-1);font-size:var(--fs-micro);font-weight:500;color:var(--muted);text-align:center;
}
.date-picker-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;}
.date-picker-pad{display:block;min-height:2rem;}
.date-picker-day{
  min-height:2rem;padding:0;border:1px solid transparent;border-radius:var(--radius-sm);
  background:transparent;color:var(--text);font-size:var(--fs-body);font-weight:500;
  font-variant-numeric:tabular-nums;cursor:pointer;
}
.date-picker-day--today{border-color:var(--accent);color:var(--accent);}
.date-picker-day--selected,.select-menu-opt--selected{background:var(--accent-bright);color:var(--accent-bright-ink);font-weight:600;}
.date-picker-day--selected:hover,.select-menu-opt--selected:hover{filter:brightness(1.06);}
.select-menu-opt{
  display:block;width:100%;box-sizing:border-box;
  min-height:2rem;padding:0 var(--sp-3);border:1px solid transparent;border-radius:var(--radius-sm);
  background:transparent;color:var(--text);font-family:inherit;font-size:var(--fs-body);font-weight:500;
  text-align:start;cursor:pointer;
}
/* The whole text of a label cut short by an ellipsis: fixed, so no scrolling or
   clipping container can cut it off (showTruncTip in js/08-ui.js). */
.trunc-tip{
  display:none;
  position:fixed;
  z-index:200;
  max-width:min(22rem,90vw);
  padding:var(--sp-1) 0.5625rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--border);
  background:var(--surface3);
  color:var(--text);
  font-size:var(--fs-micro);
  font-weight:500;
  line-height:1.35;
  text-align:start;
  white-space:normal;
  box-shadow:0 6px 16px -4px rgba(0,0,0,.5);
  pointer-events:none;
}
/* Shared hover wash (js/08-ui.js): one pill per list, same motion as the sidebar. */
.hover-pill-rail{position:relative;}
/* The category menu is placed with position:fixed. This class is one rule later and
   would otherwise win, dropping the open list into the page flow, off the bottom. */
.select-menu-panel.hover-pill-rail{position:fixed;}
.hover-pill{
  position:absolute;
  z-index:0;
  top:0;
  left:0;
  width:0;
  height:0;
  border-radius:var(--radius-sm);
  background:var(--hover-pill-bg);
  opacity:0;
  pointer-events:none;
  transition:left var(--hover-dur) var(--hover-ease),top var(--hover-dur) var(--hover-ease),width var(--hover-dur) var(--hover-ease),height var(--hover-dur) var(--hover-ease),opacity .2s ease;
}
.hover-pill.deep{background:var(--hover-pill-bg-deep);}
@media (prefers-reduced-motion: reduce){
  .hover-pill{transition:none;}
}
.side-nav .tab,.table-wrap tbody tr,.split-rows .exp-cat-row,.split-rail .exp-cat-row,
.split-rail .subs-row:not(.subs-total),.split-rail .budget-row,.ctx-menu .ctx-item,
.select-menu-panel .select-menu-opt,.date-picker-panel .date-picker-day,.date-picker-head .date-picker-nav,
.sec2-month .sec2-nav,.imp-log .line,#panel-admin .cat-list .cat-item,#cat-items-legend .cat-items-lg-item,
.guide-sections .guide-sec{position:relative;z-index:1;}
.side-nav .tab:hover:not(.active),
.table-wrap tbody tr:hover,
.split-rows .exp-cat-row:hover,.split-rail .exp-cat-row:hover,
.split-rail .subs-row:not(.subs-total):hover,.split-rail .budget-row:hover,
.ctx-menu .ctx-item:hover,
.select-menu-panel .select-menu-opt:hover:not(.select-menu-opt--selected),
.date-picker-panel .date-picker-day:hover:not(.date-picker-day--selected),
.date-picker-head .date-picker-nav:hover,.sec2-month .sec2-nav:hover,.imp-log .line:hover,
#panel-admin .cat-list .cat-item:hover,#cat-items-legend .cat-items-lg-item:hover,
.guide-sections .guide-sec:hover{background:transparent !important;}
/* A clipped list inside a card: a bottom fade and chevron only while more rows sit
   below (syncListScrollHints in js/08-ui.js). At the end the fade is off; rubber-band
   wheel input shows a hairline on the frame, not a second fade. */
.list-scroll-frame{overflow:hidden;}
.list-scroll-hint{
  position:absolute;
  inset-inline:0;
  bottom:0;
  height:2.25rem;
  display:flex;
  align-items:flex-end;
  justify-content:center;
  padding-bottom:0.125rem;
  pointer-events:none;
  opacity:0;
  background:linear-gradient(to top,color-mix(in srgb,var(--surface) 92%,transparent),transparent);
  transition:opacity .2s ease;
}
.list-scroll-hint::after{
  content:'';
  width:0.5rem;
  height:0.5rem;
  margin-bottom:0.125rem;
  border-inline-end:2px solid var(--muted);
  border-block-end:2px solid var(--muted);
  transform:rotate(45deg);
  opacity:.85;
}
.list-scroll-frame[data-scroll-hint="down"] .list-scroll-hint{opacity:1;}
.list-scroll-frame[data-overscroll="bottom"]::after{
  content:'';
  position:absolute;
  inset-inline:var(--sp-4);
  bottom:0;
  height:2px;
  border-radius:2px;
  background:var(--border-hover);
  opacity:calc(var(--list-scroll-pull,0) / 34);
  pointer-events:none;
}
.list-scroll-pulling{will-change:transform;}
@media (prefers-reduced-motion: reduce){
  .list-scroll-hint{transition:none;}
}
