/* =====================================================================
   NosGrupo — design system
   Our groups • Our culture • Our future

   Built to the supplied UI reference (resources/brand/nosgrupo-ui-reference.png).

   Navy carries the structure (sidebar, headings). A purple→violet gradient carries primary action. Soft
   tinted icon badges carry meaning on tiles and list rows. Colour is used generously but always to identify
   something, never as decoration.

   Plain CSS, no build step. Mobile first; tablet >= 768px; desktop >= 1100px.
   --brand / --accent are rewritten per tenant from App\Support\Brand.
   ===================================================================== */

:root {
  /* Structure */
  --navy: #1B1F4B;
  --navy-rgb: 27, 31, 75;
  --navy-700: #151838;
  --navy-800: #0E1129;
  --navy-900: #090B1E;
  --indigo: #2A2F6B;

  /* Primary action — the gradient from the reference */
  --violet: #7C5CFC;
  --violet-rgb: 124, 92, 252;
  --violet-2: #A855F7;
  --violet-ink: #5B33D6;
  --grad-primary: linear-gradient(135deg, #7C5CFC 0%, #A855F7 100%);

  /* Accents — the three people in the mark */
  --purple: #8B5CF6;
  --purple-rgb: 139, 92, 246;
  --teal: #14B8C4;
  --teal-rgb: 20, 184, 196;
  --gold: #F5A524;

  --gold-rgb: 245, 165, 36;
  --pink: #E8528A;

  /* Per-tenant, overwritten inline on <html> */
  --brand: var(--navy);
  --brand-rgb: var(--navy-rgb);
  --accent: var(--teal);
  --accent-rgb: var(--teal-rgb);

  /* Neutrals */
  --bg: #F4F5F9;
  --surface: #FFFFFF;
  --surface-2: #F7F8FB;
  --surface-3: #EDEFF4;
  --field: #FAFBFD;
  --line: #E6E8EF;
  --line-strong: #D4D8E2;
  --text: #14161F;
  --text-2: #434858;
  --muted: #717789;

  /* Status */
  --green: #16A34A;
  --green-ink: #0E7A37;
  --green-soft: #E7F7EE;
  --amber: #F5A524;
  --amber-ink: #8A5B00;
  --amber-soft: #FEF4E2;
  --red: #E0353B;
  --red-ink: #A8181D;
  --red-soft: #FDECEC;
  --blue: #3B82F6;
  --blue-ink: #1D4FD7;
  --blue-soft: #E8EFFE;
  --teal-soft: #E2F7F9;
  --violet-soft: #F0EBFE;
  --grey-soft: #EEF0F5;

  --radius-xl: 24px;
  --radius-lg: 20px;
  --radius: 15px;
  --radius-sm: 11px;
  --radius-pill: 999px;

  --shadow-xs: 0 1px 2px rgba(16, 18, 30, .05);
  --shadow: 0 1px 2px rgba(16, 18, 30, .04), 0 6px 18px rgba(16, 18, 30, .05);
  --shadow-lift: 0 2px 4px rgba(16, 18, 30, .05), 0 14px 32px rgba(16, 18, 30, .09);
  --shadow-violet: 0 8px 20px rgba(var(--violet-rgb), .34);
  --shadow-navy: 0 18px 40px rgba(var(--navy-rgb), .25);

  --font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --header-h: 72px;
  --bottom-h: 58px;
  --sidebar-w: 256px;
  --gutter: 16px;

  color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; overscroll-behavior-y: contain; }

/* No double-tap zoom: a tap on a row should open the row, not magnify the page. */
body {
  touch-action: manipulation;
  margin: 0;
  min-height: 100dvh;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: 680; letter-spacing: -.02em; line-height: 1.25; }
h1 { font-size: 1.55rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }
p { margin: 0 0 .75rem; }
p:last-child { margin-bottom: 0; }
a { color: var(--violet-ink); }

.muted { color: var(--muted); }
.small { font-size: .825rem; }
.tiny { font-size: .75rem; }
.strong { font-weight: 650; }
.center { text-align: center; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
.stack-sm { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
.row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.row-top { display: flex; align-items: flex-start; gap: 10px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1 1 auto; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hide { display: none !important; }
.good-text { color: var(--green-ink); }
.bad-text { color: var(--red-ink); }

/* ---- App shell ------------------------------------------------------------------------------------ */

.shell { min-height: 100dvh; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 40;
  min-height: var(--header-h);
  display: flex; align-items: center; gap: 12px;
  padding: 10px var(--gutter);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.topbar .page-title { font-size: 1.25rem; font-weight: 700; letter-spacing: -.025em; line-height: 1.2; }
.topbar .page-sub { font-size: .82rem; color: var(--muted); }
.topbar .mobile-logo img { height: 30px; width: auto; display: block; }

/* Round icon button with an optional count badge (notifications, help). */
.iconbtn {
  position: relative; flex: 0 0 auto;
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--text-2); cursor: pointer; text-decoration: none;
}
.iconbtn:hover { background: var(--surface-3); }
.iconbtn svg { width: 19px; height: 19px; }
.iconbtn .badge {
  position: absolute; top: -2px; right: -2px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--red); color: #fff;
  font-size: .64rem; font-weight: 700; line-height: 17px; text-align: center;
  border: 2px solid var(--surface);
}

/* The signed-in person, top right. */
.usermenu { display: flex; align-items: center; gap: 9px; text-decoration: none; color: inherit; }
.usermenu .who { display: none; }
.usermenu .who .n { font-size: .86rem; font-weight: 650; line-height: 1.2; }
.usermenu .who .r { font-size: .73rem; color: var(--muted); }

/* Season / organization selector. */
.selector {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: .85rem; font-weight: 600; color: var(--text-2);
  cursor: pointer;
}
.selector select {
  border: 0; background: transparent; padding: 0; font: inherit; color: inherit;
  cursor: pointer; outline: none; width: auto;
}

.main { flex: 1 1 auto; padding: var(--gutter); padding-bottom: calc(var(--bottom-h) + env(safe-area-inset-bottom) + 24px); will-change: transform; }
.page { max-width: 1240px; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
.page-head { display: grid; gap: 3px; }
.page-head .sub { color: var(--muted); font-size: .9rem; }

/* ---- Sidebar (desktop) ----------------------------------------------------------------------------- */

.sidebar { display: none; }

/* ---- Pull down at the top to reload ------------------------------------------------------------------
   Installed to the home screen there is no address bar to reload from, so the gesture people already
   know stands in for it. The tab is drawn by public/js/motion.js and only ever appears mid-pull. */

.pulltab {
  /* Below the header, not over it: the bar is sticky and opaque, and a spinner sitting across the
     page title reads as something gone wrong rather than as the page being pulled. */
  position: fixed; top: calc(var(--header-h) + 6px); left: 50%; z-index: 48;
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface); color: var(--muted);
  border: 1px solid var(--line); box-shadow: var(--shadow-lift);
  transform: translate(-50%, 0); opacity: 0; pointer-events: none;
  /* Only the fade is eased. The position follows the finger and must not lag behind it, so the
     transform is left alone while dragging and animated by script on release. */
  transition: opacity .2s ease;
}
/* The spinner: `ring-resize` from svg-spinners.

   MIT, Copyright (c) Utkarsh Verma - https://github.com/n3r4zzurr0/svg-spinners

   The shape is inlined by public/js/motion.js and its keyframes are kept here rather than inside the
   SVG, so the animation can be held back until the pull is released: while dragging, the ring is
   drawn by the gesture, and only afterwards does it start turning on its own. */
.pulltab .pullspinner {
  width: 24px; height: 24px; display: block;
  /* The gold out of the mark, which is the one colour in it that reads at this size on white. */
  stroke: var(--gold);
  transform: rotate(-90deg);   /* so the pull draws it from the top, the way a dial fills */
}
.pulltab .pullspinner .arc { stroke-linecap: round; stroke-dasharray: 0 59.7; }

/* Far enough: the ring has closed and letting go now will reload. */
.pulltab.ready { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(var(--gold-rgb), .25), var(--shadow-lift); }

/* Let go, and the library's own animation takes it: the whole ring turns while the arc grows and
   shrinks, which is what stops an indeterminate spinner looking like it has stalled. */
.pulltab.spinning .pullspinner { animation: pullring 2s linear infinite; transform-origin: center; }
.pulltab.spinning .pullspinner .arc { animation: pullarc 1.5s ease-in-out infinite; }
@keyframes pullring { 100% { transform: rotate(360deg); } }
@keyframes pullarc {
  0%        { stroke-dasharray: 0 150;  stroke-dashoffset: 0; }
  47.5%     { stroke-dasharray: 42 150; stroke-dashoffset: -16; }
  95%, 100% { stroke-dasharray: 42 150; stroke-dashoffset: -59; }
}

/* A toggle that has to look like a heading rather than a button. */
.demotoggle {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 0; background: none; border: 0; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.demotoggle .chev {
  flex: 0 0 auto; width: 18px; height: 18px;
  color: var(--muted); transition: transform .15s ease;
}
.demotoggle .chev.flip { transform: rotate(180deg); }

/* ---- Bottom navigation (phone) --------------------------------------------------------------------- */

.bottomnav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  /* The height ADDS the home-indicator inset rather than containing it. Everything here is
     border-box, so `height: var(--bottom-h)` with the inset as padding left only 24px of usable
     height on an iPhone - a 22px icon and its label crushed against the top edge. This way the row
     of items is always --bottom-h tall and the inset sits underneath it, over the indicator. */
  height: calc(var(--bottom-h) + env(safe-area-inset-bottom));
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.bottomnav a, .bottomnav button {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  text-decoration: none; color: var(--muted); font: inherit; font-size: .67rem; font-weight: 600;
  background: none; border: 0; cursor: pointer;
}
.bottomnav a svg, .bottomnav button svg { width: 22px; height: 22px; }
.bottomnav a.on, .bottomnav button.on { color: var(--violet); }

/* Behind the sheet: the page goes a little soft and a shade darker, so the eye settles on the sheet
   without the page disappearing. Below the bar in the stack, so the bar itself stays sharp. */
.navscrim {
  position: fixed; inset: 0; z-index: 44;
  background: rgba(14, 17, 41, .16);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

/* Everything past the fifth entry. It hangs off the bar - same place, one tap further - rather than
   covering the screen, because it is navigation and not a decision. */
.navsheet {
  position: fixed; left: 10px; right: 10px; z-index: 46;
  bottom: calc(var(--bottom-h) + 10px + env(safe-area-inset-bottom));
  display: grid; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lift);
}
.navsheet a {
  display: flex; align-items: center; gap: 11px;
  padding: 13px 14px; text-decoration: none; color: inherit;
  font-size: .9rem; font-weight: 650; border-bottom: 1px solid var(--line);
}
.navsheet a:last-child { border-bottom: 0; }
.navsheet a svg { width: 20px; height: 20px; color: var(--muted); flex: 0 0 auto; }
.navsheet a .chev { color: var(--line-strong); }
.navsheet a .chev svg { width: 16px; height: 16px; }
.navsheet a.on, .navsheet a.on svg { color: var(--violet); }

/* The marker on the top edge of the bar, over whichever tab you are on. It is one element that moves
   rather than a border on each tab, so changing page slides it across instead of switching it off in
   one place and on in another. */
.bottomnav .navdot {
  position: absolute; top: 0; left: 0;
  width: 26px; height: 3px; border-radius: var(--radius-pill);
  background: var(--violet);
  opacity: 0; transform: translateX(-60px);
  transition: transform .3s cubic-bezier(.2, .8, .2, 1), opacity .2s ease;
  pointer-events: none;
}
/* Put in place rather than moved: the first paint, and a resize, must not look like a slide. */
.bottomnav .navdot.instant { transition: none; }
/* Somebody who asked for stillness gets the marker, just not the travel. */
[data-motion="off"] .bottomnav .navdot { transition: none; }

/* Out of the way while reading down a list, back the moment you scroll up. The reduced-motion rule further
   down turns the slide off, and it then simply appears and disappears. */
.bottomnav { transition: transform .22s ease; will-change: transform; }
.bottomnav.is-hidden { transform: translateY(100%); }

/* ---- Cards ----------------------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 16px;
  box-shadow: var(--shadow-xs);
}
.card.flush { padding: 0; overflow: hidden; }
.card.pad-lg { padding: 20px; }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.card-head h2, .card-head h3 { font-size: 1.02rem; }
.card-head .link { font-size: .82rem; font-weight: 650; color: var(--violet-ink); text-decoration: none; }
.card-title { font-size: .74rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 700; }

/* The one navy card that answers the screen's main question. */
.card-hero {
  background: linear-gradient(145deg, var(--navy) 0%, var(--navy-700) 58%, var(--indigo) 100%);
  color: #fff; border: 0; border-radius: var(--radius-xl); padding: 20px;
  box-shadow: var(--shadow-navy); position: relative; overflow: hidden;
}
.card-hero::after {
  content: ""; position: absolute; inset: auto -60px -90px auto;
  width: 220px; height: 220px; border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--accent-rgb), .3), transparent 68%);
  pointer-events: none;
}
.card-hero .label { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; opacity: .74; font-weight: 700; }
.card-hero .amount { font-size: 2.1rem; font-weight: 700; letter-spacing: -.03em; line-height: 1.1; margin-top: 2px; }
.card-hero .meta { opacity: .8; font-size: .875rem; }

.grid { display: grid; gap: 12px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-main { grid-template-columns: minmax(0, 1fr); }

/* ---- Icon badge — the soft tinted square used on tiles and list rows -------------------------------- */

.ibadge {
  flex: 0 0 auto;
  width: 44px; height: 44px; border-radius: 13px;
  display: grid; place-items: center;
}
.ibadge svg { width: 21px; height: 21px; }
.ibadge.sm { width: 36px; height: 36px; border-radius: 11px; }
.ibadge.sm svg { width: 18px; height: 18px; }
.ibadge.violet { background: var(--violet-soft); color: var(--violet-ink); }
.ibadge.blue { background: var(--blue-soft); color: var(--blue-ink); }
.ibadge.green { background: var(--green-soft); color: var(--green-ink); }
.ibadge.red { background: var(--red-soft); color: var(--red-ink); }
.ibadge.amber { background: var(--amber-soft); color: var(--amber-ink); }
.ibadge.teal { background: var(--teal-soft); color: #0B6E76; }
.ibadge.grey { background: var(--grey-soft); color: var(--muted); }

/* ---- Stat tile ------------------------------------------------------------------------------------- */

/* A figure is a way in, not an ornament: every one of these leads to the list it counted. It is a
   link on some screens and a filter button on others, so the type is reset back to looking like a
   card either way. */
.stat {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 15px;
  box-shadow: var(--shadow-xs);
  display: flex; align-items: flex-start; gap: 12px;
  width: 100%; text-align: left; font: inherit; color: inherit; text-decoration: none;
  cursor: pointer; transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.stat:hover { border-color: var(--line-strong); box-shadow: var(--shadow); }
.stat:active { transform: scale(.985); }
/* Switched on, where the figure is currently narrowing the list below it. */
.stat.on { border-color: var(--violet); box-shadow: 0 0 0 1px var(--violet); }
.stat .body { min-width: 0; }
.stat .k { font-size: 1.4rem; font-weight: 700; letter-spacing: -.03em; line-height: 1.15; }
.stat .t { font-size: .8rem; color: var(--muted); font-weight: 600; }
.stat .delta { font-size: .73rem; font-weight: 650; margin-top: 5px; }
.stat .delta.up { color: var(--green-ink); }
.stat .delta.down { color: var(--red-ink); }
.stat .delta.flat { color: var(--muted); }

/* ---- Pills ----------------------------------------------------------------------------------------- */

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 11px; border-radius: var(--radius-pill);
  font-size: .74rem; font-weight: 650; line-height: 1.6;
  background: var(--grey-soft); color: var(--text-2);
  white-space: nowrap;
}
.pill.good { background: var(--green-soft); color: var(--green-ink); }
.pill.warn { background: var(--amber-soft); color: var(--amber-ink); }
.pill.bad { background: var(--red-soft); color: var(--red-ink); }
.pill.info { background: var(--blue-soft); color: var(--blue-ink); }
.pill.violet { background: var(--violet-soft); color: var(--violet-ink); }
.pill.teal { background: var(--teal-soft); color: #0B6E76; }
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* ---- Tabs (segmented control) ---------------------------------------------------------------------- */

.tabs {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 4px; padding: 4px;
  background: var(--surface-3); border-radius: var(--radius);
}
.tabs a, .tabs button {
  appearance: none; border: 0; cursor: pointer;
  padding: 9px 12px; border-radius: 11px;
  background: transparent; color: var(--muted);
  font: inherit; font-size: .86rem; font-weight: 650; text-align: center; text-decoration: none;
}
.tabs a.on, .tabs button.on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-xs); }

/* ---- Pager ----------------------------------------------------------------------------------------
   Previous / where you are / next, with the numbered pages appearing once there is room. Every icon in
   here is sized explicitly: an inline SVG with no size renders enormous. */

.pager { display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; padding: 14px 0 2px; }
.pager .pagebtn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--text);
  font: inherit; font-size: .85rem; font-weight: 650; white-space: nowrap; cursor: pointer;
}
.pager .pagebtn:hover { background: var(--surface-2); }
.pager .pagebtn.is-off { opacity: .45; cursor: default; }
.pager .pagebtn svg { width: 16px; height: 16px; flex: 0 0 auto; }

