/* pagum's adaptations of the operator's demo (20260929-018).
 *
 * design.css is the demo's stylesheet, generated and never edited here. The
 * demo draws one phone, 375 px wide, with a status bar, where every screen is
 * a scrolling box and a script swaps them. The app is a real page in a real
 * browser: the document scrolls, bars are fixed, safe areas come from the
 * device, a sheet is a <dialog> and a menu a popover, a desktop window is
 * wide, and the CSP refuses style attributes. Each rule below says which of
 * those it answers. Keep this file to that: a look the demo already draws
 * belongs to the demo.
 */

/* ---- tokens the demo uses but never defines ---------------------------- */
:root {
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
  --column: 640px; /* the phone's column, centred on a wide window */
}
.light {
  /* The demo's sheet and popover start from --lg-gloss and its chat bubbles
   * cast --lg-drop; neither is defined there, which in a browser drops the
   * whole background. These give them the glass the rest of the demo has. */
  --lg-gloss: rgba(255, 255, 255, 0.55);
  --lg-drop: rgba(20, 30, 60, 0.12);
  color-scheme: light;
}
/* The backdrop is the image alone: its fog, the demo's densest, is drawn
 * into it (scripts/alps.mjs; the operator, 2026-09-30: "only keep dense fog.
 * If possible just make the babckground svg like that (without additional
 * computations)"), where design.css veils it with a gradient of the page's
 * colour. What shows wherever it is not drawn (before it is, past the
 * page's ends when it bounces, and on any frame a browser draws while it
 * repaints it) is its top colour, --sky: a bluer one than the image's
 * flashed between pages (the operator, 2026-09-30: "when going from a page
 * to an other the background flashes blue"). theme.js paints the browser's
 * bar with it. */
:root.light {
  --backdrop: var(--alp-l, #f2f3f7) center bottom / cover no-repeat;
  --sky: var(--alp-v-l, var(--page));
}
:root.dark {
  --backdrop: var(--alp-d, #000000) center bottom / cover no-repeat;
  --sky: var(--alp-v-d, var(--page));
}
.dark {
  --lg-gloss: rgba(255, 255, 255, 0.1);
  --lg-drop: rgba(0, 0, 0, 0.5);
  color-scheme: dark;
}

/* ---- the page: the document scrolls, the backdrop stays ---------------- */
html {
  overflow-x: clip;
  /* The backdrop's sky, not black or white (--sky, above). */
  background: var(--sky, var(--page));
  -webkit-text-size-adjust: 100%;
}
/* Firefox alone still flashed between pages (the operator, 2026-09-30: "i
 * found the issue is only present on firefox, and not on ios or chrome"):
 * there, what shows behind the backdrop while a page changes is <html>
 * itself, so <html> carries the backdrop too, fixed to the screen as the
 * layer is, and a frame without the layer looks the same as one with it.
 * Firefox only: Safari draws a fixed background as a scrolling one, the
 * size of the whole page. */
@supports (-moz-appearance: none) or (-moz-orient: inline) {
  html {
    background: var(--backdrop, var(--sky, var(--page)));
    background-attachment: fixed;
    background-color: var(--sky, var(--page));
  }
}
body {
  margin: 0;
  min-height: 100dvh;
  color: var(--text);
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 17px;
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
}
/* The demo paints its backdrop on the phone; here it is a fixed layer of its
 * own, named, so a page transition slides the page and not the mountains. */
.alps {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--backdrop);
  pointer-events: none;
  view-transition-name: alps;
}
a {
  color: var(--primary);
}
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
[hidden] {
  display: none !important;
}

/* A screen is the page, not a box in a phone: no absolute placement, no own
 * scroll, the status bar's 54 px replaced by the device's safe area. */
.screen,
.screen.on {
  display: block;
  position: relative;
  inset: auto;
  overflow: visible;
  min-height: 100dvh;
  max-width: var(--column);
  margin-inline: auto;
  padding-top: var(--sat);
  padding-bottom: calc(104px + var(--sab));
}
.screen > .header {
  top: var(--sat);
}
.top-blur .tb {
  top: calc(-1 * var(--sat));
  height: calc(var(--sat) + 2px);
  /* full width on a wide window, not only the column */
  left: calc(50% - 50vw);
  right: calc(50% - 50vw);
}
.screen:has(> .header :is(a, button, .bubble, input, h1:not(:empty))) .top-blur .tb {
  height: calc(var(--sat) + 86px);
}
.screen > .header:has(a) {
  visibility: visible;
}
.header h1 bdi,
.header h1 {
  min-width: 0;
}
/* Bubbles and pills are links as often as buttons here. */
a.bubble,
a.pill,
a.row,
a.w-row,
a.tab,
a.tab-plus,
a.lg-pill,
a.icon-button {
  text-decoration: none;
}
a.row,
a.w-row {
  color: inherit;
}
a.row.button {
  color: var(--primary);
}
button.row {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
}
button.row.button {
  color: var(--primary);
}
button.row.danger {
  color: var(--danger);
}
.bubble .ionicon {
  font-size: 22px;
}

/* ---- the tab bar: fixed to the window, the indicator placed by CSS ----- */
.tabbar {
  position: fixed;
  left: max(16px, calc(50% - 214px));
  right: max(16px, calc(50% - 214px));
  bottom: max(20px, var(--sab));
  view-transition-name: tabbar;
}
.tabbar > .tab {
  color: var(--muted);
}
.tabbar > .tab.on {
  color: var(--primary);
}
/* The demo measures the current tab with a script; four equal tabs need
 * none: a quarter of the bar, moved by whole widths. */
.tab-ind {
  left: 3px;
  width: calc((100% - 6px) / 4);
  view-transition-name: tab-ind;
}
.tabbar:has(> .tab:nth-of-type(2).on) .tab-ind {
  transform: translateX(100%);
}
.tabbar:has(> .tab:nth-of-type(3).on) .tab-ind {
  transform: translateX(200%);
}
.tabbar:has(> .tab:nth-of-type(4).on) .tab-ind {
  transform: translateX(300%);
}
.tabbar:not(:has(> .tab.on)) .tab-ind {
  opacity: 0;
}
/* A group page: the bar folded into its "+" at the end, as the demo's
 * on-gpage does; the page transition morphs one into the other. */
:root.on-gpage:not(.log-view) .tabbar {
  left: auto;
  width: 64px;
}
.tab-plus {
  width: 100%;
  min-height: 52px;
  position: relative;
}
:root.on-gpage:not(.log-view) .tab-plus {
  opacity: 1;
  pointer-events: auto;
}

/* ---- sheets: a <dialog>, so focus, Escape and the top layer are the
 * browser's; it slides up as the demo's does ---------------------------- */
dialog.demo-sheet {
  position: fixed;
  inset: auto 0 0;
  width: 100%;
  max-width: var(--column);
  max-height: 86dvh;
  margin: 0 auto;
  padding: 10px 20px calc(34px + var(--sab));
  overflow-y: auto;
  color: var(--text);
  transform: translateY(105%);
  transition:
    transform 0.32s cubic-bezier(0.2, 0.8, 0.2, 1),
    overlay 0.32s allow-discrete,
    display 0.32s allow-discrete;
}
dialog.demo-sheet[open] {
  transform: none;
}
@starting-style {
  dialog.demo-sheet[open] {
    transform: translateY(105%);
  }
}
dialog.demo-sheet::backdrop {
  background: rgba(0, 0, 0, 0.3);
  opacity: 0;
  transition:
    opacity 0.25s,
    overlay 0.32s allow-discrete,
    display 0.32s allow-discrete;
}
dialog.demo-sheet[open]::backdrop {
  opacity: 1;
}
@starting-style {
  dialog.demo-sheet[open]::backdrop {
    opacity: 0;
  }
}
.sheet-h {
  font-size: 20px;
}
.sheet-close {
  position: absolute;
  top: 10px;
  inset-inline-end: 12px;
}

/* ---- the view menu: a popover, placed under the header's end bubble ---- */
.view-pop[popover] {
  position: fixed;
  inset: auto;
  top: calc(var(--sat) + 58px);
  right: max(12px, calc(50% - var(--column) / 2 + 12px));
  margin: 0;
  /* Its own size, not the UA's fit-content: iOS Safari stretched a menu
   * placed by its top alone down to the screen's bottom, its rows spread
   * over the height (the operator, 2026-10-01: "the menu es extreamly tall
   * and full of empty space"). */
  width: auto;
  height: auto;
  max-height: calc(100dvh - var(--sat) - 72px);
  color: var(--text);
}
.view-pop:popover-open {
  display: grid;
  align-content: start;
  animation: demo-search-drop 0.22s cubic-bezier(0.3, 0.9, 0.25, 1) both;
  transform-origin: top right;
}
.view-pop a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 14px;
  color: var(--text);
  text-decoration: none;
}
.view-pop a:hover {
  background: var(--bubble);
}
.view-pop a:not([aria-current]) .ck {
  visibility: hidden;
}
.view-pop .ck {
  color: var(--primary);
  visibility: visible;
}

/* ---- the toast: under the header ------------------------------------- */
.demo-toast {
  position: fixed;
  top: calc(var(--sat) + 62px);
  /* The demo's toast is one line whatever it says; an email address in it
   * ran past the screen's edge on a phone (and off the toast, over the dev
   * banner). It wraps inside the screen instead, breaking a long address. */
  white-space: normal;
  width: max-content;
  max-width: calc(100vw - 32px);
  overflow-wrap: anywhere;
  text-align: center;
  /* Above the dev banner, which is drawn over the page (z-index 20). */
  z-index: 30;
}
/* Under the header wherever it is (ui.js measures it into --toast-top): the
 * dev banner above it is taller than the fixed place on a phone and hid the
 * toast under it. Without a header, or before the script, the fixed place. */
.demo-toast {
  top: var(--toast-top, calc(var(--sat) + 62px));
  /* Drawn beside the page, so a layer of its own while pages change, or the
   * backdrop's covers it (the dev banner's reason, 20260930): a message
   * arriving with a page shows on it rather than after it. */
  view-transition-name: toast;
}

/* A toast carrying a button (an Undo, 20260930-022) takes taps, and its
 * button sits beside the text. */
.demo-toast.on.has-action {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}
.demo-toast .pill {
  flex: none;
  min-height: 36px;
  padding-inline: 14px;
}
/* The Undo in the page, for a browser without the script. */
.undo-form {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 16px 12px;
}

/* What a settle sheet pays (20261001-001): the figure big in the viewer's
 * currency with the group's beneath, the whole of it a button; tapped, the
 * amount and the currency side by side. */