.pager .pagenow { font-size: .82rem; font-weight: 650; color: var(--muted); white-space: nowrap; }

.pager .pagenums { display: inline-flex; align-items: center; gap: 4px; }
.pager .pagenum {
  min-width: 32px; padding: 7px 8px; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: none; color: var(--text-2);
  font: inherit; font-size: .83rem; font-weight: 650; cursor: pointer; text-align: center;
}
.pager .pagenum:hover { background: var(--surface-2); }
.pager .pagenum.on { background: var(--violet); border-color: var(--violet); color: #fff; }
.pager .pagegap { color: var(--muted); padding: 0 2px; }

/* A line of pills that wraps - figures that belong together but are not a filter. */
.pillrow { display: flex; flex-wrap: wrap; gap: 7px; }

/* ---- Filter chips with counts ---------------------------------------------------------------------- */

.chips { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chip {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  padding: 8px 14px; border-radius: var(--radius-pill);
  border: 1px solid var(--line-strong); background: var(--surface);
  font-size: .84rem; font-weight: 650; color: var(--text-2);
  cursor: pointer; text-decoration: none;
}
.chip .n {
  min-width: 20px; padding: 1px 6px; border-radius: var(--radius-pill);
  background: var(--surface-3); color: var(--text-2);
  font-size: .72rem; font-weight: 700; text-align: center;
}
.chip.on { background: var(--violet); border-color: var(--violet); color: #fff; }
.chip.on .n { background: rgba(255, 255, 255, .26); color: #fff; }

/* ---- Buttons --------------------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 18px; border-radius: var(--radius-sm);
  font: inherit; font-weight: 650; font-size: .92rem;
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
  transition: transform .06s ease, box-shadow .14s ease, filter .14s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--grad-primary); color: #fff; box-shadow: var(--shadow-violet); }
.btn-primary:hover { filter: brightness(1.06); }
.btn-navy { background: var(--navy); color: #fff; }
.btn-ghost { background: var(--surface); border-color: var(--line-strong); color: var(--text); }
.btn-ghost:hover { background: var(--surface-2); }
.btn-quiet { background: transparent; color: var(--text-2); }
.btn-danger { background: var(--red); color: #fff; }
.btn-block { width: 100%; }
.btn-sm { padding: 7px 14px; font-size: .83rem; border-radius: 9px; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .5; pointer-events: none; }

/* An inline SVG with a viewBox but no width/height fills whatever space it is given. Inside a button that a
   grid or flex parent has stretched, that makes the icon balloon and swallow the label, so every icon in a
   control gets an explicit size. */
.btn svg { width: 17px; height: 17px; flex: 0 0 auto; }
.btn-sm svg { width: 15px; height: 15px; }

/* ---- Avatars --------------------------------------------------------------------------------------- */

.avatar {
  width: 42px; height: 42px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--violet-soft); color: var(--violet-ink);
  font-weight: 700; font-size: .85rem; overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar.sm { width: 32px; height: 32px; font-size: .72rem; }
.avatar.lg { width: 56px; height: 56px; font-size: 1.05rem; }
.avatar.sq { border-radius: 13px; }

/* Overlapping stack used on events. */
.avstack { display: flex; align-items: center; }
.avstack .avatar { width: 26px; height: 26px; font-size: .62rem; border: 2px solid var(--surface); margin-left: -8px; }
.avstack .avatar:first-child { margin-left: 0; }
.avstack .more { font-size: .76rem; color: var(--muted); font-weight: 650; margin-left: 7px; }

/* ---- Lists ----------------------------------------------------------------------------------------- */

/* minmax(0, 1fr) rather than a bare auto column: a grid track sizes itself to its widest item, and a
   control that reports a wide intrinsic size - a select full of names - would otherwise push the whole
   list past the card it sits in. */
.list { display: grid; grid-template-columns: minmax(0, 1fr); }
.list > * { padding: 10px 14px; border-bottom: 1px solid var(--line); }
.list > *:last-child { border-bottom: 0; }
/* Plain links fill their row. The :not() keeps this rule off the flex rows below, which it would otherwise
   beat on specificity - turning them back into blocks and stacking their contents. */
.list a:not(.gorow) { text-decoration: none; color: inherit; display: block; }

/* A row that leads somewhere: content, then a chevron. Height comes from what is in it, so a row carrying
   only a name and one line under it stays short. */
.gorow { display: flex; align-items: center; gap: 9px; text-decoration: none; color: inherit; }
.gorow .chev { flex: 0 0 auto; color: var(--line-strong); }
.gorow .chev svg { width: 18px; height: 18px; }
.gorow:hover { background: var(--surface-2); }

/* Date block on an event row. */
.datebox { flex: 0 0 auto; width: 44px; text-align: center; }
.datebox .m { font-size: .64rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--violet-ink); }
.datebox .d { font-size: 1.3rem; font-weight: 750; letter-spacing: -.03em; line-height: 1.05; }

/* ---- Vertical timeline (payment schedule, costume production) -------------------------------------- */

.timeline { display: grid; gap: 0; }
.tl {
  position: relative;
  display: flex; align-items: flex-start; gap: 13px;
  padding: 0 0 20px 0;
}
.tl:last-child { padding-bottom: 0; }
/* The connector, drawn from each dot down to the next. */
.tl::before {
  content: ""; position: absolute; left: 11px; top: 24px; bottom: 0;
  width: 2px; background: var(--surface-3);
}
.tl:last-child::before { display: none; }
.tl.done::before { background: var(--green); }

.tl .mark {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; z-index: 1;
  background: var(--surface-3); color: var(--muted);
  border: 2px solid var(--surface);
}
.tl .mark svg { width: 13px; height: 13px; }
.tl.done .mark { background: var(--green); color: #fff; }
.tl.now .mark { background: var(--blue); color: #fff; box-shadow: 0 0 0 4px var(--blue-soft); }
.tl.due .mark { background: var(--amber); color: #fff; box-shadow: 0 0 0 4px var(--amber-soft); }
.tl.late .mark { background: var(--red); color: #fff; box-shadow: 0 0 0 4px var(--red-soft); }

.tl .body { flex: 1 1 auto; min-width: 0; padding-top: 1px; }
.tl .t { font-weight: 650; font-size: .93rem; }
.tl .s { font-size: .79rem; color: var(--muted); }
.tl.todo .t, .tl.todo .s { color: var(--muted); }
.tl .amt { font-weight: 700; font-size: .93rem; white-space: nowrap; }

/* ---- Bar chart ------------------------------------------------------------------------------------- */

.chart { display: flex; align-items: flex-end; gap: 10px; height: 180px; padding-top: 6px; }
.chart .col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: 7px; height: 100%; }
.chart .bars { flex: 1 1 auto; width: 100%; max-width: 34px; display: flex; flex-direction: column; justify-content: flex-end; border-radius: 6px; overflow: hidden; }
.chart .bars span { display: block; width: 100%; }
.chart .bars .a { background: var(--teal); }
.chart .bars .b { background: var(--gold); }
.chart .lab { font-size: .7rem; color: var(--muted); font-weight: 600; }
.legend { display: flex; gap: 14px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; font-size: .78rem; color: var(--muted); font-weight: 600; }
.legend i { width: 9px; height: 9px; border-radius: 50%; }

/* ---- Progress -------------------------------------------------------------------------------------- */

.bar { height: 8px; background: var(--surface-3); border-radius: var(--radius-pill); overflow: hidden; }
.bar > span { display: block; height: 100%; border-radius: inherit; background: var(--grad-primary); }
.bar.good > span { background: var(--green); }
.bar.warn > span { background: var(--amber); }
.bar.teal > span { background: var(--teal); }

.steps { display: flex; align-items: center; gap: 6px; }
.steps i { flex: 1 1 0; height: 4px; border-radius: var(--radius-pill); background: var(--surface-3); }
.steps i.on { background: var(--violet); }

/* ---- Forms ----------------------------------------------------------------------------------------- */

.field { display: grid; gap: 6px; }
.field > label { font-size: .85rem; font-weight: 650; color: var(--text-2); }
.field .help { font-size: .79rem; color: var(--muted); }
.field .err { font-size: .79rem; color: var(--red-ink); font-weight: 600; }

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="time"], input[type="tel"], input[type="search"],
select, textarea {
  width: 100%; padding: 13px 14px;
  font: inherit; color: var(--text);
  background: var(--field);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  outline: none;
  transition: border-color .12s ease, box-shadow .12s ease, background .12s ease;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--violet); background: var(--surface);
  box-shadow: 0 0 0 3px rgba(var(--violet-rgb), .15);
}
input[aria-invalid="true"] { border-color: var(--red); }
textarea { min-height: 110px; resize: vertical; }

input[type="color"] {
  width: 100%; height: 46px; padding: 4px;
  background: var(--field); border: 1px solid var(--line-strong); border-radius: var(--radius-sm); cursor: pointer;
}

/* Password field with a reveal toggle. */
.pw { position: relative; }
.pw input { padding-right: 46px; }
.pw button {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border: 0; border-radius: 9px;
  background: transparent; color: var(--muted); cursor: pointer;
  display: grid; place-items: center;
}
.pw button svg { width: 19px; height: 19px; }

.check { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; }
.check input { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--violet); }

.pick { display: grid; gap: 10px; }
.pick label {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px; border: 1.5px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.pick input { margin-top: 3px; accent-color: var(--violet); width: 18px; height: 18px; }
.pick label:has(input:checked) { border-color: var(--violet); box-shadow: 0 0 0 3px rgba(var(--violet-rgb), .14); }
.pick .t { font-weight: 650; }
.pick .d { font-size: .83rem; color: var(--muted); }

/* ---- Tables ---------------------------------------------------------------------------------------- */

table.data { width: 100%; border-collapse: collapse; }
table.data th { text-align: left; font-size: .73rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); padding: 11px 14px; font-weight: 700; }
table.data td { padding: 13px 14px; border-top: 1px solid var(--line); }

@media (max-width: 767px) {
  table.data, table.data tbody, table.data tr, table.data td { display: block; width: 100%; }
  table.data thead { display: none; }
  table.data tr { border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 10px; background: var(--surface); box-shadow: var(--shadow-xs); }
  table.data td { border: 0; border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; gap: 12px; }
  table.data td:last-child { border-bottom: 0; }
  table.data td::before { content: attr(data-label); font-size: .78rem; color: var(--muted); font-weight: 650; }
}

/* ---- Empty states and notices ---------------------------------------------------------------------- */

.empty { text-align: center; padding: 34px 18px; display: grid; gap: 8px; justify-items: center; }
.empty .ico { width: 50px; height: 50px; border-radius: 15px; display: grid; place-items: center; background: var(--violet-soft); color: var(--violet-ink); }
.empty h3 { font-size: 1rem; }
.empty p { color: var(--muted); font-size: .89rem; max-width: 42ch; }

.notice { padding: 12px 14px; border-radius: var(--radius-sm); font-size: .88rem; border: 1px solid; }
/* A notice that carries an action - most often the way back out of what it is reporting. */
.notice:has(.btn) { display: flex; align-items: center; gap: 10px; }
.notice .btn { flex: 0 0 auto; border-color: currentColor; color: inherit; }
.notice.info { background: var(--blue-soft); border-color: #C4D6FB; color: var(--blue-ink); }
.notice.warn { background: var(--amber-soft); border-color: #F3D79B; color: var(--amber-ink); }
.notice.bad { background: var(--red-soft); border-color: #F5C2C2; color: var(--red-ink); }
.notice.good { background: var(--green-soft); border-color: #B7E3C7; color: var(--green-ink); }

/* ---- Auth / bare pages ----------------------------------------------------------------------------- */

.bare {
  min-height: 100dvh; display: grid; place-items: center; padding: 28px var(--gutter);
  background:
    radial-gradient(760px 420px at 12% -8%, rgba(var(--violet-rgb), .16), transparent 60%),
    radial-gradient(680px 420px at 92% 4%, rgba(var(--teal-rgb), .14), transparent 58%),
    radial-gradient(620px 420px at 78% 100%, rgba(var(--gold-rgb), .12), transparent 58%),
    var(--bg);
}
.bare-card { width: 100%; max-width: 420px; display: grid; gap: 18px; }
.bare-logo { display: grid; justify-items: center; }
.bare-logo img { width: min(250px, 70vw); height: auto; }

.divider { display: flex; align-items: center; gap: 12px; color: var(--muted); font-size: .8rem; }
.divider::before, .divider::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--line); }

/* ---- Desktop --------------------------------------------------------------------------------------- */

@media (min-width: 768px) {
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .main { padding: 22px; padding-bottom: calc(var(--bottom-h) + env(safe-area-inset-bottom) + 24px); }
  .usermenu .who { display: block; }
  h1 { font-size: 1.7rem; }
}

@media (min-width: 1100px) {
  .bottomnav, .navsheet, .navscrim { display: none; }
  .mobile-logo { display: none; }
  .main { padding-bottom: 32px; margin-left: var(--sidebar-w); }
  .topbar { margin-left: var(--sidebar-w); padding: 14px 24px; }
  .grid-main { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); align-items: start; }

  .sidebar {
    display: flex; flex-direction: column;
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 50;
    width: var(--sidebar-w);
    background: var(--navy-800);
    color: #fff;
    padding: 18px 12px 12px;
  }
  .sidebar .brand { display: block; padding: 0 2px 18px; text-decoration: none; }
  .sidebar .brand img { width: 100%; height: auto; display: block; border-radius: 14px; }
  .sidebar nav { display: grid; gap: 3px; align-content: start; flex: 1 1 auto; overflow-y: auto; }
  .sidebar nav a {
    display: flex; align-items: center; gap: 12px;
    padding: 11px 12px; border-radius: var(--radius-sm);
    color: rgba(255, 255, 255, .72); text-decoration: none;
    font-size: .9rem; font-weight: 600;
  }
  .sidebar nav a svg { width: 19px; height: 19px; flex: 0 0 auto; }
  .sidebar nav a .chev { margin-left: auto; opacity: .5; }
  .sidebar nav a:hover { background: rgba(255, 255, 255, .07); color: #fff; }
  .sidebar nav a.on { background: var(--grad-primary); color: #fff; box-shadow: var(--shadow-violet); }
  .sidebar nav a.on .chev { opacity: .85; }
  .sidebar .group-label { font-size: .66rem; text-transform: uppercase; letter-spacing: .1em; color: rgba(255, 255, 255, .38); font-weight: 700; padding: 16px 12px 6px; }

  /* The organization this person is working in, pinned at the bottom. */
  .sidebar .orgcard {
    flex: 0 0 auto; margin-top: 12px;
    display: flex; align-items: center; gap: 10px;
    padding: 11px; border-radius: var(--radius);
    background: rgba(255, 255, 255, .07);
    text-decoration: none; color: #fff;
  }
  .sidebar .orgcard .n { font-size: .83rem; font-weight: 650; line-height: 1.25; }
  .sidebar .orgcard .t { font-size: .71rem; opacity: .64; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* Shown from tablet up; the phone layout has no room for it. */
.hide-phone { display: none !important; }
@media (min-width: 768px) { .hide-phone { display: inline-flex !important; } }

/* On a narrow phone a currency amount beside a 44px badge has nowhere to go, so the badge moves above
   the number rather than squeezing it onto two lines. */
@media (max-width: 460px) {
  .stat { flex-direction: column; gap: 9px; }
  .stat .k { font-size: 1.28rem; }
}

/* ---- Social sign-in -------------------------------------------------------------------------------
   Apple and Google both publish branding rules for their sign-in buttons. Apple's is solid black with
   the white mark; Google's is a white button with a light border and the four-colour mark. Neither
   takes the NosGrupo gradient, so they are separate from .btn-primary on purpose. */

.btn-apple { background: #000; color: #fff; border-color: #000; }
.btn-apple:hover { background: #151515; }
.btn-google { background: #fff; color: #1F1F1F; border-color: var(--line-strong); }
.btn-google:hover { background: var(--surface-2); }
.btn-apple, .btn-google { min-height: 48px; font-weight: 600; }

.social { display: grid; gap: 10px; }

/* Shown when a provider is designed but not yet connected, so the button is never a silent no-op. */
.social-pending {
  font-size: .76rem; color: var(--muted); text-align: center;
}

/* An SVG inside a grid/flex parent still lays out when [hidden] is set, so say it explicitly. This is
   what keeps the password reveal from showing both the open and the struck-through eye at once. */
[hidden] { display: none !important; }

/* At phone width "keep me signed in" and the forgotten-password link do not fit side by side. */
@media (max-width: 420px) {
  .auth-options { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* Compact language switcher: four fixed-width code buttons that stay on one row at phone width. */
.langbar { display: flex; justify-content: center; gap: 8px; }
.langbar form { display: contents; }
.chip-code {
  min-width: 56px;
  justify-content: center;
  letter-spacing: .04em;
  font-weight: 700;
  font-size: .8rem;
  padding: 8px 10px;
}

/* Group switcher. On desktop it is folded into the organization card at the foot of the sidebar; on a
   phone there is no sidebar, so it sits in the top bar instead. */
.show-phone { display: inline-flex !important; }
@media (min-width: 1100px) { .show-phone { display: none !important; } }

.orgcard .orgswitch {
  position: relative; flex: 0 0 auto;
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 8px;
  color: rgba(255, 255, 255, .62);
}
.orgcard .orgswitch:hover { background: rgba(255, 255, 255, .1); color: #fff; }
.orgcard .orgswitch svg { width: 16px; height: 16px; pointer-events: none; }
/* The real select sits invisibly over the chevron so the whole corner is the control. */
.orgcard .orgswitch select {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; border: 0; padding: 0;
}

/* ---- Member list ----------------------------------------------------------------------------------- */

.searchbox { position: relative; }
.searchbox svg { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; color: var(--muted); pointer-events: none; }
.searchbox input { padding-left: 40px; }

/* The closed filter bar: what is currently on, then the way to clear it, then the toggle - which sits
   hard right, where a thumb reaches without crossing the screen. */
.filterhead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.filterhead .chev { transition: transform .15s ease; }
.filterhead .chev.flip { transform: rotate(180deg); }
.filterpills { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; flex: 1 1 auto; }
/* Kept as one block so the two controls never end up on different lines from each other. */
.filteractions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; margin-left: auto; }

.filters { display: grid; gap: 8px; grid-template-columns: 1fr; }
.filters select { font-size: .875rem; }

/* A form control smaller than 16px makes iOS zoom the page in the moment it is focused. On a touch
   screen they are all 16px for that reason; padding keeps them looking the size they should. */
@media (pointer: coarse) {
  input, select, textarea, .filters select, .pickbar select { font-size: 16px; }
}


.memberrow { padding: 9px 14px; }
.memberrow-meta { flex: 0 0 auto; display: grid; gap: 4px; justify-items: end; text-align: right; }
.memberrow-meta .amt { font-weight: 700; font-size: .9rem; white-space: nowrap; }
.memberrow-meta .amt.muted { color: var(--muted); font-weight: 600; }

/* A row that is its own little form: who it is on the left, the choice on the right. Used where an
   action belongs to one person and opening a whole screen for it would be a waste of a tap. */
.assignrow { display: flex; align-items: center; gap: 9px; min-width: 0; }
/* Smaller than in a list you only read, because the row is doing a job as well as naming somebody. */
.assignrow .avatar { width: 34px; height: 34px; font-size: .74rem; }

/* The logo as a document would show it: on white, at the size it is actually printed, with the group's
   name beside it - so what you see here is what heads the invoice. */
.logopreview {
  display: flex; align-items: center; gap: 13px;
  padding: 14px; border-radius: var(--radius);
  background: #fff; border: 1px dashed var(--line-strong);
}
.logopreview img { max-height: 54px; max-width: 190px; width: auto; height: auto; object-fit: contain; }

/* An agreed amount, typed straight onto the row it belongs to. The currency sits inside the field so
   the number and its unit read as one thing. */
.ratebox { flex: 0 0 auto; display: flex; align-items: center; gap: 6px;
  padding: 6px 9px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--field); }
.ratebox .cur { font-size: .72rem; font-weight: 700; color: var(--muted); letter-spacing: .02em; }
.ratebox .rateinput {
  width: 62px; padding: 0; border: 0; background: none;
  font: inherit; font-size: .88rem; font-weight: 650; text-align: right;
  font-variant-numeric: tabular-nums;
}
.ratebox .rateinput:focus { outline: none; box-shadow: none; }
.ratebox:focus-within { border-color: var(--violet); box-shadow: 0 0 0 3px rgba(var(--violet-rgb), .15); }
.ratebox .rateinput[aria-invalid="true"] { color: var(--red-ink); }

/* A row that is ticked rather than followed. It is a button, so it answers to the keyboard and to a
   screen reader as the pressable thing it is, but it has to be stripped back to looking like a row. */
.pickrow {
  width: 100%; font: inherit; color: inherit; text-align: left; cursor: pointer;
  background: none; border: 0; border-bottom: 1px solid var(--line);
}
.pickrow:hover { background: var(--surface-2); }
.pickrow.picked { background: rgba(var(--violet-rgb), .06); }

/* Always there, so a row does not change height when it is ticked - only the tick fills in. */
.pickrow .tick {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%;
  border: 1.5px solid var(--line-strong); color: transparent;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.pickrow .tick svg { width: 13px; height: 13px; }
.pickrow.picked .tick { background: var(--violet); border-color: var(--violet); color: #fff; }

/* The seamstress is chosen once, in the bar, so the select lives on a dark surface there. */
.pickbar select {
  flex: 0 1 auto; width: auto; min-width: 92px; max-width: 32vw;
  padding: 7px 6px 7px 9px; font-size: .82rem;
  background: rgba(255, 255, 255, .14); border-color: transparent; color: #fff;
}
/* The count gives way first, so the control and the verb stay on one line on a phone. */
.pickbar > .grow { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pickbar select option { color: var(--text); }
.pickbar select[aria-invalid="true"] { border-color: #FFC9C9; }
.pickbar-err { flex: 1 0 100%; color: #FFC9C9; }


@media (min-width: 640px) { .filters { grid-template-columns: repeat(3, minmax(0, 1fr)) auto; align-items: center; } }

/* Pagination, as Laravel renders it. */
.pagination, nav[role="navigation"] ul { display: flex; gap: 6px; list-style: none; padding: 0; margin: 0; flex-wrap: wrap; justify-content: center; }
nav[role="navigation"] li span, nav[role="navigation"] li a {
  display: inline-grid; place-items: center; min-width: 38px; height: 38px; padding: 0 10px;
  border-radius: 10px; border: 1px solid var(--line); background: var(--surface);
  text-decoration: none; color: var(--text-2); font-size: .86rem; font-weight: 650;
}
nav[role="navigation"] li[aria-current] span { background: var(--violet); border-color: var(--violet); color: #fff; }
nav[role="navigation"] li[aria-disabled] span { opacity: .4; }
nav[role="navigation"] p { display: none; }

/* ---- Member profile -------------------------------------------------------------------------------- */

.facts { display: grid; grid-template-columns: auto 1fr; gap: 8px 14px; margin: 0; font-size: .86rem; }
.facts dt { color: var(--muted); font-weight: 600; white-space: nowrap; }
.facts dd { margin: 0; min-width: 0; }

.recordbox { background: var(--surface-2); border-color: var(--line-strong); margin-bottom: 16px; box-shadow: none; }
.reversebox { background: var(--red-soft); border: 1px solid #F5C2C2; border-radius: var(--radius-sm); padding: 12px; margin-top: 8px; }

.paymentrow { display: flex; align-items: flex-start; gap: 12px; }
.paymentrow.voided { opacity: .6; }
.paymentrow.voided .strong { text-decoration: line-through; }

.bar-dark { background: rgba(255, 255, 255, .22); }
.bar-dark > span { background: #fff; }

/* Button labels must never wrap. Papiamentu and Dutch labels run longer than the English ones, and a
   wrapped label turns a button into an oversized block. */
.btn { white-space: nowrap; }

/* "Back" is navigation, not an action, so it reads as a quiet link rather than a button. */
.backlink {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0; border: 0; background: none;
  color: var(--muted); font-size: .85rem; font-weight: 650;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  justify-self: start;
}
.backlink:hover { color: var(--text); }
.backlink svg { width: 16px; height: 16px; }

/* ---- Costumes ---------------------------------------------------------------------------------------
   The costume is the thing members care most about seeing, so the picture leads and the text follows. */

.costume-grid { display: grid; gap: 14px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 700px) { .costume-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .costume-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.costume-card { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.costume-figure { position: relative; aspect-ratio: 3 / 4; display: grid; place-items: center; overflow: hidden; }
.costume-figure img { width: 100%; height: 100%; object-fit: cover; display: block; }
.costume-placeholder { color: var(--muted); opacity: .55; }
.costume-placeholder svg { width: 40px; height: 40px; }
.costume-tag { position: absolute; top: 8px; left: 8px; }
.costume-body { padding: 11px 12px 12px; }

/* Thumbnails under a design: front, back, headpiece. */
.costume-strip { display: flex; gap: 6px; margin-top: 8px; overflow-x: auto; scrollbar-width: none; }
.costume-strip::-webkit-scrollbar { display: none; }
.costume-strip button {
  flex: 0 0 auto; width: 40px; height: 40px; padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: 8px; overflow: hidden; background: none;
}
.costume-strip button.on { border-color: var(--violet); }
.costume-strip img { width: 100%; height: 100%; object-fit: cover; display: block; }

.costume-thumb { width: 44px; height: 54px; object-fit: cover; border-radius: 8px; flex: 0 0 auto; }
.costume-previews { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.costume-previews img { width: 64px; height: 80px; object-fit: cover; border-radius: 8px; }

.sectionrow { padding: 9px 0; border-bottom: 1px solid var(--line); }
.sectionrow:last-child { border-bottom: 0; }
/* The costume it is set to is already named under the section, so the picker gives way to the name
   rather than the other way round. */
.section-picker { width: auto; min-width: 0; max-width: 31vw; flex: 0 1 auto; font-size: .84rem; padding: 8px 6px 8px 9px; }

/* The member own costume, shown large on their profile and dashboard. */
.mycostume { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 560px) { .mycostume { grid-template-columns: 150px 1fr; align-items: start; } }
.mycostume .figure { position: relative; aspect-ratio: 3 / 4; border-radius: var(--radius); overflow: hidden; background: var(--surface-3); display: grid; place-items: center; }
.mycostume .figure img { width: 100%; height: 100%; object-fit: cover; }
.mycostume .gallery { display: flex; gap: 8px; margin-top: 10px; overflow-x: auto; scrollbar-width: none; }
.mycostume .gallery::-webkit-scrollbar { display: none; }
.mycostume .gallery img { width: 56px; height: 70px; object-fit: cover; border-radius: 9px; flex: 0 0 auto; }

/* ---- Measurements ------------------------------------------------------------------------------------ */

/* One measurement per row: the name on the left, the box on the right, the way the paper form reads.
   Going down the screen matches going down the form on the table, which is what makes it quick to fill. */
.measure-list { display: grid; }

.measure-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 108px;
  align-items: center;
  gap: 4px 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
}
.measure-row:last-child { border-bottom: 0; }
.measure-row.has-error { background: var(--red-soft); border-radius: var(--radius-sm); padding-inline: 10px; }
.measure-row .err { grid-column: 1 / -1; }

.measure-label { display: grid; gap: 1px; min-width: 0; cursor: pointer; }
.measure-label .l { font-size: .9rem; font-weight: 650; color: var(--text); }
.measure-label .s { font-size: .74rem; color: var(--muted); }
.measure-label .req { color: var(--red); margin-left: 2px; }

/* The unit sits inside the box, so the number stays the thing being typed. */
.measure-input { position: relative; }
.measure-input input { padding: 9px 34px 9px 11px; font-size: 1rem; font-weight: 650; text-align: right; }
.measure-input .unit {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-size: .72rem; color: var(--muted); font-weight: 600; pointer-events: none;
}

/* ---- Collapsible work rows ----------------------------------------------------------------------------
   Closed, every row is the same height and carries only what is needed to scan the list. Tapping opens the
   detail. On a phone the row also slides sideways to uncover its buttons - a scroll container rather than a
   gesture library, so it works with a thumb, a trackpad and a keyboard.

   Both routes reach the same buttons; neither is the "real" one. */

[x-cloak] { display: none !important; }

/* Rows sit straight inside the card with nothing between them but their divider, so the list reads as one
   continuous thing rather than a stack of separated blocks. */
.rowwrap { border-bottom: 1px solid var(--line); }
.list > .rowwrap:last-child, .rowwrap:last-child { border-bottom: 0; }

.swipe { display: flex; }

.swipe-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 11px;
  padding: 6px 13px; min-height: 52px;
  background: var(--surface);
  cursor: pointer; text-align: left;
  border: 0; font: inherit; color: inherit;
}
.swipe-main:hover { background: var(--surface-2); }
.swipe-main:focus-visible { outline: 2px solid var(--violet); outline-offset: -2px; }

.rowhead-text { display: grid; gap: 1px; }
.rowhead-text .strong { font-size: .875rem; line-height: 1.28; }

/* The section and the status pills share one line, which is what keeps the row to two lines.
   The pills are anchored to the right edge and run leftwards (row-reverse), so the first pill in the markup
   is the rightmost one and they stack into a straight column down the list, whatever the section is called. */
.rowmeta { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: .76rem; }
.rowmeta > .small { min-width: 0; }
.pillgroup { flex: 0 0 auto; margin-left: auto; display: flex; flex-direction: row-reverse; align-items: center; gap: 5px; }
.pillgroup .pill { flex: 0 0 auto; }
.pill.tiny-pill { font-size: .64rem; padding: 0 6px; line-height: 1.6; }

.swipe-main .chev { flex: 0 0 auto; color: var(--line-strong); display: grid; place-items: center; transition: transform .15s ease; }
.swipe-main .chev svg { width: 16px; height: 16px; }
.swipe-main .chev.turned { transform: rotate(180deg); color: var(--violet); }

/* Uncovered by sliding: full-height coloured tiles, an icon over a label, flush against each other. */
.swipe-actions { flex: 0 0 auto; display: flex; align-items: stretch; }

.swipe-tile {
  flex: 0 0 auto; width: 74px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  border: 0; padding: 0; cursor: pointer; text-decoration: none;
  color: #fff; font: inherit; font-size: .68rem; font-weight: 650; line-height: 1.2; text-align: center;
}
.swipe-tile svg { width: 17px; height: 17px; flex: 0 0 auto; }
.swipe-tile.measure { background: var(--violet); }
.swipe-tile.measure:hover { background: var(--violet-ink); }
.swipe-tile.claim { background: var(--navy); }
.swipe-tile.claim:hover { background: var(--navy-700); }

.rowbody { padding: 2px 16px 15px; background: var(--surface-2); border-top: 1px solid var(--line); }
.rowbody .facts { padding-top: 12px; }
/* On a desktop the buttons already sit at the end of the row, so the opened copy would be a duplicate. */
.rowbody-actions { display: none; gap: 8px; margin-top: 14px; }

@media (max-width: 560px) {
  /* The row becomes a scroll track: the content takes the full width and the buttons sit just off the edge,
     fully hidden until the row is slid. An earlier version let the first button peek through as a hint, but
     it read as a stray coloured blob - and only showed at all when that button happened to be the coloured
     one - so the row now looks clean and tapping it open is the discoverable route to the same buttons. */
  .swipe {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }
  .swipe::-webkit-scrollbar { display: none; }

  .swipe-main { flex: 0 0 100%; scroll-snap-align: start; }
  .swipe-actions { scroll-snap-align: end; }

  .rowbody-actions { display: flex; }
}

/* A row avatar is an identifier, not a portrait - it does not need to set the row's height. */
.swipe-main .avatar { width: 31px; height: 31px; font-size: .68rem; }

/* The member number leads every row, in front of the avatar: it is how people refer to each other out
   loud, so it is the first thing on the line. A fixed width with tabular figures keeps the avatars and names
   in a straight column whether the number is 1 or 148. */
.memberno { flex: 0 0 auto; color: var(--muted); font-weight: 650; font-variant-numeric: tabular-nums; }
.memberno.lead { min-width: 24px; font-size: .78rem; text-align: right; }
.memberno.lead.sm { min-width: 20px; font-size: .72rem; }

/* ---- Group tabs -------------------------------------------------------------------------------------
   One tab per group a seamstress sews for. Each is its own page: the whole screen moves to that tenant,
   so two groups' work is never mixed into one list. The count is what is waiting for her there. */

.grouptabs { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.grouptabs::-webkit-scrollbar { display: none; }

.grouptab {
  flex: 0 0 auto; display: flex; align-items: center; gap: 9px;
  padding: 8px 14px 8px 9px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface);
  font: inherit; text-align: left; cursor: pointer; color: inherit;
  min-width: 0; max-width: 240px;
}
.grouptab:hover { background: var(--surface-2); }
.grouptab.on { border-color: var(--violet); box-shadow: 0 0 0 2px rgba(var(--violet-rgb), .16); cursor: default; }

.grouptab-text { display: grid; gap: 1px; min-width: 0; }
.grouptab-text .n { font-size: .85rem; font-weight: 650; }
.grouptab-text .t { font-size: .72rem; color: var(--muted); }
.grouptab.on .grouptab-text .t { color: var(--violet-ink); font-weight: 600; }

/* From tablet up there is no sliding, so the tiles sit permanently at the end of the row. A tall narrow
   tile is a phone shape - here it reads better as an ordinary compact button, side by side with its label. */
@media (min-width: 561px) {
  .swipe-actions { align-items: center; gap: 6px; padding: 0 14px 0 6px; }

  .swipe-tile {
    width: auto; flex-direction: row; gap: 7px;
    padding: 7px 13px; border-radius: var(--radius-sm);
    font-size: .82rem; white-space: nowrap;
  }
  .swipe-tile svg { width: 15px; height: 15px; }
}

/* ---- Text corrections (platform owner) --------------------------------------------------------------
   The English original sits above the translation so nothing is corrected blind, and an edited string is
   marked so the corrections can be found again later. */

.textrow { padding: 12px 16px; border-bottom: 1px solid var(--line); display: grid; gap: 6px; }
.textrow:last-child { border-bottom: 0; }
.textrow.is-edited { background: rgba(var(--violet-rgb), .04); }

.textrow-head { display: flex; align-items: center; gap: 8px; }
.textkey { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .72rem; color: var(--muted); }

.textrow-en, .textrow-value { display: flex; align-items: flex-start; gap: 9px; font-size: .86rem; text-align: left; }
.textrow-en { color: var(--muted); }

.textrow-value {
  width: 100%; border: 0; background: none; font: inherit; font-size: .9rem; cursor: pointer;
  padding: 6px 8px; border-radius: var(--radius-sm); color: var(--text);
}
.textrow-value:hover { background: var(--surface-2); }
.textrow-value .chev { margin-left: auto; color: var(--line-strong); }
.textrow-value .chev svg { width: 16px; height: 16px; }

.lbl {
  flex: 0 0 auto; min-width: 28px;
  font-size: .64rem; font-weight: 700; letter-spacing: .05em;
  color: var(--muted); padding-top: 3px;
}
.textrow-edit textarea { min-height: 70px; font-size: .9rem; }

/* The measure screen has three regions, and on a phone they stack in the order the work happens:
   who is being measured, then the measurements, and the costume last - worth a glance, but not what she
   needs while holding the tape. On a desktop the person and the costume sit in the right-hand column. */
@media (max-width: 1099px) {
  .measure-who { order: -1; }
  .measure-costume { order: 1; }
}

@media (min-width: 1100px) {
  .measure-layout {
    grid-template-areas: "main who" "main costume";
    align-content: start;
  }
  .measure-main { grid-area: main; }
  .measure-who { grid-area: who; }
  .measure-costume { grid-area: costume; }
}

/* ---- Phone: cards as one panel (opt-in) -------------------------------------------------------------
   A screen whose cards are sections of the same thing can ask for them to run edge to edge on a phone,
   divided by a line rather than separated by background. Opt-in per screen with `.page.flush-phone`:
   applying it everywhere flattened screens where the cards really are separate things and should keep
   their own space. From tablet up nothing changes - there is room for cards to float. */

@media (max-width: 767px) {
  .flush-phone { gap: 0; }
  .flush-phone > .stack,
  .flush-phone > .grid-main,
  .flush-phone > .grid-main > .stack { gap: 0; }

  /* Cards nested inside another card keep their own shape; only the page-level ones join the panel. */
  .flush-phone > .card,
  .flush-phone > .card-hero,
  .flush-phone > .stack > .card,
  .flush-phone > .stack > .card-hero,
  .flush-phone > .grid-main > .stack > .card,
  .flush-phone > .grid-main > .stack > .card-hero,
  .flush-phone > .grid-main > .stack > form.card {
    border-radius: 0;
    border-left: 0;
    border-right: 0;
    border-top: 0;
    border-bottom: 1px solid var(--line);
    box-shadow: none;
    margin-left: calc(var(--gutter) * -1);
    margin-right: calc(var(--gutter) * -1);
  }

  .flush-phone > .card-hero,
  .flush-phone > .stack > .card-hero,
  .flush-phone > .grid-main > .stack > .card-hero { border-bottom-color: rgba(255, 255, 255, .14); }
}

/* ---- Picking people for a seamstress ----------------------------------------------------------------
   Tiles rather than a list: splitting forty people between three seamstresses is a picking job, and a grid
   of faces is quicker to work through than rows. Tap to pick; the bar only appears once something is
   picked, so it is never in the way. */

.picktiles { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 620px) { .picktiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .picktiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.picktile {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 11px; border-radius: var(--radius);
  border: 1.5px solid var(--line); background: var(--surface);
  font: inherit; text-align: left; cursor: pointer; color: inherit;
  min-width: 0;
}
.picktile:hover { border-color: var(--line-strong); }
.picktile.picked { border-color: var(--violet); background: rgba(var(--violet-rgb), .05); }
.picktile.is-mine { border-left-width: 4px; border-left-color: var(--green); }

/* stretch, not start: `start` lets a child size to its own content, so a long name grew past the tile
   instead of being cut off by it. */
.picktile-body { display: grid; gap: 2px; min-width: 0; justify-items: stretch; }

/* The name and the number share a line. Without this the spans are inline, the name cannot shrink
   (truncate sets nowrap) and it simply runs out of the tile, pushing the number onto its own row. */
.picktile .nameline { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.picktile .nameline .strong { min-width: 0; flex: 0 1 auto; }
.picktile .nameline .memberno { flex: 0 0 auto; font-size: .76rem; }
.picktile .picktile-body > .small { min-width: 0; }

/* Your own name in the top bar, which is the way to your own settings. */
.usermenu .me { display: flex; align-items: center; gap: 9px; text-decoration: none; color: inherit;
  border-radius: var(--radius-sm); padding: 3px; }
.usermenu .me:hover { background: var(--surface-2); }

/* The address field: the name is typed, the domain after it is fixed and part of the furniture. */
.handlerow { display: flex; align-items: stretch; }
.handlerow input {
  border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: 0;
  text-align: right; font-weight: 650;
}
.handlerow .suffix, .handlerow .prefix {
  display: flex; align-items: center; padding: 0 10px;
  border: 1px solid var(--line-strong);
  background: var(--surface-2); color: var(--muted);
  font-size: .82rem; font-weight: 650; white-space: nowrap;
}
.handlerow .suffix {
  border-left: 0;
  border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm);
}
/* The path form: the fixed part comes first, and the name is typed on the end of it. */
.handlerow .prefix {
  border-right: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm);
}
.handlerow.path input {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
  border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm);
  border-left: 0; border-right: 1px solid var(--line-strong);
  text-align: left;
}

/* The group's address, shown rather than typed: NosGrupo gives it, the group reads it. A plain line, not
   a disabled input - a greyed-out box invites clicking and then refuses, which reads as broken rather
   than as "not yours to set". The label is a span here for the same reason: there is no control to label. */
.field > .label { font-size: .85rem; font-weight: 650; color: var(--text-2); }
.addressline {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  padding: 11px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.addressline svg { width: 16px; height: 16px; flex: none; color: var(--muted); }
.addressline .addr {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .92rem;
}
/* A picture in the running order, with its caption typed straight onto the row. */
.photothumb { flex: 0 0 auto; width: 54px; height: 54px; border-radius: var(--radius-sm); object-fit: cover; background: var(--surface-3); }
.captioninput {
  width: 100%; padding: 6px 8px; font-size: .86rem;
  border: 1px solid transparent; border-radius: var(--radius-sm); background: var(--surface-2);
}
.captioninput:focus { border-color: var(--violet); background: var(--surface); }
.photoacts { flex: 0 0 auto; display: flex; gap: 2px; }
.photoacts .btn { padding: 6px 7px; }
.photoacts .btn[disabled] { opacity: .3; cursor: default; }

/* ---- A group's own public page ---------------------------------------------------------------------
   Nothing of the app's chrome: whoever is reading has no account and may never have one. */

.publicbody { background: var(--surface-2); min-height: 100dvh; display: flex; flex-direction: column; }
.publicpage {
  flex: 1 1 auto; width: 100%; max-width: 680px; margin: 0 auto;
  padding: var(--gutter); display: grid; gap: 16px; align-content: start;
}
.publichero { text-align: center; padding: 26px 8px 10px; display: grid; gap: 10px; justify-items: center; }
.publiclogo { max-height: 92px; max-width: 220px; width: auto; height: auto; object-fit: contain; }
.publichero h1 { font-size: 1.9rem; letter-spacing: -.03em; line-height: 1.1; }
.publictheme { font-size: 1.05rem; color: var(--text-2); max-width: 36ch; }
.publicstory p { white-space: normal; line-height: 1.65; }

/* The slideshow is the scroll the phone already does: one picture per screen, held in place by
   scroll-snap. Swiping is native, so it feels right on a phone and needs no carousel library. */
.slideshow { display: grid; gap: 10px; }
.slides {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  gap: 10px; scrollbar-width: none;
}
.slides::-webkit-scrollbar { display: none; }
/* Square corners: a photograph is the thing itself, not a card with something in it. */
.slide {
  flex: 0 0 100%; scroll-snap-align: center; margin: 0;
  position: relative; overflow: hidden;
  background: var(--surface-3);
}
.slide img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.slide figcaption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 22px 14px 12px;
  color: #fff; font-size: .88rem; font-weight: 600;
  background: linear-gradient(to top, rgba(14, 17, 41, .72), transparent);
}
.slidedots { display: flex; justify-content: center; gap: 7px; }
.slidedots button {
  width: 7px; height: 7px; padding: 0; border: 0; border-radius: 50%;
  background: var(--line-strong); cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.slidedots button.on { background: var(--brand, var(--violet)); transform: scale(1.35); }
.publicjoin h2 { font-size: 1.05rem; margin-bottom: 4px; }
.publicfoot { padding: 22px; display: grid; place-items: center; opacity: .75; }
.publicfoot img { height: 22px; width: auto; }

/* A setting that is simply on or off, with its explanation beside it rather than under a label. */
.switchrow { display: flex; align-items: center; gap: 14px; cursor: pointer; }
.switch {
  flex: 0 0 auto; appearance: none; -webkit-appearance: none;
  width: 48px; height: 28px; border-radius: var(--radius-pill);
  background: var(--surface-3); border: 1px solid var(--line-strong);
  position: relative; cursor: pointer; padding: 0;
  transition: background .16s ease, border-color .16s ease;
}
.switch::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 22px; height: 22px; border-radius: 50%;
  background: #fff; box-shadow: var(--shadow-xs);
  transition: transform .16s ease;
}
.switch:checked { background: var(--violet); border-color: var(--violet); }
.switch:checked::after { transform: translateX(20px); }
.switch:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(var(--violet-rgb), .25); }

/* A card that folds away. The heading stays a heading - it just happens to be pressable. */
.cardtoggle {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 0; background: none; border: 0; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.cardtoggle h2 { flex: 0 1 auto; min-width: 0; }
.cardtoggle .pill { flex: 0 0 auto; }
.cardtoggle .chev {
  flex: 0 0 auto; width: 20px; height: 20px; margin-left: auto;
  color: var(--muted); transition: transform .15s ease;
}
.cardtoggle .chev.flip { transform: rotate(180deg); }

/* A run of tiles that belongs to one person, with their name above it. */
.pickgroup { margin-top: 16px; }
.pickgroup:first-of-type { margin-top: 0; }
.pickgroup-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  font-size: .76rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}
.pickgroup-head .pill { text-transform: none; letter-spacing: 0; }

/* The tick only shows once the tile is picked, so an unpicked grid stays quiet. */
.picktile .tick {
  position: absolute; top: 7px; right: 7px;
  width: 19px; height: 19px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--violet); color: #fff;
  opacity: 0; transform: scale(.7);
  transition: opacity .12s ease, transform .12s ease;
}
.picktile .tick svg { width: 12px; height: 12px; }
.picktile.picked .tick { opacity: 1; transform: scale(1); }

/* Sits above the bottom navigation on a phone, and at the foot of the page on a desktop. */
.pickbar {
  /* Sits above the navigation while it is there, and follows it down when it slides away - the same
     easing, so the two move as one thing rather than as two. */
  position: sticky; bottom: calc(var(--bottom-h) + 10px + env(safe-area-inset-bottom)); z-index: 30;
  transition: bottom .22s ease;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; border-radius: var(--radius);
  background: var(--navy); color: #fff;
  box-shadow: var(--shadow-navy);
}
.pickbar .btn-quiet { color: rgba(255, 255, 255, .78); }
.pickbar .btn-ghost { background: rgba(255, 255, 255, .12); border-color: transparent; color: #fff; }

.shell.nav-away .pickbar { bottom: calc(10px + env(safe-area-inset-bottom)); }

@media (min-width: 1100px) { .pickbar { bottom: 16px; } }

/* Back to the top, on pages long enough that scrolling all the way would be a nuisance. It lifts clear of
   the bottom navigation whenever that bar is showing, so the two never sit on top of each other. */
.totop {
  position: fixed; right: 16px; z-index: 44;
  bottom: calc(16px + env(safe-area-inset-bottom));
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--line); background: var(--surface); color: var(--text-2);
  box-shadow: var(--shadow-lift); cursor: pointer;
  transition: bottom .22s ease;
}
.totop:hover { background: var(--surface-2); }
.totop svg { width: 20px; height: 20px; }
.totop.above-nav { bottom: calc(var(--bottom-h) + 14px + env(safe-area-inset-bottom)); }

@media (min-width: 1100px) {
  .totop, .totop.above-nav { bottom: 20px; right: 20px; }
}