.pay-show {
  display: grid;
  justify-items: center;
  gap: 2px;
  width: 100%;
  padding: 8px 0 4px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.pay-main {
  font-size: 34px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.pay-orig {
  font-size: 14px;
  color: var(--muted);
}
.pay-hint {
  margin-top: 4px;
  font-size: 13px;
  color: var(--primary);
}
.pay-fields {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
}
.pay-fields[hidden] {
  display: none;
}
.pay-fields {
  flex-wrap: wrap;
}
.pay-was {
  flex-basis: 100%;
  font-size: 13px;
  color: var(--muted);
}
.pay-was[hidden] {
  display: none;
}
.pay-fields .pay-amount {
  flex: 1;
  min-width: 0;
}
.pay-fields .ae-cur {
  flex: none;
}

/* ---- a person's and a group's colour: the demo sets --h in a style
 * attribute, which the CSP refuses; the server names one of eight -------- */
.who-0 { --h: 25; }
.who-1 { --h: 145; }
.who-2 { --h: 255; }
.who-3 { --h: 310; }
.who-4 { --h: 70; }
.who-5 { --h: 195; }
.who-6 { --h: 350; }
.who-7 { --h: 45; }
/* Bars the demo sizes with style="width:…": the server names a step of 5 %. */
.w-0 { width: 0%; } .w-5 { width: 5%; } .w-10 { width: 10%; } .w-15 { width: 15%; }
.w-20 { width: 20%; } .w-25 { width: 25%; } .w-30 { width: 30%; } .w-35 { width: 35%; }
.w-40 { width: 40%; } .w-45 { width: 45%; } .w-50 { width: 50%; } .w-55 { width: 55%; }
.w-60 { width: 60%; } .w-65 { width: 65%; } .w-70 { width: 70%; } .w-75 { width: 75%; }
.w-80 { width: 80%; } .w-85 { width: 85%; } .w-90 { width: 90%; } .w-95 { width: 95%; }
.w-100 { width: 100%; }
.h-0 { height: 2%; } .h-5 { height: 5%; } .h-10 { height: 10%; } .h-15 { height: 15%; }
.h-20 { height: 20%; } .h-25 { height: 25%; } .h-30 { height: 30%; } .h-35 { height: 35%; }
.h-40 { height: 40%; } .h-45 { height: 45%; } .h-50 { height: 50%; } .h-55 { height: 55%; }
.h-60 { height: 60%; } .h-65 { height: 65%; } .h-70 { height: 70%; } .h-75 { height: 75%; }
.h-80 { height: 80%; } .h-85 { height: 85%; } .h-90 { height: 90%; } .h-95 { height: 95%; }
.h-100 { height: 100%; }

/* ---- forms: the demo's field (its new-expense sheet's .ae-in) for every
 * input, so allauth's and the operator's pages match the rest ----------- */
.field,
.plain-card input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="submit"]),
.plain-card select,
.plain-card textarea,
#content form input:is([type="text"], [type="password"], [type="email"], [type="number"], [type="tel"], [type="url"], [type="search"], :not([type])),
#content form select,
#content form textarea {
  width: 100%;
  min-height: 48px;
  padding: 0 14px;
  border: 0;
  border-radius: 14px;
  background: var(--track);
  color: var(--text);
  font: inherit;
  font-size: 17px;
  outline: none;
}
#content form textarea,
.plain-card textarea {
  padding: 10px 14px;
  min-height: 88px;
  resize: vertical;
}
:is(.field, #content form :is(input, select, textarea), .plain-card :is(input, select, textarea)):focus {
  box-shadow: 0 0 0 2px var(--primary);
}
.form-list {
  display: grid;
  gap: 14px;
  margin: 0 16px;
}
.form-list label,
.plain-card label,
.auth-form label {
  display: block;
  margin: 0 0 6px;
  font-size: 15px;
  font-weight: 600;
  color: var(--muted);
}
.helptext,
.form-help {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  color: var(--muted);
}
.errorlist {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  color: var(--danger);
  font-size: 15px;
}
.auth-form {
  display: grid;
  gap: 14px;
}
.auth-form p {
  margin: 0;
}
.auth-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 50px;
  padding: 0 16px;
  border: none;
  border-radius: 999px;
  background: var(--primary);
  color: var(--on-primary);
  font: inherit;
  font-size: 17px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.auth-button-secondary {
  background: var(--track);
  color: var(--text);
}
.auth-button-danger {
  background: var(--danger);
  color: var(--on-danger);
}
.auth-p {
  margin: 12px 0 0;
}

/* ---- signed out: one glass card on the backdrop ------------------------ */
.screen.plain {
  display: grid;
  place-items: center;
  padding: calc(24px + var(--sat)) 16px calc(24px + var(--sab));
}
.plain-card {
  width: min(440px, 100%);
}
.plain-card h1 {
  margin: 0 0 12px;
  font-size: 28px;
  letter-spacing: -0.01em;
}

/* ---- Django's messages, above the page's own content ------------------ */
.pagum-messages p {
  margin: 8px 16px;
  padding: 10px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--cell) 86%, transparent);
  font-size: 15px;
}
.plain-card .pagum-messages p {
  margin: 0 0 12px;
}
.pagum-message-error {
  color: var(--danger);
}

/* ---- the dev login's banner ------------------------------------------ */
#dev-login-banner {
  position: relative;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  padding: calc(4px + var(--sat)) 12px 4px;
  background: #8a5300;
  color: #ffffff;
  font-size: 13px;
}
#dev-login-banner form {
  margin-inline-start: auto;
}
#dev-login-banner button {
  border: 1px solid #ffffff;
  border-radius: 999px;
  padding: 2px 10px;
  background: none;
  color: #ffffff;
  font: inherit;
}
.dev-login .screen > .header {
  top: 0;
}

/* ---- page transitions (20260929-019): the demo's push, pop and fade,
 * across documents. motion.js names the direction; the header's parts
 * morph as the demo's morphIcons does --------------------------------- */
@view-transition {
  navigation: auto;
}
.screen > .header > h1 {
  view-transition-name: hd-title;
}
.screen > .header > .bubble:first-child {
  view-transition-name: hd-start;
}
.screen > .header > .bubble:last-child:not(:first-child) {
  view-transition-name: hd-end;
}
.screen > .header > .bubble:first-child > svg {
  view-transition-name: hd-start-icon;
}
.screen > .header > .bubble:last-child:not(:first-child) > svg {
  view-transition-name: hd-end-icon;
}
::view-transition-group(*) {
  animation-duration: 0.32s;
  animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* The page itself is a layer of its own, "screen", above the backdrop's.
 * Until 20260929 the page moved as part of the root while the backdrop was
 * named: named layers draw above the root, so for the length of every
 * transition the backdrop covered the page, which the operator saw as the
 * screen flashing black (the dark valley) between pages. Now the root
 * holds nothing but the canvas and swaps at once; the backdrop sits on it;
 * the page slides over the backdrop, as the demo's screens do. */
#main {
  view-transition-name: screen;
}
::view-transition-old(root),
::view-transition-new(root),
::view-transition-group(root) {
  animation: none;
}
::view-transition-old(root) {
  opacity: 0;
}
/* The group does not stretch from one page's height to the next's; its
 * images do the moving. The old page fades quickly, the new one arrives:
 * pushed in from the right, popped in from the left, faded between tabs. */
::view-transition-group(screen) {
  animation: none;
}
::view-transition-old(screen) {
  animation: pagum-leave 0.16s ease-out both;
}
::view-transition-new(screen) {
  animation: demo-fade 0.2s ease-out both;
}
html:active-view-transition-type(push)::view-transition-new(screen) {
  animation: demo-push 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
html:active-view-transition-type(pop)::view-transition-new(screen) {
  animation: demo-pop 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes pagum-leave {
  to {
    opacity: 0;
  }
}
/* The dev login's banner is a layer of its own too (2026-09-30: "still the
 * background flashes each time i change page"): named layers draw above the
 * root while a transition runs, so the backdrop covered the banner, and the
 * top of the screen went from orange to the pale sky and back on every page
 * change. Named, it stays drawn above the backdrop, still. */
#dev-login-banner {
  view-transition-name: dev-banner;
}
::view-transition-group(dev-banner),
::view-transition-old(dev-banner),
::view-transition-new(dev-banner) {
  animation: none;
}
::view-transition-old(dev-banner) {
  opacity: 0;
}
/* The backdrop does not move. */
::view-transition-group(alps),
::view-transition-old(alps),
::view-transition-new(alps) {
  animation: none;
}
::view-transition-old(alps) {
  opacity: 0;
}
/* The title: the old one goes, the new one rises in (demo-title-in). */
::view-transition-group(hd-title) {
  animation: none;
}
::view-transition-old(hd-title) {
  animation: none;
  opacity: 0;
}
::view-transition-new(hd-title) {
  animation: demo-title-in 0.3s ease-out both;
}
/* A bubble that appears pops in; one that goes shrinks away; one that stays
 * keeps its glass while its icon turns into the new one. */
::view-transition-new(hd-start):only-child,
::view-transition-new(hd-end):only-child {
  animation: demo-bubble-in 0.34s cubic-bezier(0.3, 1.4, 0.5, 1) both;
}
::view-transition-old(hd-start):only-child,
::view-transition-old(hd-end):only-child {
  animation: demo-bubble-out 0.28s ease-in both;
}
::view-transition-old(hd-start-icon),
::view-transition-old(hd-end-icon) {
  animation: demo-icon-out 0.26s ease-in both;
}
::view-transition-new(hd-start-icon),
::view-transition-new(hd-end-icon) {
  animation: demo-icon-in 0.4s cubic-bezier(0.3, 1.35, 0.5, 1) both;
}
/* The same icon on both pages (a back chevron to a back chevron) does not
 * turn: motion.js compares them, as the demo's morphIcons does. */
html:active-view-transition-type(same-start)::view-transition-old(hd-start-icon),
html:active-view-transition-type(same-end)::view-transition-old(hd-end-icon) {
  animation: none;
  opacity: 0;
}
html:active-view-transition-type(same-start)::view-transition-new(hd-start-icon),
html:active-view-transition-type(same-end)::view-transition-new(hd-end-icon) {
  animation: none;
}
/* The tab bar holds still; its indicator slides with the demo's overshoot,
 * and folding into a group page's "+" is the bar's own box morphing. */
::view-transition-group(tabbar) {
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.3, 0.9, 0.25, 1);
}
::view-transition-group(tab-ind) {
  animation-duration: 0.42s;
  animation-timing-function: cubic-bezier(0.3, 1.25, 0.4, 1);
}
::view-transition-old(tabbar):only-child {
  animation: pagum-tabbar-out 0.35s cubic-bezier(0.3, 0.9, 0.25, 1) both;
}
::view-transition-new(tabbar):only-child {
  animation: pagum-tabbar-in 0.35s cubic-bezier(0.3, 0.9, 0.25, 1) both;
}
@keyframes pagum-tabbar-out {
  to {
    transform: translateY(130px);
    opacity: 0;
  }
}
@keyframes pagum-tabbar-in {
  from {
    transform: translateY(130px);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
  dialog.demo-sheet,
  dialog.demo-sheet::backdrop,
  .view-pop:popover-open {
    transition: none;
    animation: none;
  }
}

/* ---- search in the header: the demo swaps the header's content for a
 * field and Cancel; here both are always in the page, and the class says
 * which shows, so it works from a plain link (?search) too ---------------- */
.header .search-form {
  display: contents;
}
.header:not(.searching) > .search-form {
  display: none;
}
.header.searching > :not(.search-form) {
  display: none;
}
.search-field input::-webkit-search-cancel-button {
  -webkit-appearance: none;
}
.header.searching .search-field .ionicon {
  flex: none;
  font-size: 18px;
}

/* ---- the Groups tab's balance: owed and owing shown apart (R-M8, R-M11),
 * each in its own colour, where the demo shows one net amount ----------- */
.g-bal .g-amt.plus,
.g-bal:not(.plus):not(.minus) .g-amt.plus {
  color: var(--plus);
}
.g-bal .g-amt.minus,
.g-bal:not(.plus):not(.minus) .g-amt.minus {
  color: var(--minus);
}
a.search-cancel {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

/* ---- a person's face (_avatar.html): the demo's .avatars circle, shared
 * by every area; the initial on a colour, or their photo -------------------- */
.face {
  /* No default --h here: this rule comes after .who-N and would win over it
   * (every face drew the same blue); the default is the var()'s fallback. */
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  overflow: hidden;
  border-radius: 50%;
  background: oklch(0.52 0.12 var(--h, 255));
  color: #ffffff;
  font-size: 13px;
  font-weight: 700;
}
.face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.face.avatar-big {
  width: 84px;
  height: 84px;
  font-size: 34px;
}

/* ======================================================================
 * Per-area sections (the conversion of 20260929-018 was done area by area;
 * each area's rules stay inside its own section, so they read together).
 * ====================================================================== */

/* ==== area: home (Home tab, Arrange) ==== */
/* The demo's bars are spans sized by style=; here the balance and people
 * bars are SVG rectangles sized by their width attribute (exact, and legal
 * under the CSP), drawn with the demo's .split and .track. */
svg.split,
svg.track {
  width: 100%;
}
svg.split:not(.owing) {
  background: var(--track);
}
.split .bar-owed,
.track .bar-owed {
  fill: var(--plus);
}
.track .bar-owe {
  fill: var(--minus);
}
.w-currency + .w-currency {
  margin-top: 12px;
}
.widget .net {
  overflow-wrap: anywhere;
}
.w-row .label bdi {
  overflow-wrap: anywhere;
}
.w-row .chev {
  flex: none;
  color: #c4c4c7;
  font-size: 16px;
}
.w-row form {
  flex: none;
}
ol.months {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
}
ol.months li {
  flex: 1;
  min-width: 0;
}
.hw-sub {
  margin: 12px 0 0;
  font-weight: 600;
}
/* A month's amount: the columns are decoration, their numbers are read. */
.hw-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.arrange-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 12px;
  color: var(--primary);
  text-decoration: none;
}
/* Arrange home: the demo's arrows, at the app's 44 px touch size (the demo
 * draws them 34 px). */
.ar-row .ar-mv {
  flex: none;
  width: 44px;
  height: 44px;
}
.ar-row .ar-mv .ionicon {
  font-size: 18px;
}
.ar-row .ar-mv:disabled {
  opacity: 0.3;
}

/* ==== end of area: home ==== */


/* ==== area: group overview (a group's Overview, settle sheet, view menu) ==== */

/* The Overview's rows open a person's sheet: buttons drawn as the demo's
 * w-row (settle-row), the whole row the target. */
button.w-row {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
}
.settle-row .money {
  flex: none;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.settle-row .avatars span {
  width: 28px;
  height: 28px;
  font-size: 12px;
}
/* The demo's balance bar sizes its two parts with style="flex-grow:…",
 * which the CSP refuses; overview.py names a step of 5 %. */
.fg-5 { flex-grow: 5; } .fg-10 { flex-grow: 10; } .fg-15 { flex-grow: 15; } .fg-20 { flex-grow: 20; }
.fg-25 { flex-grow: 25; } .fg-30 { flex-grow: 30; } .fg-35 { flex-grow: 35; } .fg-40 { flex-grow: 40; }
.fg-45 { flex-grow: 45; } .fg-50 { flex-grow: 50; } .fg-55 { flex-grow: 55; } .fg-60 { flex-grow: 60; }
.fg-65 { flex-grow: 65; } .fg-70 { flex-grow: 70; } .fg-75 { flex-grow: 75; } .fg-80 { flex-grow: 80; }
.fg-85 { flex-grow: 85; } .fg-90 { flex-grow: 90; } .fg-95 { flex-grow: 95; } .fg-100 { flex-grow: 100; }
.gp-widgets {
  padding-top: 8px;
  padding-bottom: 0;
}
.bal-v.money {
  font-variant-numeric: tabular-nums;
}
/* The view menu opens under its own bubble, wherever the header is (a
 * banner above it moves it down); app.css's fixed place is the fallback
 * where anchors are not supported. */
@supports (anchor-name: --a) {
  #open-view {
    anchor-name: --view-menu;
  }
  #view-menu[popover] {
    position-anchor: --view-menu;
    top: calc(anchor(bottom) + 8px);
    right: anchor(right);
  }
  /* The Groups tab's + menu the same way (it opened at the fixed place,
   * over the dev banner, not under its bubble). */
  #open-groups-add {
    anchor-name: --groups-add;
  }
  #groups-add[popover] {
    position-anchor: --groups-add;
    top: calc(anchor(bottom) + 8px);
    right: anchor(right);
  }
}
/* A title that links: the header's h1 holds the group's name and chevron. */
.header h1 .t-link {
  color: inherit;
  text-decoration: none;
}
.header h1 .t-chev .ionicon {
  font-size: 12px;
}
/* A person's notes (people/_methods.html): the demo's pm-list, on a sheet or
 * the How to pay page; the rest of a person's notes behind a <details>. */
/* On a page (How to pay), the list sits in the page's column like any
 * other; the demo's pm-list and pm-head are drawn for its sheet. */
#content > .pm-head {
  margin: 20px 16px 8px;
  padding-inline: 16px;
}
#payment-methods > .pm-list {
  margin: 0 16px 12px;
}
.pm-list .pm-row {
  align-items: flex-start;
}
.pm-list .pm-row .pm-copy {
  align-self: center;
}
.pay-host {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  color: var(--muted);
}
.demo-sheet .pm-head,
.demo-sheet .section-head {
  margin-inline: 0;
  padding-inline: 4px;
}
.demo-sheet .list {
  margin-inline: 0;
}
.pm-more-d > summary {
  list-style: none;
  cursor: pointer;
}
.pm-more-d > summary::-webkit-details-marker {
  display: none;
}
.pm-more-d[open] > .pm-row {
  animation: demo-fade 0.2s ease-out;
}
.pm-row + .pm-more-d > summary::before,
.pm-more-d > .pm-row::before {
  content: "";
  position: absolute;
  top: 0;
  left: 16px;
  right: 0;
  border-top: 0.5px solid var(--separator);
}
/* The settle forms, on the pair sheets and the Log's settle sheet. */
.settle-form {
  display: grid;
  gap: 6px;
  margin-top: 16px;
}
.settle-label {
  font-size: 15px;
  font-weight: 600;
  color: var(--muted);
}
.settle-list .row.stacked .settle-form {
  margin-top: 0;
}
.settle-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.settle-list + .section-head,
.settle-list + .settle-list {
  margin-top: 12px;
}
@media (prefers-reduced-motion: reduce) {
  .pm-more-d[open] > .pm-row {
    animation: none;
  }
}
/* ==== end of area: group overview ==== */


/* ==== area: group log (the chat log, its entries, the composer) ==== */
/* The Log (groups/chat_log.html), as the demo's renderLog draws it: the
 * .chat column of day pills, quiet .sys lines and .msg bubbles. What the demo
 * lacks and the app has: receipts under a bubble, a pending "I paid" with its
 * buttons, the members line, the balance strip, and the new-expense sheet. */
.balance-strip {
  margin: 4px 16px 8px;
  padding: 6px 6px 6px 16px;
  border-radius: 26px;
}
.balance-strip .balance-strip-text {
  font-size: 15px;
}
.balance-strip .bubble.text {
  flex: none;
  font-weight: 600;
}
/* Words on the backdrop get a ground of their own, as the day pills have. */
.log-members,
.log-solo {
  margin: 4px 16px 0;
  padding: 8px 14px;
  border-radius: 18px;
  background: color-mix(in srgb, var(--cell) 86%, transparent);
  color: var(--text);
}
.log-solo {
  margin-top: 12px;
  color: var(--muted);
  text-align: center;
}
.log {
  padding: 0;
  gap: 0;
}
#log-entries.chat {
  padding: 8px 12px 16px;
}
.log-older {
  align-self: center;
}
.log-older .pill {
  min-height: 36px;
  font-size: 14px;
}
/* A bubble with what sits under it (receipts): one column beside the face. */
.msg-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
}
.msg.me .msg-col {
  align-items: flex-end;
}
a.bubble-c {
  color: var(--text);
  text-decoration: none;
}
a.bubble-c:hover {
  filter: brightness(0.98);
}
.bubble-c {
  min-width: 0;
  overflow-wrap: anywhere;
}
a.reply {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  text-decoration: none;
}
.reply .ionicon {
  flex: none;
  margin-top: 2px;
}
.bubble-c .log-who {
  font-size: 12px;
}
.bubble-c .log-people {
  gap: 2px 6px;
}
.bubble-c .log-person:not(:last-child)::after {
  content: ",";
}
.log-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.log-attachment {
  padding: 0 12px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cell) 86%, transparent);
  font-size: 14px;
  text-decoration: none;
}
.log-attachment .ionicon {
  flex: none;
  font-size: 16px;
}
/* The demo's quiet lines: its .sys pill, with an icon and an inline action. */
.chat .log-line.sys,
.chat .log-request {
  max-width: 92%;
  min-width: 0;
  /* A pasted link as an expense's description is one unbroken word. */
  overflow-wrap: anywhere;
}
.chat .log-line .ionicon {
  font-size: 14px;
  vertical-align: -2px;
}
.log-inline-form {
  display: inline;
}
button.log-inline {
  min-height: 32px;
  padding: 0 6px;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.chat .log-line a {
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
}
/* A pending "I paid": a glass card across the column, its two answers side
 * by side. */
.chat .log-request {
  align-self: stretch;
  margin: 4px 0;
  padding: 12px 14px;
  border-radius: 20px;
  font-size: 15px;
}
.log-request > p {
  margin: 0 0 4px;
}
.log-request .log-actions {
  margin-top: 10px;
}
.log-actions-one {
  grid-template-columns: 1fr;
}
.log-actions form,
.log-actions .pill {
  width: 100%;
}
/* A reversal's link lands on the line it reverses: the demo's flash. */
.chat .msg:target .bubble-c,
.chat .log-line:target,
.chat .log-request:target {
  animation: demo-flash 1.2s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .chat :target,
  .chat :target .bubble-c {
    animation: none;
  }
}

/* The composer bar: the demo's tab bar turned composer on the Log, its "+"
 * for a new expense at the start. The bar holds no tabs to give it height. */
.composer-bar {
  min-height: 58px;
}
.composer-bar .composer {
  padding-inline-start: 3px;
}
.c-plus {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--bubble);
  color: var(--primary);
  font-size: 26px;
  cursor: pointer;
}
.composer-bar .c-input {
  color: var(--text);
}
.composer-bar .c-input::placeholder {
  color: var(--muted);
}

/* The new-expense sheet: the demo's add-expense fields. People are real
 * checkboxes inside the demo's chips; the chip shows the state. */
.compose-sheet .small-label {
  font-weight: 600;
  color: var(--muted);
}
.compose-sheet .ae-form {
  margin-top: 4px;
}
.compose-sheet .ae-split,
.compose-sheet .composer-files {
  margin-top: 14px;
}
.compose-sheet .ae-in {
  border: 0;
}
.ae-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
}
.ae-chip input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.ae-chip:has(input:checked) {
  border-color: var(--primary);
  color: var(--primary);
  background: rgba(var(--primary-rgb), 0.1);
  font-weight: 600;
}
.ae-chip:has(input:focus-visible) {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.composer-files input[type="file"] {
  width: 100%;
  color: var(--muted);
  font: inherit;
  font-size: 15px;
}
.composer-files input[type="file"]::file-selector-button {
  min-height: 44px;
  margin-inline-end: 10px;
  padding: 0 16px;
  border: 0;
  border-radius: 999px;
  background: var(--track);
  color: var(--text);
  font: inherit;
  font-weight: 600;
}


/* ==== end of area: group log ==== */


/* ==== area: group admin (settings, Add people, create, join) ==== */

/* The demo's "group" and "addp" screens (settings, Add people), and the
 * pages it draws none of (new group, join, settle and remove) in the same
 * grouped lists. Where a rule differs from the demo it says why. */

/* The hero: tile (the photo picker, for an organizer) over the name. The
 * name's field sits in a form, and #content's field rule (app.css) would
 * draw it as a boxed input: the demo's inline look is restated here. */
#content .gs-hero .gs-name {
  width: 100%;
  min-height: 0;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: 14px;
  background: none;
  color: var(--text);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-align: center;
  box-shadow: none;
}
#content .gs-hero .gs-name:hover {
  background: var(--bubble);
}
#content .gs-hero .gs-name:focus {
  background: var(--cell);
  border-color: var(--primary);
}
.gs-name-read {
  margin: 0;
  font-size: 28px;
  font-weight: 700;
  text-align: center;
}
.gs-rename-form {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
}
/* Without the script the Rename button is always there (it is how a name is
 * sent); with it, only once the name differs, as the demo does. */
.gs-hero:not(.js) .gs-rename {
  display: flex;
}
.gs-hero:not(.js) .gs-hint {
  display: none;
}
.gs-photo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
#content .photo-file {
  position: absolute;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}
.gs-photo:not(:has(.photo-file:valid)) .gs-photo-save,
.list:not(:has(.photo-file:valid)) .photo-save {
  display: none;
}
.ph-pick:has(:focus-visible) {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
}
.gs-photo-remove {
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  background: none;
  color: var(--primary);
  font: inherit;
  font-size: 15px;
  cursor: pointer;
}

/* The participants' head: the demo's bubbles are 32 px; a tap target here is
 * 44 (ui-check's strict targets), so the button is 44 with the demo's 32 px
 * disc drawn inside it. */
.gs-head .bubble {
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  background: radial-gradient(circle, var(--bubble) 15.5px, transparent 16px);
}
.gs-head .bubble[aria-pressed="true"] {
  background: radial-gradient(circle, color-mix(in srgb, var(--primary) 16%, transparent) 15.5px, transparent 16px);
}
.gs-search {
  display: flex;
}
.gs-search input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  outline: none;
  color: var(--text);
  font: inherit;
}
.gs-person {
  gap: 12px;
}
.list.gs-add {
  margin-top: 12px;
}
.list.gs-danger {
  margin-top: 20px;
}

/* A checkbox or radio drawn as the demo's .check: the real control stays in
 * the form, out of sight, and the box beside it shows its state. */
.split-box {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
}
label.row {
  cursor: pointer;
}
.split-box:not(:checked) + .check {
  background: transparent;
  border: 2px solid var(--muted);
}
.split-box:not(:checked) + .check .ionicon {
  visibility: hidden;
}
.split-box:focus-visible + .check {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.gs-choice {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}
.gs-choice legend {
  display: block;
  padding-inline: 16px;
}

/* Rows that hold a field: its label, and the value typed at the end. */
#content .gs-field .gs-input {
  flex: 2;
  width: auto;
  min-width: 0;
  min-height: 44px;
  padding: 0;
  background: none;
  text-align: end;
  box-shadow: none;
}
#content .gs-field .gs-input:focus {
  box-shadow: none;
}
.gs-field:focus-within {
  box-shadow: inset 0 0 0 2px var(--primary);
}
#content .gs-field .gs-code {
  text-align: center;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.gs-error {
  margin: 8px 16px;
  color: var(--danger);
}

/* The invite link: the demo's switch, inside a 44 px tap target. */
.switch-hit {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 51px;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.ap-link .ap-code code,
.ap-link .ap-url code {
  font: inherit;
}
/* The demo's action pills here are 36 px; 44 keeps the tap targets. */
.ap-acts .pill {
  min-height: 44px;
}
.ap-request {
  flex-wrap: wrap;
  gap: 8px 12px;
}

/* A sheet's forms are its actions' own boxes. */
.sheet-actions > form {
  display: grid;
}
.gs-confirm-label {
  display: block;
  margin: 12px 0 6px;
  font-size: 15px;
  font-weight: 600;
  color: var(--muted);
}
.cur-list .row {
  width: 100%;
}

/* Join and settle-and-remove: the demo's heading block. */
.hero .hero-amount {
  margin: 4px 0;
  font-size: 28px;
  overflow-wrap: anywhere;
}
.gs-join .g-tile {
  margin: 8px auto;
}

/* ==== end of area: group admin ==== */


/* ==== area: entries (expense, new expense, settle, Activity, person) ==== */

/* The expense page (entries/detail.html): the demo's renderExpense. */
.ex-widgets {
  padding-top: 8px;
  padding-bottom: 0;
}
/* One column no wider than the screen, as the demo's home has: an auto
 * track grows to its widest line (two pills side by side), past the phone. */
.ex-widgets,
.ex-form .widgets,
.pp-hero + .widgets {
  grid-template-columns: minmax(0, 1fr);
}
.gp-actions {
  flex-wrap: wrap;
}
.expense-hero h2 {
  color: var(--text);
  font-size: 17px;
}
.row .value.amt {
  flex: none;
  margin-inline-start: auto;
  font-variant-numeric: tabular-nums;
}
/* What people type (a title, a reason, a comment, a file's name) can be one
 * unbroken run, a link say: it wraps inside its block rather than widening
 * the page. */
.expense-hero h2,
.contest-card p,
.c-text,
.an-body,
#split .label,
#receipts .label,
#history .label,
#history .sub {
  overflow-wrap: anywhere;
}
/* A face in a row, a comment or a notice: the demo's 32 px circle; a face
 * with no palette colour of its own (a comment's author) takes the blue. */
.row .avatars,
.c-row .avatars {
  padding: 0;
}
.row .avatars span,
.c-row .avatars span {
  width: 32px;
  height: 32px;
  margin: 0;
  border: 0;
  font-size: 13px;
}
.row .avatars span:not([class*="who-"]),
.c-row .avatars span:not([class*="who-"]) {
  --h: 255;
}
.c-who {
  display: block;
  font-weight: 600;
}
.c-who time {
  color: var(--muted);
  font-weight: 400;
}
.c-text {
  white-space: normal;
}
.c-add-form {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
#content .c-add-form textarea.c-input {
  flex: 1;
  width: auto;
  min-height: 44px;
  padding: 11px 14px;
  border: 1px solid var(--separator);
  border-radius: 22px;
  background: var(--page);
  font-size: 15px;
  line-height: 1.3;
  resize: none;
}
a.doc-name {
  display: flex;
  flex-direction: column;
  color: var(--text);
  text-decoration: none;
}
.row.doc form,
.row .st-acts form {
  flex: none;
}
.ex-upload,
.ex-file {
  flex-wrap: wrap;
  gap: 10px;
}
.ex-file {
  display: flex;
  flex: 1;
  align-items: center;
  min-width: 0;
}
.ex-file input[type="file"] {
  flex: 1 1 180px;
  min-width: 0;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
}
.ex-acts {
  padding-bottom: 0;
}
.ex-manage {
  margin-top: 20px;
}
.ex-delete {
  padding: 0;
  gap: 0;
}
.ex-warning {
  margin: 0;
  padding: 10px 16px 0;
  color: var(--danger);
  font-size: 15px;
}
.ex-choice {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 10px 16px;
}
.ex-choice input {
  flex: none;
  width: 22px;
  height: 22px;
  accent-color: var(--primary);
}
.gp-actions form {
  display: flex;
  flex: 1;
}
.gp-actions form .pill {
  flex: 1;
}

/* A new expense and an edit (entries/form.html): the demo's new-expense
 * sheet, on the page. */
.ex-error {
  margin: 8px 16px;
  padding: 10px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--cell) 86%, transparent);
  color: var(--danger);
}
.ex-cleared {
  margin-bottom: 12px;
}
#content .ae-in.big {
  font-size: 24px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ae-cur {
  cursor: default;
}
.ex-people {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.ex-people legend {
  margin-bottom: 6px;
  padding: 0;
}
/* A participant is a chip with a real checkbox in it: the chip is its label,
 * as tall as a thumb, and draws pressed while the box is ticked. */
.ae-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  font-size: 15px;
}
.ae-chip input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
.ae-chip:has(:checked) {
  border-color: var(--primary);
  color: var(--primary);
  background: rgba(var(--primary-rgb), 0.1);
  font-weight: 600;
}
.ae-chip:has(:focus-visible) {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.split-preview-rows {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 15px;
}
.split-preview-rows li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}
.split-preview-amount {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.split-preview-error {
  color: var(--danger);
}

/* Settle up (entries/settle.html): a block per pair. */
.st-pair {
  margin-bottom: 16px;
}
.st-pair:first-child {
  margin-top: 8px;
}
.st-form {
  flex-wrap: wrap;
  gap: 10px;
}
.st-amount {
  display: grid;
  flex: 1 1 140px;
  gap: 4px;
}
.st-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.st-ok {
  color: var(--plus);
  font-size: 22px;
}

/* Activity (entries/activity.html) and a person (entries/person.html). */
.an-body .sub {
  display: block;
  font-size: 13px;
  color: var(--muted);
}
.an-acts form {
  display: flex;
}
.an-icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--track);
  color: var(--primary);
  font-size: 18px;
}
.person-row .g-line {
  display: block;
  font-size: 15px;
}
.person-row .g-word {
  flex: none;
  font-size: 12px;
  color: var(--muted);
}
.act-lead {
  margin-top: 16px;
}
/* ---- what stands between you and each person (the operator, 2026-09-29):
 * the face, the name, one figure in big type; the originals, when it is
 * converted, small beneath it. The same row on a group's Overview. ------ */
.ap-head {
  display: flex;
  align-items: center;
  gap: 4px;
}
.ap-sort {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin-block: -12px;
  color: var(--primary);
}
.ap-sort svg {
  width: 18px;
  height: 18px;
}
.bal-row .label .g-name,
.settle-row .label {
  font-size: 18px;
}
.bal-fig {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  text-align: end;
}
.bal-big,
.settle-row .bal-big {
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* In the Log, money coming to the viewer is green and money going out red
 * (the operator, 20260929-069); a line that is not theirs keeps its colour. */
[data-flow="in"] .money,
[data-flow="in"] .xp-amt {
  color: var(--plus);
}
[data-flow="out"] .money,
[data-flow="out"] .xp-amt {
  color: var(--minus);
}
/* An open expense as an Activity row (20260929-068): who paid, the title to
 * the right of the name fading out under the amount, the date beneath. */
.ex-row .label {
  min-width: 0;
}
.ex-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.ex-top .g-name {
  flex: none;
  max-width: 55%;
}
.ex-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  /* The text colour, not muted: muted on the glass over a light backdrop
   * read 4.43:1 (ui-check, 2026-09-30); the name is set apart by weight. */
  color: var(--text);
  font-weight: 400;
  -webkit-mask-image: linear-gradient(to right, #000 75%, transparent);
  mask-image: linear-gradient(to right, #000 75%, transparent);
}
/* The Overview's people, each a glass bubble of its own, in a list
 * (20260929-067). The glass is the widgets' own. */
.people-bubbles {
  display: grid;
  gap: 10px;
  /* The page's gutter, as its lists have (Activity's ran edge to edge on a
   * phone, the operator, 2026-09-30); inside the widgets' column, theirs. */
  margin-inline: 16px;
}
.widgets > .people-bubbles {
  margin-inline: 0;
}
/* A link on Activity, a button in a group: one look. As a button it is also
 * a w-row, whose button rule (no padding, border or glass) came later in
 * weight and drew the group's thin and bare (2026-09-30). */
:is(a, button).person-bubble {
  padding: 12px 16px;
  border-radius: 22px;
  border: 1px solid var(--glass-edge);
  background: var(--glass);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  box-shadow: inset 0 1px 0 var(--glass-light), 0 10px 30px var(--glass-shadow);
}
.bal-orig,
.amt-orig,
.xp-orig {
  font-size: 12px;
  color: var(--muted);
}
.amt-orig {
  margin-inline-start: 4px;
}
.exp-row .tag {
  flex: none;
}

/* ==== end of area: entries ==== */


/* ==== area: account (Profile, credit notes, notifications, auth, operator) ==== */

/* Section heads: a heading and, beside it, the section's Save or Edit (the
 * demo's .link), a real button or link with a thumb-sized target. */
.section-head h2 {
  margin: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.section-head .link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-block: -12px;
  padding: 0 4px;
  border: 0;
  background: none;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
}
.list-gap {
  margin-top: 20px;
}
.list-gap:last-of-type {
  margin-bottom: 24px;
}
.list-top {
  margin-top: 8px;
}
.intro.alert {
  color: var(--danger);
}

/* A field in a row, as the demo's profile draws one: the label, then the
 * value typed in place at the row's end. */
#content .field-row input.value {
  flex: 1;
  width: auto;
  min-width: 0;
  min-height: 24px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: end;
  box-shadow: none;
  outline: none;
}
#content .field-row input.value::placeholder {
  color: var(--muted);
}
.field-row {
  cursor: text;
}
.field-row:focus-within {
  box-shadow: inset 0 0 0 2px var(--primary);
}
.list .row:first-child.field-row:focus-within {
  border-radius: 10px 10px 0 0;
}

/* The App section's choosers (theme.js sets aria-pressed). */
.row > .label > .chooser-label {
  margin: 0 0 8px;
}
.pal-glacier-dot { background: linear-gradient(oklch(0.8 0.035 240), oklch(0.45 0.045 240)); }
.pal-pine-dot { background: linear-gradient(oklch(0.8 0.035 175), oklch(0.45 0.045 175)); }
.pal-dusk-dot { background: linear-gradient(oklch(0.8 0.028 300), oklch(0.45 0.036 300)); }
.pal-larch-dot { background: linear-gradient(oklch(0.8 0.028 70), oklch(0.45 0.036 70)); }
.pal-slate-dot { background: linear-gradient(oklch(0.8 0.012 255), oklch(0.45 0.016 255)); }

/* Credit notes (the demo's credit screen): the forms are only for their
 * controls (form=), so they take no place in the block's grid. */
.note-block > form {
  display: contents;
}
.note-body {
  min-width: 0;
}
.note-block.editing {
  grid-template-columns: minmax(0, 1fr);
}
.note-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  grid-column: 1 / -1;
}
.note-moves {
  display: flex;
  gap: 8px;
}
.sn-acts .note-save {
  margin-top: 0;
}
/* The editor's own border is its focus mark (the demo's .sn-ed). */
.sn-ta:focus-visible {
  outline: none;
}
.icon-button {
  cursor: pointer;
}
.icon-button[disabled] {
  opacity: 0.35;
  cursor: default;
}
.icon-button .ionicon {
  font-size: 20px;
}
.sn-view .pm-note {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.sn-view .pm-k {
  margin-top: 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
}
.sn-view .pm-k:first-child,
.sn-view .pm-pref + .pm-k {
  margin-top: 0;
}
.sn-view .pm-val {
  font-family: inherit;
  font-size: inherit;
  white-space: pre-wrap;
  word-break: break-word;
}
.sn-view .pm-pref {
  margin-bottom: 4px;
}
.intro.notes-updated {
  margin: -8px 16px 16px;
  font-size: 13px;
}
.pay-host {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--muted);
}
.note-unreadable {
  margin: 0;
  color: var(--muted);
}
.note-label label {
  margin: 0;
  font: inherit;
  color: inherit;
}
/* Without notes.js every note is its form: the text area drawn plainly, and
 * every control shown (the demo hides them outside an edit). */
.notes:not(.js) .sn-mirror {
  display: none;
}
.notes:not(.js) .sn-ta {
  position: static;
  min-height: 88px;
  color: var(--text);
}
.notes:not(.js) .note-block .note-moves,
.notes:not(.js) .note-block .sn-acts {
  display: flex;
}

/* Notification settings: an event, then its two switches (the demo's notif). */
fieldset.event {
  min-width: 0;
  margin: 0 0 12px;
  padding: 0;
  border: 0;
}
legend.event-name {
  padding: 0;
}
input.switch {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  cursor: pointer;
  transition: background-color 0.2s;
}
input.switch::after {
  transition: left 0.2s;
}
input.switch:checked {
  background: var(--primary);
  border-color: var(--primary);
}
input.switch:checked::after {
  left: 22px;
}
@media (prefers-reduced-motion: reduce) {
  input.switch,
  input.switch::after {
    transition: none;
  }
}
.push-actions {
  margin: 0;
}

/* The operator's pages. */
.account-username {
  color: var(--muted);
}
.account-actions {
  margin-top: 6px;
}
.row form {
  margin: 0;
}
.list-fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
legend.section-head {
  display: block;
}
.choice-row {
  cursor: pointer;
}
.choice-row input[type="radio"] {
  flex: none;
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--primary);
}
.temporary-password code {
  font-size: 28px;
  letter-spacing: 0.06em;
  word-break: break-all;
}

/* allauth's pages, signed in: their content one glass card under the header,
 * as the signed-out card is on the backdrop. */
:root.auth-page #content {
  margin: 8px 16px 0;
  padding: 16px;
  border: 1px solid var(--glass-edge);
  border-radius: 24px;
  background: var(--glass);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  box-shadow: inset 0 1px 0 var(--glass-light), 0 10px 30px var(--glass-shadow);
}
:root.auth-page #content h1,
.plain-card h1 {
  margin: 0 0 12px;
  font-size: 24px;
  letter-spacing: -0.01em;
}
:root.auth-page #content h2 {
  margin: 20px 0 8px;
  font-size: 17px;
}
:root.auth-page #content table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}
:root.auth-page #content :is(th, td) {
  padding: 8px 4px;
  border-bottom: 0.5px solid var(--separator);
  text-align: start;
}
.devlogin-accounts {
  display: grid;
  gap: 8px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}
/* ==== end of area: account ==== */


/* ======================================================================
 * What the first full browser check of the converted app found
 * (20260929-018). The demo is drawn for one phone and was never measured:
 * where its values fail WCAG AA on the backdrop, or its controls are under
 * the 44 px the app holds every tap target to, the fix is here, named.
 * ====================================================================== */

/* Text straight on the backdrop. The demo's muted grey (#5f6368) and
 * danger red (#c5000f) read at 3.9:1 on the light valley's pale blue, and
 * its blue (#0054e9) at 4.1:1; each is the same hue, darker, in light only.
 * Fills (pills, the tab indicator) keep the demo's blue. */
.light {
  --muted: #4a4f55;
  --danger: #a8000c;
  --primary-text: #0047c4;
}
.dark {
  --primary-text: #8ab4f8;
}
.search-cancel,
.section-head .link,
.arrange a,
.list-foot a,
.tabbar > .tab.on {
  color: var(--primary-text);
}
/* The Log's times: the demo fades them further (opacity .85) on top of the
 * muted grey, which took them under 4.5:1. */
.chat .sys .time,
.bubble-c .time {
  opacity: 1;
}
/* The toast sits over whatever is at the top, the dev banner's orange
 * included: glass there let 3.3:1 through. Nearly opaque, like the sheets. */
.demo-toast {
  background: color-mix(in srgb, var(--cell) 94%, transparent);
}

/* ---- 44 px targets ---------------------------------------------------- */
.an-acts .pill,
.pm-copy,
.pm-list .pm-row .pm-copy,
.ap-acts .pill,
.demo-act .pill {
  min-height: 44px;
}
.section-head .link,
.header h1 .t-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
  justify-content: center;
}
.log-attachment,
.row a.doc-link,
a.row.doc {
  min-height: 44px;
}
#dev-login-banner button {
  min-height: 44px;
  padding: 0 14px;
}
/* A link inside a sentence on the signed-out card (sign up, Forgot your
 * password?, Back to sign in) is still a target. */
.plain-card a:not(.auth-button),
#content .auth-p a,
#content .helptext a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.plain-card label:has(> input[type="radio"], > input[type="checkbox"]),
#content label:has(> input[type="radio"]) {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
}
/* One switch in the whole app (the operator, 2026-09-29: "use the same
 * switch on the entire app"): the demo's .switch, a 51 x 31 track and its
 * knob, drawn by a span inside a 44 px button.switch-hit (Arrange home, the
 * invite link) or by a checkbox itself (notification settings, a form
 * field); both slide the knob the same way. */
.switch,
input.switch {
  transition: background-color 0.2s, border-color 0.2s;
}
.switch::after,
input.switch::after {
  transition: left 0.2s;
}
@media (prefers-reduced-motion: reduce) {
  .switch,
  .switch::after {
    transition: none;
  }
}

/* ---- nothing wider than the screen ------------------------------------ */
/* A long name (a pasted address as an expense's title) in a one-line row:
 * the row clips it with an ellipsis, and so does the name's own <bdi>, so
 * its box is the row's and not the whole text's. */
/* Right-to-left text starts at the left like the rest (the operator,
 * 20260929-065: "allign arabic chars to the left side too"): a name that is
 * its own block, or a field someone types it into, would otherwise begin at
 * the right. Still isolated by <bdi>, so it reads in its own order. */
bdi,
input,
textarea {
  text-align: left;
}
.g-name > bdi,
.t-name > bdi,
.exp-row .label bdi {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A file field hidden behind its picker is clipped to nothing, not only
 * made transparent: its own button text otherwise spills out of its 1 px. */
#content .photo-file {
  overflow: hidden;
  clip-path: inset(50%);
}
.ex-file .label {
  flex: none;
}
/* A long label wraps inside a full-width button. */
.pill.wide,
.auth-button {
  white-space: normal;
  text-align: center;
}
/* allauth's session list is a table: it fits the card, and long cells wrap. */
#content table,
.plain-card table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}
#content :is(td, th),
.plain-card :is(td, th) {
  overflow-wrap: anywhere;
  padding: 6px 4px;
  text-align: start;
  vertical-align: top;
}

/* ---- the second pass: text on the backdrop's darker lower half ---------
 * The valley darkens towards the foot of the screen (the demo's lower
 * ridges): a muted grey that passes near the sky reads 2.95:1 low down, and
 * no grey can pass on both. Text that stands straight on the backdrop
 * (section heads, list notes, intros, hints) takes a near-black (light) or
 * near-white (dark) of the same hue; text inside cells and glass keeps the
 * demo's muted grey, which passes there. Dark's danger red and link blue
 * are lightened for the same reason. */
.light {
  --on-backdrop: #25282c;
}
.dark {
  --on-backdrop: #d3d8de;
  --danger: #ff6b7d;
  --primary-text: #a8c7fa;
}
a {
  color: var(--primary-text);
}
.section-head,
.section-head h2,
.list-foot,
.intro,
.hello,
.gs-hint,
.cur-now,
#no-hits,
#content .muted:not(:is(.list, .widget, dialog, .lg, .plain-card, .chat) .muted) {
  color: var(--on-backdrop);
}
.reply,
.reply span {
  color: var(--text);
}
.row.doc > a.doc-name {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 44px;
}
label:has(input[type="radio"]) {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 10px;
}
#content .photo-file {
  inset: 0;
}

/* ---- the third pass ----------------------------------------------------
 * The current tab's label sits on its own indicator (primary at 14 % over
 * glass): the link blue reads 4.2:1 there, so the current tab takes a
 * deeper blue. A picked chip and a warning tag carry their colour on a
 * tint of it; both take the text shade of that colour. Red text standing
 * on the backdrop (an operator's warning) takes a lighter red in dark. */
.light .tabbar > .tab.on {
  color: #003ea8;
}
.ae-chip:has(input:checked),
.ae-chip:has(input:checked) bdi,
.ae-chip[aria-pressed="true"] {
  color: var(--primary-text);
}
.dark .tag.warn {
  color: #ffa3ad;
}
.dark .intro.alert,
.dark #content > .alert {
  color: #ffa3ad;
}
:is(input[type="radio"], input[type="checkbox"]) + label[for] {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
/* The organizer's photo picker: the demo's camera badge overhangs its tile
 * by 2 px, which made the tile's box wider than its content; flush here. */
.gs-photo .ph-badge {
  right: 0;
  bottom: 0;
}
/* The tab bar's glass over whatever scrolls beneath it: at the demo's 62 %
 * a saturated block (a chart's current month) showed through enough to take
 * a tab's grey label to 4.4:1. A little more frost, the same glass. */
.light .tabbar {
  background: rgba(255, 255, 255, 0.8);
}
.dark .tabbar {
  background: rgba(40, 40, 44, 0.78);
}
/* Links and warnings standing straight on the backdrop take the backdrop's
 * own deeper shades: on a wide window the lower ridges reach higher up the
 * page, under a section head's Save (4.3:1 in the link blue) and an
 * operator's warning (3.0:1 in the danger red). */
.light {
  --link-on-backdrop: #00338f;
  --danger-on-backdrop: #5c0007;
}
.dark {
  --link-on-backdrop: #a8c7fa;
  --danger-on-backdrop: #ffa3ad;
}
.search-cancel,
.section-head .link,
.arrange a,
.list-foot a {
  color: var(--link-on-backdrop);
}
.intro.alert,
#content > .alert {
  color: var(--danger-on-backdrop);
}
/* A form's labels standing straight on the backdrop (a page of fields, not
 * a card of them) are text on the backdrop too: 4.4:1 in the muted grey on
 * the sky (the full browser check at f1ef599, the gallery's fields). */
#content form label:not(:is(.list, .widget, dialog, .lg, .plain-card, .row) label),
#content .form-list label {
  color: var(--on-backdrop);
}

/* ---- the default currency's sheet (20260929-028): the demo's currency
 * list, a radio per currency, and the rates' source named under it ------- */
.cur-list .cur-opt {
  cursor: pointer;
}
.cur-list .cur-opt input {
  flex: none;
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--primary);
}
.cur-list .cur-opt:has(input:checked) .label {
  color: var(--primary-text);
  font-weight: 600;
}
.cur-source {
  margin: 12px 4px 0;
  font-size: 13px;
  color: var(--muted);
}

/* ---- the phone as a country code and a number (the operator, 2026-09-29):
 * the label, the dropdown and the number on one row, the number taking
 * what is left ------------------------------------------------------------ */
.phone-row {
  flex-wrap: wrap;
  gap: 8px 12px;
}
/* The label, the prefix and the number on one line (the operator: "keep
 * the phone number and the title on the same line"), the number starting
 * right after the prefix. */
.phone-row {
  flex-wrap: nowrap;
  column-gap: 8px;
}
.phone-row .label {
  flex: none;
}
.phone-row .phone-country {
  flex: none;
  max-width: 11em;
  min-height: 44px;
  padding: 0 8px;
  border: 0;
  border-radius: 12px;
  background: var(--track);
  color: var(--text);
  font: inherit;
  font-size: 15px;
}
/* Compact (profile.js swaps in the prefixes): a fixed width, so opening it
 * (the whole names) moves nothing. */
#content .phone-row .phone-country.compact {
  /* #content: the page's rule for form fields (width 100 %) is an id rule.
   * Just the code, as text (the operator, 2026-09-29: "remove the country
   * code flag and bubble completely and just keep the country code"): no
   * background, no arrow; still a 44 px target that opens the list. */
  width: 3.2em; /* "+971", the widest code; */
  field-sizing: content; /* where supported, just its own code */
  min-width: 2.2em;
  padding: 0;
  background: none;
  -webkit-appearance: none;
  appearance: none;
  color: var(--text);
  text-align: start;
  cursor: pointer;
}
.phone-row .phone-local,
#content .field-row.phone-row input.value.phone-local {
  /* #content: the profile's field rule puts values at the row's end, and is
   * an id rule; the number reads from the left, after its code. */
  flex: 1 1 7em;
  min-width: 0;
  min-height: 44px;
  text-align: start;
  font-variant-numeric: tabular-nums;
}

/* The full name in capitals (the operator, 2026-09-29): drawn so even
 * before the script has changed what was typed. */
input[data-uppercase] {
  text-transform: uppercase;
}

/* ---- an expense in another currency (20260929-029): the amount's currency
 * as the demo's chip, a dropdown; what it becomes, under the amount -------- */
#content form select.ae-cur,
.compose-sheet select.composer-currency {
  /* #content: the page's rule for form fields (width 100 %) is an id rule;
   * the chip is as wide as its code, the amount takes the rest. */
  flex: none;
  width: auto;
  -webkit-appearance: none;
  appearance: none;
  min-height: 48px;
  padding: 0 14px;
  font-weight: 700;
  text-align: center;
  cursor: pointer;
}
.ex-converted {
  margin: 0 0 2px;
  font-size: 17px;
}
.rate-line {
  overflow-wrap: anywhere;
}

/* ---- settling with one person across groups (20260929-072) and per
 * expense (20260929-071): the person page's one total, its groups small
 * beneath; a share's paid and open parts; the Log's paid dots ------------- */
.settle-total .settle-big {
  font-size: 34px;
}
.settle-parts {
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
  font-size: 14px;
}
.settle-parts li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 6px;
  padding: 4px 0;
}
.settle-parts li + li {
  border-top: 0.5px solid var(--separator);
}
.settle-parts .sp-group {
  font-weight: 600;
  min-width: 0;
  overflow-wrap: anywhere;
}
.settle-parts .sp-what,
.settle-parts .sp-note {
  color: var(--muted);
}
.settle-parts .money {
  margin-inline-start: auto;
  font-variant-numeric: tabular-nums;
}
.settle-parts .sp-note {
  flex-basis: 100%;
  font-size: 13px;
}
.settle-parts .sp-remind {
  display: flex;
  flex-basis: 100%;
  justify-content: flex-end;
}
.share-state {
  display: block;
  color: var(--muted);
  font-size: 13px;
}
.share-state .open {
  color: var(--minus);
}
.row .settle-this {
  flex: none;
}
/* A share's row holds the name, what is paid and open, its tags, the amount
 * and Settle this: on a phone that is wider than the screen (ui-check,
 * 20260930), so it wraps, the name giving way and Settle this taking its own
 * line at the end. */
#split .row {
  flex-wrap: wrap;
  row-gap: 6px;
}
#split .row .label {
  min-width: 0;
  flex: 1 1 8em;
}
#split .row .settle-this {
  margin-inline-start: auto;
}
.paid-dots {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}
.paid-dots .pd {
  flex: none;
  width: 8px;
  height: 8px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
}
.paid-dots .pd.paid {
  background: currentColor;
}
.paid-dots .pd-text {
  margin-inline-start: 2px;
}

/* A sheet is dragged down by its grab bar and title: the browser must not
 * take that gesture for a scroll (ui.js, 20260930-008). */
dialog.demo-sheet .lg-grab,
dialog.demo-sheet .sheet-h {
  touch-action: none;
}

/* A payment that already counts and waits: when it confirms itself
 * (20260930-006). Your own confirmed ones, greyed at the bottom of Waiting. */
.an-due {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--muted);
}
.w-row.w-done {
  opacity: 0.55;
}


/* ---- the Log's expense card (the operator's three designs, 2026-10-01:
 * Status drawn, Hero and Compact kept; settings.PAGUM_EXPENSE_CARD). Ported
 * from the designs' file with its final colours: owed to you in the
 * primary blue, owed by you in red, paid back in green; a dot per person,
 * filled once paid, an outline while open, dashed while contested, ringed
 * for you. -------------------------------------------------------------- */
.bubble-c.xp,
.msg.me .bubble-c.xp {
  min-width: 220px;
  background: var(--cell);
  box-shadow: inset 0 0 0 1.5px var(--primary), 0 1px 1px var(--lg-drop, rgba(0, 0, 0, 0.08));
}
.bubble-c.xp .xp-title {
  color: var(--primary);
  font-weight: 600;
}
.xpv {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.xpv .fx {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.xp-count,
.xc-n {
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* dots */
.xdots {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-width: 190px;
  padding: 4px; /* room for the viewer's ring */
}
.xd {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  box-sizing: border-box;
}
.xd.p {
  background: var(--plus);
}
.xd.o {
  border: 1.5px solid var(--muted);
  opacity: 0.7;
}
.xd.c {
  border: 1.5px dashed var(--text);
}
/* The viewer's ring: a shadow, which follows the circle exactly, not an
 * outline (the operator, 2026-10-01: "the circle rappresenting me is not
 * centered around its dot"). */
.xd.me {
  box-shadow: 0 0 0 1.5px var(--cell), 0 0 0 3.5px var(--primary);
}
.xdots.big {
  gap: 5px;
  max-width: 210px;
}
.xdots.big .xd {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  font-size: 11px;
  font-weight: 700;
}
.xdots.big .xd.p {
  background: oklch(0.55 0.1 var(--h, 255));
  color: #ffffff;
}
.xdots.big .xd.o {
  color: var(--muted);
  opacity: 1;
  border-color: color-mix(in srgb, var(--muted) 60%, transparent);
}
.xdots.big .xd.c {
  color: var(--text);
}
/* The card colours its own figures by its tone, not by the Log's in/out
 * (which colours every .money in a line): the total stays muted. */
[data-flow] .xpv .money {
  color: inherit;
}
[data-flow] .xpv .xa-total,
[data-flow] .xpv .fx,
.xpv .xb-title .money {
  color: var(--muted);
}
/* Status */
.xa-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.xa-total {
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.xa-status {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin: 6px 0 4px; /* not the design's -4px: the card's box must hold its line */
  padding: 8px 10px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--text) 5%, transparent);
}
.xa-lead {
  font-size: 13px;
  font-weight: 600;
}
.xa-amtc {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.xa-amt {
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.xa-status.done .xa-amt {
  font-size: 16px;
}
.xa-status.warn .xa-amt {
  color: var(--muted);
  text-decoration: line-through;
}
.xa-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
/* Hero */
.xb-title {
  font-size: 13px;
  color: var(--muted);
}
.xb-hero {
  margin-top: 2px;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.xb-hero.done {
  font-size: 20px;
}
.xb-lead {
  font-size: 13px;
  font-weight: 600;
}
.xb-sep {
  height: 1px;
  margin: 6px 0 4px;
  background: color-mix(in srgb, var(--text) 10%, transparent);
}
/* Compact */
.xc-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.xc-total {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.xc-bar {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin: 4px 0;
}
.xc-bar span {
  flex: 1 0 14px;
  height: 6px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--muted) 30%, transparent);
}
.xc-bar .p {
  background: var(--plus);
}
.xc-bar .c {
  background: repeating-linear-gradient(90deg, var(--text) 0 3px, transparent 3px 5px);
}
.xc-bar .me {
  box-shadow: 0 0 0 1.5px var(--cell), 0 0 0 3px var(--primary);
}
.xc-chip {
  align-self: flex-start;
  margin-top: 2px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  background: color-mix(in srgb, var(--text) 6%, transparent);
}
.xc-fx {
  margin-inline-start: 10px;
}
/* The tone, alike in all three: an icon before the sentence, the figure
 * and the tint in the tone's colour. */
.xa-lead::before,
.xb-lead::before,
.xc-chip::before {
  display: inline-grid;
  place-items: center;
  width: 16px;
  height: 16px;
  margin-inline-end: 5px;
  border-radius: 50%;
  background: var(--text);
  color: var(--cell);
  font-size: 10px;
  font-weight: 700;
  vertical-align: -3px;
}
.xa-status.minus .xa-lead::before,
.xb-lead.minus::before,
.xc-chip.minus::before {
  content: "↑";
  background: var(--danger);
  color: #ffffff;
}
.xa-status.plus .xa-lead::before,
.xb-lead.plus::before,
.xc-chip.plus::before {
  content: "↓";
  background: var(--primary);
  color: #ffffff;
}
.xa-status.done .xa-lead::before,
.xb-lead.done::before,
.xc-chip.done::before {
  content: "✓";
  background: var(--plus);
  color: #ffffff;
}
.xa-status.warn .xa-lead::before,
.xb-lead.warn::before,
.xc-chip.warn::before {
  content: "!";
  background: none;
  color: var(--text);
  box-shadow: inset 0 0 0 1.5px var(--text);
}
.xa-status.none .xa-lead::before,
.xb-lead.none::before,
.xc-chip.none::before {
  display: none;
}
.dark .xa-status.minus .xa-lead::before,
.dark .xb-lead.minus::before,
.dark .xc-chip.minus::before,
.dark .xa-status.plus .xa-lead::before,
.dark .xb-lead.plus::before,
.dark .xc-chip.plus::before,
.dark .xa-status.done .xa-lead::before,
.dark .xb-lead.done::before,
.dark .xc-chip.done::before {
  color: #000000;
}
.xa-status.minus,
.xc-chip.minus {
  background: color-mix(in srgb, var(--danger) 9%, transparent);
}
.xa-status.plus,
.xc-chip.plus {
  background: color-mix(in srgb, var(--primary) 9%, transparent);
}
.xa-status.done,
.xc-chip.done {
  background: color-mix(in srgb, var(--plus) 10%, transparent);
}
[data-flow] .xa-status.minus .xa-amt,
[data-flow] .xb-hero.minus,
.xa-status.minus .xa-amt,
.xb-hero.minus,
.xb-lead.minus,
.xc-chip.minus {
  color: var(--danger);
}
[data-flow] .xa-status.plus .xa-amt,
[data-flow] .xb-hero.plus,
.xa-status.plus .xa-amt,
.xb-hero.plus,
.xb-lead.plus,
.xc-chip.plus {
  color: var(--primary);
}
.xa-status.done .xa-lead,
.xb-lead.done,
.xc-chip.done {
  color: var(--plus);
}

/* ---- notification cards, "Amount first" (the operator's design,
 * 2026-10-01): each request its own card, a coloured label for its kind,
 * the amount the biggest thing on it, two equal buttons along the bottom.
 * The same in Activity and in a group's Log (_nc_head.html). ----------- */
.nc-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 16px;
}
.nc {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 14px 12px;
  border-radius: 22px;
  background: var(--cell);
  box-shadow: 0 1px 2px var(--lg-drop, rgba(0, 0, 0, 0.08));
  min-width: 0;
}
.chat .nc,
.log .nc {
  align-self: stretch;
  margin: 2px 4px;
}
.nc-top {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 13px;
  font-weight: 600;
}
.nc-top time {
  margin-inline-start: auto;
  font-weight: 500;
  color: var(--muted);
}
.nc-kind {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 7px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  background: color-mix(in srgb, var(--text) 6%, transparent);
}
.nc-kind .ionicon {
  width: 12px;
  height: 12px;
}
.nc-kind.plus {
  color: var(--plus);
  background: color-mix(in srgb, var(--plus) 13%, transparent);
}
.nc-kind.minus {
  color: var(--minus);
  background: color-mix(in srgb, var(--minus) 12%, transparent);
}
.nc-kind.warn {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 11%, transparent);
}
.nc-main {
  display: flex;
  align-items: center;
  gap: 12px;
}
.nc-txt {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.nc-amt {
  font-size: 28px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.nc-amt small {
  margin-inline-start: 0;
  font-size: 0.55em;
  font-weight: 600;
}
.nc-amt.plus {
  color: var(--plus);
}
.nc-amt.minus {
  color: var(--minus);
}
.nc-line {
  font-size: 15px;
}
.nc-line strong {
  font-weight: 600;
}
.nc-ctx {
  font-size: 13px;
  color: var(--muted);
  /* Wraps, a long title or a pasted link broken anywhere: kept on one line
   * its box ran past the screen (ui-check, 2026-10-01). */
  overflow-wrap: anywhere;
}
.nc-line,
.nc-quote {
  overflow-wrap: anywhere;
}
.nc-quote {
  margin: 0;
  padding: 8px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--text) 6%, transparent);
  font-size: 14px;
}
.nc-acts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.nc-acts.one {
  grid-template-columns: 1fr;
}
.nc-acts form {
  display: contents;
}
.nc-btn {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  border: 0;
  border-radius: 14px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  background: var(--track);
  color: var(--text);
}
.nc-btn.p {
  background: var(--primary);
  color: var(--on-primary, #ffffff);
}
.nc-btn .ionicon {
  width: 15px;
  height: 15px;
}
/* The cards keep Activity's an-item class (what the pages' tests and
 * scripts read), not its list look: stretched, no separator line. */
.nc.an-item {
  align-items: stretch;
  padding: 14px 14px 12px;
}
.nc.an-item + .nc.an-item::before {
  content: none;
}

/* A person's page: the header collapse, ported from the operator's demo
 * (Person_Header_Collapse, 2026-10-01), CSS only. Geometry in px: the title
 * bar 56 tall, its middle at 28. Face 88 -> 30 (scale 1 -> .341), name 26 ->
 * 17 (scale 1.529 -> 1), gap 8. At the start the face's middle is 108 below
 * the bar's top and the name below it; over 140 px of scrolling the name
 * slides (0-.3), goes round the face's edge on a quarter arc at the constant
 * gap (.3-.85) and lifts to the middle (.85-1), so no letter passes under the
 * face. --pp runs 0 to 1 on the page's scroll; both ways. The pair lives in
 * the bar (drawn above its blur); .pp-hero keeps its place in the page.
 * Without scroll timelines: no face, the name a plain title. */
.pp-av,
.pp-hero {
  display: none;
}
@property --pp {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
@keyframes pp-move {
  from {
    --pp: 0;
  }
  to {
    --pp: 1;
  }
}
@supports (animation-timeline: scroll()) {
  .header h1:has(> .pp-pair) {
    position: static;
    overflow: visible;
    min-height: 44px;
  }
  .pp-pair {
    position: absolute;
    top: 0;
    left: 50%;
    display: block;
    width: max-content;
    transform: translateX(-50%);
    animation: pp-move linear both;
    animation-timeline: scroll(root);
    animation-range: 0 140px;
    --k: calc(1 - 0.659 * var(--pp));
    --r: calc(44px * var(--k));
    --s: calc(1.529 - 0.529 * var(--pp));
    --t1: clamp(0, var(--pp) / 0.3, 1);
    --t2: clamp(0, (var(--pp) - 0.3) / 0.55, 1);
    --t3: clamp(0, (var(--pp) - 0.85) / 0.15, 1);
    --th: calc(90deg * (1 - var(--t2)));
  }
  .pp-nm {
    display: block;
    font-size: 17px;
    line-height: 22px;
    white-space: nowrap;
    transform-origin: 0 0;
    transform: translate(
        calc(
          100% * ((1 - var(--pp)) / 2 - var(--s) * (1 - var(--t1)) / 2) - 4px * var(--pp) +
            (var(--r) + 8px) * cos(var(--th))
        ),
        calc(108px - 80px * var(--pp) + (var(--r) + 8px) * sin(var(--th)) - 11px * var(--s) * var(--t3))
      )
      scale(var(--s));
  }
  .pp-av {
    display: block;
    position: absolute;
    width: 88px;
    height: 88px;
    margin: -44px 0 0 -44px;
    left: calc(100% * (1 - var(--pp)) / 2 - 4px * var(--pp));
    top: calc(108px - 80px * var(--pp));
    transform: scale(var(--k));
  }
  .pp-av .face {
    width: 100%;
    height: 100%;
    font-size: 36px;
  }
  .pp-hero {
    display: block;
    height: 150px;
  }
  /* Room to always finish: the viewport and 140 px of scrolling. */
  main#content:has(> .pp-hero) {
    min-height: calc(100dvh + 140px);
  }
}

/* The credits, one line: the developer's avatar (a copy shipped with
 * pagum), the line, GitHub's mark (2026-10-01). */
.credits-row {
  gap: 12px;
}
/* The avatar in the same round face as a person's (.face, the operator,
 * 2026-10-01: "put the GH picture in a round circle (like the user avatar
 * thing)"). */
.credit-face {
  flex: none;
}
.credits-row .label {
  font-size: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* A group's tile in smaller places (_group_tile.html; the operator,
 * 2026-10-01: the group's picture beside its name in the title, and
 * "when i set a group picture it will show everyware"): sm on Home's
 * "Your groups", xs in a group page's title and a person page's group
 * heads. The radius keeps the tile's proportion (12 of 40). */
.g-tile.sm {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  font-size: 14px;
}
.g-tile.xs {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  font-size: 12px;
}
.header h1 .t-link {
  gap: 8px;
}
.section-head.g-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Your photo on the profile: the row is the picker, your face at its
 * start (the person page's size, 32 px). */
.photo-row {
  position: relative;
  gap: 12px;
  cursor: pointer;
}
.photo-row .face {
  width: 32px;
  height: 32px;
  font-size: 14px;
}
.photo-row:has(:focus-visible) {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

/* ---- the picture picker (_photo_picker.html, photo-picker.js; the
 * operator's profile-picture-picker.css, on pagum's tokens): a tall sheet,
 * its bar (Cancel, the title, Done), a preview, three tabs and a body
 * that scrolls ----------------------------------------------------------- */
dialog.demo-sheet.ppk {
  height: min(88dvh, 760px);
  padding-top: 6px;
}
dialog.demo-sheet.ppk[open] {
  display: flex;
  flex-direction: column;
}
.ppk-bar {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr) 88px;
  align-items: center;
}
.ppk-bar .sheet-h {
  margin: 0;
  text-align: center;
}
.ppk-bar button {
  min-height: 44px;
  padding: 0 8px;
  border: 0;
  background: none;
  font: inherit;
  color: var(--primary);
  cursor: pointer;
}
.ppk-bar .ppk-cancel {
  text-align: start;
}
.ppk-bar .ppk-done {
  font-weight: 600;
  text-align: end;
}
.ppk-bar .ppk-done:disabled {
  color: var(--muted);
  opacity: 0.5;
  cursor: default;
}
.ppk-prev {
  display: flex;
  justify-content: center;
  padding: 4px 0 14px;
}
.ppk-av {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  overflow: hidden;
  border-radius: 28px;
  background: oklch(0.9 0.04 var(--h, 255));
  color: oklch(0.42 0.1 var(--h, 255));
  font-size: 40px;
  font-weight: 700;
}
.ppk-av.round {
  border-radius: 50%;
  background: oklch(0.52 0.12 var(--h, 255));
  color: #fff;
}
.ppk-av img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ppk-av img.ppk-glyph {
  width: 68%;
  height: 68%;
  object-fit: contain;
}
.ppk-tabs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2px;
  padding: 2px;
  border-radius: 12px;
  background: var(--track);
}
.ppk-tabs button {
  min-height: 36px;
  border: 0;
  border-radius: 10px;
  background: none;
  font: inherit;
  font-size: 14px;
  color: var(--text);
  cursor: pointer;
}
.ppk-tabs button[aria-selected="true"] {
  background: var(--cell);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
  font-weight: 600;
}
.ppk-panel {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 0 8px;
}
.ppk-panel[hidden] {
  display: none;
}
.ppk-note,
.ppk-foot {
  margin: 10px 4px 0;
  font-size: 13px;
}
.ppk-foot {
  text-align: center;
}
.ppk-search {
  margin-bottom: 12px;
}
.ppk-h {
  margin: 0 0 8px;
  font-size: 13px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.ppk-sw {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 14px;
}
.ppk-sw button {
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
}
.ppk-sw button[aria-checked="true"] {
  outline: 2.5px solid var(--primary);
  outline-offset: 2px;
}
/* The colours, plain sRGB so a canvas draws exactly what is shown, and
 * the same in both themes: the picture is the same for everyone. */
.ppk-bg-0 {
  --ppk-bg: #c9dcff;
}
.ppk-bg-1 {
  --ppk-bg: #c3ecd2;
}
.ppk-bg-2 {
  --ppk-bg: #ffe0a8;
}
.ppk-bg-3 {
  --ppk-bg: #ffd0de;
}
.ppk-bg-4 {
  --ppk-bg: #e3f0b0;
}
.ppk-bg-5 {
  --ppk-bg: #e6d4ff;
}
.ppk-sw button,
.ppk-av[class*="ppk-bg-"] {
  background: var(--ppk-bg);
}
.ppk-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 4px;
}
.ppk-grid.gif {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}
.ppk-item {
  aspect-ratio: 1;
  min-height: 44px;
  padding: 6px;
  border: 0;
  border-radius: 12px;
  background: none;
  cursor: pointer;
}
.ppk-grid.gif .ppk-item {
  background: var(--bubble);
}
.ppk-item[hidden] {
  display: none;
}
.ppk-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.ppk-item[aria-pressed="true"] {
  background: rgba(var(--primary-rgb), 0.16);
  box-shadow: inset 0 0 0 2px var(--primary);
}
.ppk-empty {
  margin: 24px 0;
  text-align: center;
}

/* ---- the Welcome page (core/welcome.html; the operator's design,
 * 2026-10-02): a guest's /. A header with the wordmark, a hero over the
 * valley, then a sheet that slides up over it with the app's own blocks.
 * Tokens only, so it follows the theme like the app. -------------------- */
.welcome {
  position: relative;
  z-index: 1;
  min-height: 100dvh;
  color: var(--text);
}
.wl-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 1120px;
  margin: 0 auto;
  padding: calc(var(--sat, 0px) + 16px) 16px 0;
}
.wl-mark {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.03em;
}
.wl-nav {
  display: flex;
  align-items: center;
  gap: 4px;
}
.wl-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 12px;
  font-weight: 600;
  text-decoration: none;
}
.welcome .pill.quiet.lg {
  background: var(--glass);
}
.wl-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  min-height: calc(100dvh - 76px);
  max-width: 1120px;
  margin: 0 auto;
  padding: 8px 16px 120px;
  text-align: center;
}
.wl-hero h1 {
  margin: 0;
  font-size: clamp(44px, 7vw, 84px);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -0.035em;
  text-wrap: balance;
}
.wl-lede {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(17px, 1.8vw, 21px);
  text-wrap: pretty;
}
.wl-cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
.wl-cta .pill {
  min-height: 50px;
  padding: 0 26px;
  font-size: 17px;
  text-decoration: none;
}
.wl-more {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  margin-top: 24px;
  padding: 0 16px;
  border-radius: 999px;
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}
.wl-how {
  border-radius: 32px 32px 0 0;
  background: color-mix(in srgb, var(--grouped) 92%, transparent);
  -webkit-backdrop-filter: blur(30px);
  backdrop-filter: blur(30px);
  box-shadow: 0 -10px 40px var(--glass-shadow);
  padding: 56px 0 calc(var(--sab, 0px) + 48px);
}
.wl-inner {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 16px;
}
.wl-how h2 {
  margin: 0 0 8px;
  font-size: clamp(28px, 4vw, 40px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.wl-sub {
  margin: 0 0 6px;
  max-width: 52ch;
  text-wrap: pretty;
}
.wl-note {
  margin: 0 0 32px;
  font-size: 13px;
}
.wl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: 32px 20px;
  align-items: start;
}
.wl-item {
  display: grid;
  gap: 12px;
  min-width: 0;
}
.wl-say h3 {
  margin: 0;
  font-size: 17px;
}
.wl-say p {
  margin: 2px 0 0;
  font-size: 15px;
  color: var(--muted);
}
/* The app's blocks keep their own look; here they sit in the grid's
 * column rather than the app's padded one. */
.wl-item .widgets {
  padding: 0;
}
.wl-item .list {
  margin: 0;
}
.wl-bubbles {
  display: grid;
  gap: 10px;
}
.wl-end {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  margin-top: 64px;
  text-align: center;
}
.wl-end h2 {
  font-size: clamp(26px, 3.4vw, 34px);
}
.wl-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  margin-top: 64px;
  padding-top: 20px;
  border-top: 0.5px solid var(--separator);
  font-size: 13px;
}
.wl-foot a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
/* The wordmark is a link on signed-out pages: a full tap target. */
a.wl-mark {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text);
  text-decoration: none;
}
.plain-head {
  position: relative;
  z-index: 2;
}
/* Sign in (account/login.html, the operator's design): a heading, a line,
 * allauth's form, then Create account below "New to pagum?". */
.si-h {
  margin: 0 0 4px;
  font-size: 24px;
  letter-spacing: -0.01em;
}
.si-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 12px;
  color: var(--muted);
  font-size: 13px;
}
.si-or span {
  flex: 1;
  height: 1px;
  background: var(--separator);
}
.si-new {
  text-decoration: none;
}
/* A person bubble's line beneath the name (sub=: the Overview's "waiting",
 * the Welcome page's groups), on its own line as a row's is. */
.person-bubble .label .sub {
  display: block;
  font-size: 13px;
  color: var(--muted);
}
/* On the Welcome page each block has its heading above it (wl-say), so the
 * block's own title is for screen readers only, not said twice on screen. */
.wl-item .widget > h2 {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* The wordmark across the top of a signed-out page, the card centred below. */
.screen.plain:has(> .plain-head) {
  grid-template-rows: auto 1fr;
  padding-top: 0;
}
.screen.plain > .plain-head {
  justify-self: stretch;
  align-self: start;
  width: 100%;
  box-sizing: border-box;
  padding-inline: 0; /* the screen's own side padding is enough */
}
/* The operator page's Usage block (instance/status.html; the operator's admin
 * design, 2026-10-04): four counts two by two, then expenses by month in
 * Home's columns. */
.op-widgets {
  padding: 8px 0 0;
}
.op-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 16px;
  margin-bottom: 14px;
}
.op-search {
  margin: 0 0 10px;
}
.wl-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 16px;
}
.op-maint-note {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}
.op-maint-text {
  width: 100%;
  min-height: 96px;
  box-sizing: border-box;
}
.op-check {
  gap: 12px;
  cursor: pointer;
}
.danger-text {
  color: var(--danger);
}
/* The maintenance page (instance/maintenance.html): its card, with a mark. */
.maint-icon {
  display: inline-flex;
  margin-bottom: 8px;
  color: var(--primary);
  font-size: 40px;
}
.maintenance-message {
  white-space: normal;
  overflow-wrap: anywhere;
}
