/* KRONKER HOME - Household Task Manager Styles */

:root {
  /* Colors */
  --primary-orange: #f97316;
  --secondary-orange: #ea580c;
  --success-green: #10b981;
  --warning-yellow: #f59e0b;
  --danger-orange: #f97316;
  --urgent-red: #ef4444;
  --gray-50: #f9fafb;
  --gray-100: #f3f4f6;
  --gray-200: #e5e7eb;
  --gray-300: #d1d5db;
  --gray-400: #9ca3af;
  --gray-500: #6b7280;
  --gray-600: #4b5563;
  --gray-700: #374151;
  --gray-800: #1f2937;
  --gray-900: #111827;
  --white: #ffffff;
  --black: #000000;

  /* Spacing */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;
  --space-16: 4rem;

  /* Typography */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;

  /* Shadows */
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);

  /* Borders */
  --border-radius: 0.5rem;
  --border-radius-lg: 0.75rem;
  --border-radius-xl: 1rem;

  /* Animations */
  --transition-fast: 150ms ease-in-out;
  --transition-normal: 300ms ease-in-out;
  --transition-slow: 500ms ease-in-out;
}

/* Reset & Base */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  /* THE GREEN BASE LAYER (the gospel's bottom layer, made real July 11, 2026 -
     intended as the standard on all screens). The canvas
     under everything is brand green; pull-to-refresh and rubber-band
     overscroll are where a person actually sees it. The gray they read on is
     the BODY, which now guarantees full viewport coverage (see the base-layer
     block at the end of this sheet), so inside the window nothing changes. */
  background: #26a11c;
  min-height: 100vh;
}

body {
  font-family: var(--font-sans);
  line-height: 1.6;
  color: #ffffff;
  background: #1a1a1a; /* neutral - matches BILLS/FEED */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}

/* App Container */
.app-container {
  min-height: 100vh;
  max-width: 1000px;
  margin: 0 auto;
  padding: 0;
}

/* Header */
.header {
  background: #1a1a1a; /* neutral - matches BILLS/FEED */
  padding: 40px 0 20px 0;
  margin-bottom: 0;
}

.header-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 42px;
}

.app-title {
  font-size: 1.75rem;
  font-weight: 800;
  color: #ffffff;
  letter-spacing: -0.025em;
}

/* The three controls sit just above the black rectangle.
   The gear button is the tallest thing in the header row (8px + 29px + 8px), so
   centering left the glyphs ~28px above the black box. Nudged down visually
   rather than by changing the header's height, so the logo does not move and
   nothing reflows. The dropdowns anchor to their own containers and follow. */
/* All three dropdowns hang from ONE horizontal line.
   Each panel is positioned from its own button's wrapper, and the buttons are
   32px, 45px and 40px tall - so identical `top` values landed three different
   heights. Stretching the wrappers to the row height makes 100% mean the same
   thing for all three. Each wrapper still centers its icon, so nothing moves.
   The wrappers are switched to flex further down, where they are defined. */
.header-actions {
  display: flex;
  align-items: stretch;
  gap: var(--space-4);
  position: relative;
  top: 14px;
}

/* Same 14px nudge as .header-actions, so the wordmark, the app icon and the
   three controls all share one horizontal centerline.
   align-items:center already aligned their middles - nudging only the right
   side had broken that. */
.logo-section {
  position: relative;
  display: flex;
  align-items: center;
  top: 14px;
}

/* THE APP NAME SITS ABOVE THE KRONKER WORDMARK, right-aligned to its edge.
   RULED July 9, 2026: words above the wordmark - correct and final.
   `bottom: 100%` is what puts it there; `.logo-section { top: 14px }` is what makes
   the room for it. It was flipped from below earlier the same day - HOME's comment was
   updated at the flip, these six were not, and for a while the code and the comment
   disagreed. FEED's header was built to the CODE. Do not "fix" this back to top:100%. */
.brand-stack {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.app-subhead {
  margin-left: 10px; /* inline to the RIGHT of the wordmark, same 11px */
  position: relative;
  top: 6px;   /* FOOT-ALIGN to the wordmark's text foot - eyeballed; the logo PNG carries ~10% blank below the letters, so centred sat too high. Tune this one number. */
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #878787;
  white-space: nowrap;
  pointer-events: none;
}

/* THE WORDMARK'S SIZE LIVES HERE (Aug 30, 2026). The markup carries an
   inline height: 32px as well, but the brand painter (kr-brand.js)
   clears that inline pin on its decouple road, and with no height in
   this stylesheet the 2506x576 PNG painted at natural size across the
   header. The stylesheet owns the size; the inline pin is a courtesy. */
.logo-section img[src="icons/kronker-logo.png"] {
  height: 32px;
  width: auto;
  filter: drop-shadow(0 0 10px rgba(200, 200, 200, 0.75));
}

.logo-section img[src="icons/HOME.png"] {
  height: 32px;
  width: auto;
  margin-left: 10px;
  filter: drop-shadow(0 0 10px rgba(156, 163, 175, 0.6));
}

.sync-btn {
  background: none;
  border: none;
  padding: var(--space-2);
  margin-left: 10px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.6);
  transition: all var(--transition-fast);
  display: flex;
  align-items: center;
  justify-content: center;
}

.sync-btn:hover {
  color: rgba(255, 255, 255, 0.9);
}

.sync-btn.syncing .gear-icon {
  animation: spin 1s linear infinite;
}

.sync-btn.success {
  color: var(--success-green);
  border-color: var(--success-green);
}

/* On success the gray gear is swapped for the green art
   gear image (icons/gear-green.png) and keeps spinning; errors tint red. */
.sync-btn.success .gear-icon {
  animation: spin 1s linear infinite;
  content: url('icons/gear-green.png');
}

.sync-btn.error .gear-icon {
  filter: brightness(0) invert(50%) sepia(100%) saturate(600%) hue-rotate(320deg) brightness(1.1);
}

.sync-container {
  position: relative;
  display: flex;        /* spans the header row so its dropdown shares the common top */
  align-items: center;
}

/* Every dropdown panel is drawn the same. The gear was
   the odd one out: a 20% border where the picker and avatar use 10%, a
   hard-coded 8px radius where they use the token (same value, different words),
   and a heavier shadow. All three now match. */
.sync-dropdown {
  position: absolute;
  top: 100%;
  margin-top: 8px; /* the 8px drop, matching .picker-dropdown */
  right: 0;
  background: rgba(26, 26, 26, 0.95); /* neutral panel - matches BILLS/FEED */
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--border-radius);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
  width: 238px; /* one width shared with the avatar dropdown (280 -15% by the word) */
  z-index: 1000;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-10px);
  transition: all var(--transition-fast);
}

.sync-dropdown.show {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.sync-item {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  color: rgba(255, 255, 255, 0.8);
  cursor: pointer;
  transition: all 0.2s ease;
}

.sync-item:last-child {
  border-bottom: none;
}

.sync-item:hover {
  background: rgba(255, 255, 255, 0.1);
}

.sync-item span {
  font-size: 0.9rem;
  font-weight: 500;
}

/* Divider lines between EXPORT, IMPORT and AI, drawn
   exactly as the picker dropdown draws its own: a 5%-white hairline, dropped
   on the last section so the panel does not end on a rule. */
.sync-section {
  padding: 8px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.sync-section:last-child {
  border-bottom: none;
}

/* A line of plain English under each section heading.
   Left edge lines up with the title and the buttons; sentence case, not caps,
   because it is a sentence. The title's bottom padding is dropped to 2px so the
   description reads as belonging to it rather than floating between sections. */
.sync-section-desc {
  font-size: 0.84rem; /* descriptor type, +20% by the word */
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.45);
  padding: 0 16px 10px 16px;
}

.sync-section-title {
  font-size: 0.8rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.6);
  padding: 12px 16px 2px 16px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.sync-section:first-child { padding-top: 0; }
.sync-section:first-child .sync-section-title {
  /* the panel's first line starts exactly where the avatar's does:
     12px down, 16px in */
  padding-top: 12px;
}

.sync-checkbox-group {
  padding: 0 16px 8px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.sync-checkbox-label {
  display: flex;
  align-items: center;
  cursor: pointer;
  margin: 0;
}

.sync-checkbox-label input[type="checkbox"],
.sync-checkbox-label input[type="radio"] {
  width: auto;
  margin-right: 8px;
  transform: scale(1.1);
  cursor: pointer;
  background: #000000 !important;
  border: 2px solid rgba(255, 255, 255, 0.2) !important;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border-radius: 3px;
  width: 16px;
  height: 16px;
  position: relative;
}

.sync-checkbox-label input[type="radio"] {
  border-radius: 50%;
}

.sync-checkbox-label input[type="checkbox"]:checked {
  background: #26a11c !important;
  border-color: #26a11c !important;
}

/* A ticked RADIO keeps the unticked button exactly - black fill, faint gray
   ring - and only its centre goes brand green.
   Checkboxes above are unchanged: they still fill green and show a white tick. */
.sync-checkbox-label input[type="radio"]:checked {
  background: #000000 !important;
  border-color: rgba(255, 255, 255, 0.2) !important;
}

.sync-checkbox-label input[type="checkbox"]:checked::after {
  content: '✓';
  position: absolute;
  color: white;
  font-size: 12px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.sync-checkbox-label input[type="radio"]:checked::after {
  content: '';
  position: absolute;
  width: 6px;
  height: 6px;
  background: #26a11c; /* brand green - the only thing that changes when ticked */
  border-radius: 50%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.sync-checkbox-label span {
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.8);
  user-select: none;
}

.export-password-field input {
  width: calc(100% - 16px) !important;
  margin: 8px 8px !important;
}

.sync-divider {
  height: 1px;
  background: rgba(255, 255, 255, 0.15);
  margin: 8px 0;
}

/* The gear dropdown's buttons wear the site's gray, not a color.
   Solid #1a1a1a - the shared background of every site - which
   sits a shade lighter than the panel behind it (rgba(26,26,26,0.95) over the
   black rectangle), so the button reads as a button. Light gray border, caps.
   The text was green too; it goes gray with the rest. */
.sync-action-btn {
  /* Dressed as the avatar dropdown's buttons (SIGN OUT / CANCEL):
     solid gray, no border, rounded, centered caps - one button
     family across both header dropdowns. */
  width: calc(100% - 32px);
  background: #333333;
  border: none;
  color: #ffffff;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--border-radius);
  cursor: pointer;
  font-size: 0.825rem; /* the standard button type, +10% by the word */
  font-weight: 600;
  text-align: center;
  text-transform: uppercase;
  transition: background var(--transition-fast);
  margin: 8px 16px 12px 16px;
}

.sync-action-btn:hover {
  background: #555555;
}

/* User Avatar */
.user-avatar {
  position: relative;
  display: flex;        /* spans the header row so its dropdown shares the common top */
  align-items: center;
}

.avatar-btn {
  background: rgba(255, 255, 255, 0.1);
  border: none;
  border-radius: 50%;
  width: 32px;
  height: 32px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.4);
  transition: all var(--transition-fast);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.avatar-btn:hover {
  color: rgba(255, 255, 255, 0.7);
  background: rgba(255, 255, 255, 0.15);
}

.avatar-btn.authenticated {
  color: #10b981;
  background: rgba(255, 255, 255, 0.1);
}

.avatar-dot {
  display: none;
}

.avatar-dropdown {
  position: absolute;
  top: 100%;
  margin-top: 8px; /* the 8px drop, matching .picker-dropdown */
  right: 0;
  background: rgba(26, 26, 26, 0.95); /* neutral panel - matches BILLS/FEED */
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--border-radius);
  backdrop-filter: blur(10px);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
  width: 238px; /* one width shared with the gear dropdown (280 -15% by the word) */
  z-index: 1000;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-10px);
  transition: all var(--transition-fast);
}

.avatar-dropdown.show {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.avatar-item {
  padding: var(--space-3) var(--space-4);
  color: rgba(255, 255, 255, 0.8);
  cursor: pointer;
  transition: background var(--transition-fast);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.avatar-item:last-child {
  border-bottom: none;
}

.avatar-item:hover {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 1);
}

/* SIGN OUT reads as a button like every other: the
   site's gray, a light gray border, centered caps. It stays a <div> because
   app.js toggles it with style.display = 'block'. The separator line above it
   is .avatar-account-info's own bottom border, which already sits between this
   and CANCEL SUBSCRIPTION. Targeted by id so it outranks .avatar-item:hover. */
#logoutItem,
#loginItem {
  margin: 12px 16px;
  padding: var(--space-3) var(--space-4);
  background: #333333;
  border: none;
  border-radius: var(--border-radius);
  color: #ffffff;
  font-size: 0.85rem;
  font-weight: 600;
  text-align: center;
  text-transform: uppercase;
  cursor: pointer;
  transition: background var(--transition-fast);
}

#logoutItem:hover,
#loginItem:hover {
  background: #555555;
}

/* .avatar-item span forces 0.875rem, which was overriding the 0.75rem set on
   the container - that is why SIGN OUT read larger than CANCEL SUBSCRIPTION,
   a plain <button> with no span. Sign In keeps the original size. */
#logoutItem span,
#loginItem span {
  font-size: 0.825rem; /* the standard button type */
}

.avatar-item span {
  font-size: 0.875rem;
}

.avatar-account-info {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.account-email {
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.875rem;
  margin-bottom: var(--space-2);
  font-weight: 500;
}

.account-plan {
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.75rem;
  margin-bottom: var(--space-3);
}

/* The small green dot on the plan's left: the mark that this plan is
   the CURRENT one. */
.account-plan::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #26a11c;
  margin-right: 6px;
}

/* Wears the same face as the gear dropdown's buttons - no red.
   The site's gray, a light gray border, caps. */
.cancel-subscription-btn {
  display: block;
  width: 100%;
  background: #333333;
  color: #ffffff;
  border: none;
  border-radius: var(--border-radius);
  padding: var(--space-3) var(--space-4);
  /* THE STANDARD HEIGHT: every dropdown button wears 0.75rem type in
     this padding - CANCEL and SIGN OUT identical, the gear's buttons
     and the hidden SIGN IN in the same family. */
  font-size: 0.825rem;
  font-weight: 600;
  cursor: pointer;
  text-transform: uppercase;
  transition: background var(--transition-fast);
}

.cancel-subscription-btn:hover {
  background: #555555;
}

/* Main Content */
.main-content {
  background: #000000; /* unified solid black — matches BILLS/FEED */
  margin: 0 40px 40px 40px;
  padding: 0 20px 40px 20px;
  border-radius: 0 0 12px 12px;
  box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.8);
  min-height: calc(100vh - 200px);
  overflow: hidden;
  position: relative;
}

/* Date Header Row */
/* THE TICKETING | HISTORY RULE: the same fading hairline the
   FEED draws between its date and the collapse toggle. flex:1 auto eats the gap so
   HISTORY stays right; the gradient holds 0.3 white then fades to 0 alpha (never to
   black - the R2.33 lesson) just before HISTORY. Copied byte-for-byte from FEED .head-rule. */
.head-rule { flex: 1 1 auto; height: 1px; background: linear-gradient(to right, rgba(255, 255, 255, 0.3) 80%, rgba(255, 255, 255, 0) 100%); margin-left: 14px; border-radius: 1px; }   /* line kept put; the row moved up 2px instead so word+line+button share one centre */
.head-rule + .history-btn { margin-left: 16px; }

.date-header-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 24px 0 10px 4px; /* -2px top lifts TICKETING+HISTORY onto the line; right 0 = HISTORY flush to the card edge */
}

.archive-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 16px;
  margin-bottom: var(--space-4);
  padding: 0 2px;
}

.archive-title {
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.9rem;
  font-weight: 600;
}

.back-btn {
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.8);
  padding: 8px 6px;
  cursor: pointer;
  font-size: 0.8rem;
  transition: all 0.2s;
}

.back-btn:hover {
  color: white;
}

.date-header {
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.9rem;
  font-weight: 600;
}

/* THE DATE IS A DOOR: date + triangle open FEED VIEW.
   The button is a bare button - no chrome - so the date looks exactly as it always did
   until you hover it. The date and its caret take ONE colour from the button, the same
   way HISTORY and its caret do, so the pair can never drift apart.
   The menu hangs off .date-menu (position:relative) and opens LEFT-aligned, under the
   date, because the date sits at the left edge of its row. The picker's menu is the same
   panel opening right-aligned; only that one line differs. */
.date-menu { position: relative; }

.date-btn {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  color: rgba(255, 255, 255, 0.7);
  transition: color var(--transition-fast);
}
.date-btn:hover { color: white; }
.date-btn:hover .date-header { color: inherit; }

.date-caret { font-size: 0.6rem; line-height: 1; }

.date-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  background: rgba(26, 26, 26, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--border-radius);
  backdrop-filter: blur(10px);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
  min-width: 160px;
  z-index: 1000;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-10px);
  transition: all var(--transition-fast);
  margin-top: 8px;
}
.date-dropdown.show {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.date-item {
  display: block;
  padding: var(--space-3) var(--space-4);
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  transition: background var(--transition-fast);
}
.date-item:hover { background: rgba(255, 255, 255, 0.1); color: white; }

/* Bills Grid */
.bills-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-8);
}

/* Bill Card */
/* ONE COLOUR ON EVERY CARD, AND IT IS BILLS'
.
   #1a1a1a is what a default BILLS ticket actually renders - .bill-card.future and .soon
   both paint it. BILLS' own base rule carries rgba(26,26,26,0.9), which composites to
   rgb(23,23,23) over the black backer, and NOBODY EVER SEES IT: every bills card wears an
   urgency class that repaints the colour opaque. The colour a person sees is this one.

   IT REPLACED #54545680, this app's translucent wash. Exactly four rules carried it -
   .bill-card and its .future / .week / .soon twins - and all four read #1a1a1a now, so
   every ticket in this app is one colour in every state. Nothing else in this sheet ever
   used that hex; that was checked before the swap, not after.

   BILLS, FEED, SOCIAL AND BIG DAYS KEEP THEIR OWN COLOURS. The ruling names the four
   apps that change and the four that do not. Do not "finish the job".

   THE CAP IS NOT THE COLOUR. Colour and image stay separate declarations here, exactly as BILLS warns:
   a `background:` shorthand on an urgency rule would silently wipe the cap out.
   Never collapse them. */
.bill-card {
  /* ONE CAP PER APP: "reduce each to 1... the same one that
     they see in feed for that same color". The cap used to be picked by the card's AREA
     or CATEGORY - eight PNGs, eight colours, a different endcap for KITCHEN than for
     INTERIOR. It told a person nothing they needed. Every ticket in this app now wears
     the app's own cap: the very one the FEED has always painted it with (GRAY.png).
     SCHOOL alone keeps its many colours, because there the colour is the SUBJECT.

     app.js still stamps color-<x> on each card from getColorForArea()/Category(). Those
     classes are now deliberately UNSTYLED, and the colour is still stored and synced -
     so the colours can come back one day without a data migration.

     background-COLOR, never the `background` shorthand. The shorthand resets
     background-image and would take this cap straight back out. Same law as FEED
     (R2.18). Every rule below that repaints a card had to learn it too. */
  background-color: #1a1a1a; /* BILLS' default - ALL cards, every state. See above. */
  background-image: url('images/GRAY.png');
  background-repeat: no-repeat;
  background-position: right center;
  background-size: auto 100%;   /* natural 32x180 - NEVER scaled */
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  padding: 12px 16px 16px 16px;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;
  height: 180px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  box-sizing: border-box;
  overflow: hidden;
}

.bill-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
}

/* Urgency Colors - Full Card Backgrounds */
.bill-card.future { background-color: #1a1a1a; border: 2px solid #6b7280; }
.bill-card.week { background-color: #1a1a1a; border: 2px solid #6b7280; }
.bill-card.soon { background-color: #1a1a1a; border: 2px solid #6b7280; }
.bill-card.urgent { border: 2px solid #6b7280; }
.bill-card.urgent .bill-due,
.bill-card.urgent .bill-actual-date { color: #ffffff; }

.bill-card.overdue { border: 2px solid #6b7280; }
.bill-card.overdue .bill-due,
.bill-card.overdue .bill-actual-date { color: #ffffff; }

.bill-card.completed {
  border: 2px solid #6b7280;
  opacity: 0.6;
}

/* Color Classes: RETIRED July 10, 2026. TWO families lived here: the plain
   .bill-card.color-<x> (8) AND .bill-card.<tier>.color-<x> (24, one per urgency
   tier), which out-specified the plain ones and re-asserted the cap with
   !important. Removing only the first family changes NOTHING on screen - the
   second still paints. Both are gone. Eight caps became one (see .bill-card).
   The color-<x> class is still emitted by app.js and matches nothing here. */

.bill-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: var(--space-4);
  margin-top: 0;
}

.bill-name {
  font-size: 1.8rem;
  font-weight: 700;
  color: #ffffff;
  margin-bottom: var(--space-1);
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
  z-index: 2;
  top: 18px;
  line-height: 1.1;
}

.bill-description {
  font-size: 0.9rem;
  font-weight: 500;
  color: #ffffff;
  margin-top: 8px;
  position: relative;
  z-index: 2;
}

/* BUILDER MODE. When home is building a kronk's tickets (body.building):
   the AREA dropdown is gone - a kronk ticket has no category, it belongs
   to the kronk. The page stacks like EVERY room: full tooltip, the dock,
   THE KRONK CARD being built (the k- card, full control panel; clicking
   it opens the identity panel), then TICKETING. No save control exists -
   everything saves as it changes; every door out is a clean exit. */
#builderKronkCard { display: none; }
body.building #builderKronkCard { display: block; margin-top: 14px; }
body.building #areaFormGroup { display: none; }

/* THE TRUNCATED DESCRIPTION + MORE. The card shows a
   short preview; MORE opens the whole text in a popup. */
.card-desc {
  margin-top: 2px;
  font-size: 0.9rem;
  color: rgba(255, 255, 255, 0.85);
  line-height: 1.3;
  /* The nugget preview: a column up to 340px wide that wraps and clamps to
     two lines. max-width caps it; on a narrow device (down to a watch) the
     card is smaller, so the column shrinks to fit on its own. The full text
     still lives in the DOM (and the data) - MORE opens it. */
  max-width: 340px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
/* The text column and its MORE stack vertically: description on top, MORE on
   its own line beneath it, left-justified. MORE is revealed only on a real
   overflow past the 2-line clamp, so whenever it shows the box is a full two
   lines - MORE therefore lands in the same spot under every card that has it. */
.card-desc-row { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; margin-bottom: 10px; }
.desc-more {
  /* Hidden until renderBills measures a real 2-line overflow, then shown. */
  display: none;
  flex: none;
  color: #ffffff; /* always solid white */
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  cursor: pointer;
  white-space: nowrap;
  /* THE FINGER TARGET: the tap zone runs 16/18px past the word on
     every side, in real pixels - finger size does not scale with the
     screen - pulled back by matching negative margins so the text
     does not move. A near-miss now lands on MORE instead of falling
     through to the card's edit road. */
  padding: 16px 18px;
  margin: -16px -18px;
}
.desc-more.desc-more--on { display: inline-block; }
.desc-more:hover { text-decoration: underline; }
/* MORE tapped (card-open): the card's own height animates open (see the MORE
   handler); this releases the 2-line clamp so the full text flows inside it. */
.card-desc.desc-open { display: block; -webkit-line-clamp: unset; }
/* THE COMPACT (collapse) ICON rides beside the title, but only once the card is
   open - it is how you close it. MORE (the opener) hides while open, so the two
   controls never both show. */
/* The close twin wears the same finger target as MORE. */
.desc-compact { display: none; cursor: pointer; vertical-align: middle; opacity: 0.75; padding: 16px 18px; margin: -16px -18px; }
/* Builder: the closer sits exactly where the opener sits. */
body.kr-builder .desc-compact { position: relative; top: -2px; }
.desc-compact img { display: block; }
.desc-compact:hover { opacity: 1; }
.bill-card.card-open .desc-compact { display: inline-block; }
.bill-card.card-open .desc-more { display: none; }

/* The kronk ticket-count that rides beside the recurring icon on an
   evergreen's single dock card - how many tickets live in that kronk. */
.kronk-count {
  margin-left: 3px;
  font-weight: 700;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.9);
  vertical-align: middle;
}

/* The evergreen shelf card's dock control + ticket-count/recur badge ride
   TOGETHER in a bottom-left row (founder, July 13, 2026: free the top-right
   corner for the two-state design). The row anchors bottom-left; the control and
   the count flow inside it, so the count sits beside whichever control shows (the
   plus or the five dots), whatever its width. The go-live wheel stays bottom-right. */
.kl-ever-corner {
  position: absolute; left: 16px; bottom: 12px; z-index: 2;
  display: flex; align-items: center; gap: 10px;
}
/* The control's own CSS pins it to the corner; inside the row it just flows. */
.kl-ever-corner .kl-livelight { position: static; left: auto; bottom: auto; }
/* The plus circle stays a POSITIONING CONTEXT (relative, not static) so its
   +'s ::before/::after centre ON the circle, not on the row - static made the
   plus fall off the circle. */
.kl-ever-corner .kl-dockplus { position: relative; left: auto; bottom: auto; }
.kl-ever-count {
  display: inline-flex; align-items: center; gap: 3px;
  font-weight: 700; font-size: 0.85rem; color: rgba(255,255,255,0.92);
}
.kl-ever-count img { display: block; }

/* THE DESCRIPTION POPUP WAS RETIRED (founder, July 13, 2026): MORE now expands
   the card in place instead of opening a popup. Its markup + handlers went with
   it; the .modal base stays for the other panels that use it. */

/* A KRONK CARD'S LINK ICON (July 11, 2026) - bills' .card-link, ported so the
   favicon sits inline beside the card name with a comfortable tap padding. */
.card-link {
  display: inline-flex;
  align-items: center;
  padding: 4px;
  margin: -4px;
  border-radius: 50%;
  text-decoration: none;
}
.payment-favicon {
  border-radius: 3px;
  opacity: 0.8;
}

.bill-name a {
  padding: 8px;
  margin: -8px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

.bill-amount {
  font-size: 1rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.8);
  font-family: var(--font-sans);
  text-transform: uppercase;
  position: relative;
  z-index: 2;
}

.bill-icon {
  position: absolute;
  top: 50%;
  right: var(--space-6);
  transform: translateY(-50%);
  opacity: 0.85;
  z-index: 1;
}

.bill-date-section {
  position: absolute;
  bottom: var(--space-3);
  left: var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  z-index: 2;
}

.bill-due {
  font-size: 0.84rem;
  color: #ffffff;
  margin-bottom: 2px;
}

.bill-actual-date {
  font-size: 1.17rem;
  color: #ffffff;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
}

.recurring-indicator {
  opacity: 0.7;
  display: flex;
  align-items: center;
  position: relative;
}

.recurring-indicator img {
  display: block;
}

.autopay-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 6px;
  height: 6px;
  background: #10b981;
  border-radius: 50%;
  pointer-events: none;
}

.payment-link-indicator {
  opacity: 0.7;
  display: flex;
  align-items: center;
  margin-left: 4px;
}

.payment-link-indicator img {
  display: block;
}

.edit-indicator {
  position: absolute;
  top: 12px;
  right: 32px;
  opacity: 0.6;
  transition: opacity var(--transition-fast);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.bill-content:hover .edit-indicator {
  opacity: 1;
}

.bill-brandmark {
  position: absolute;
  bottom: var(--space-3);
  right: 32px;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--transition-fast);
}

.bill-brandmark:hover {
  opacity: 1;
}

/* The builder is a workroom, not a tracker: its tickets wear the
   brandmark as identity only - dim, no pointer, no click. */
.bill-brandmark.kr-inert {
  opacity: 0.25;
  cursor: default;
  pointer-events: none;
}

.bill-brandmark img {
  display: block;
  width: 32px;
  height: 32px;
  object-fit: contain;
}

/* The TOOLTIP card's completion icon: 48px - half again
   the 32px a real ticket wears - sitting 12px off the bottom and HALF that off
   the right, so it tucks toward the corner. The right cushion is written as half
   the bottom so the relationship survives a change to the spacing scale.
   Scoped to .tooltip-card - the same class draws the completion icon on every
   real ticket, and those keep their 32px size and their 32px right inset,
   which is what clears the cap on BILLS and BIG DAYS. */
.tooltip-card .bill-brandmark {
  bottom: var(--space-3);
  right: calc(var(--space-3) / 2);
  opacity: 1;
}

.tooltip-card .bill-brandmark img {
  width: 48px;
  height: 48px;
  filter: brightness(0) invert(1);
  transition: filter var(--transition-fast);
}

.tooltip-card .bill-brandmark:hover img {
  filter: brightness(0) invert(1) drop-shadow(0 0 10px #FFFFFF);
}

.bill-content {
  cursor: pointer;
  position: relative;  /* stacks the text above the kr watermark below it in DOM */
  flex: 1;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  justify-content: space-between;
  gap: 12px;
  transition: all var(--transition-fast);
}

/* ── THE UNIFIED CARD: CONTENT LEFT, KRONKER RIGHT ──────
   TITLE = the item name at this app's own size. SECTION-WORD = the type. No watermark,
   no pencil. .card-relative is styled here for the apps that show it. */
.card-left { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.card-title { font-size: 1.53rem; font-weight: 700; color: #ffffff; line-height: 1.1; }
/* TINY, ABOVE THE DATE. The source-kronk line - small,
   muted, uppercase - sits just over the date at the card floor. */
/* White at 60%, never gray: these lines sit on kronk-coloured fields,
   and translucent white reads on any colour where a fixed gray dies. */
.card-section { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255, 255, 255, 0.6); margin-bottom: 3px; }
.card-when { margin-top: auto; }
.card-relative { font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; color: #9ca3af; line-height: 1; margin-bottom: 3px; }
.card-relative.is-today   { color: #26a11c; }
.card-relative.is-overdue { color: #ef4444; }
.card-time { font-size: 1.05rem; color: rgba(255, 255, 255, 0.6); font-weight: 500; display: flex; align-items: center; gap: 8px; line-height: 1; }
.card-bucket { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; margin-right: 16px; }
.card-bucket-icon { width: 34px; height: 34px; display: block; }

.bill-content:hover {
  opacity: 0.9;
}

/* Add Card */
.add-card {
  border: 2px dashed rgba(255, 255, 255, 0.2);
  background: rgba(26, 26, 26, 0.9);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 180px;
  color: rgba(255, 255, 255, 0.6);
  transition: all 0.3s ease;
  text-align: center;
}

.add-card[style*="display: none"] {
  display: none !important;
}

.add-card:hover {
  border-color: rgba(255, 255, 255, 0.4);
  color: rgba(255, 255, 255, 0.9);
  background: rgba(26, 26, 26, 1);
}

.add-icon {
  margin: 0; opacity: 0.6; transition: opacity var(--transition-fast); display: flex; align-items: center; justify-content: center;
  position: relative; z-index: 2; /* above the band layer */
}

.add-icon img { display: block; width: 48px; height: 48px; object-fit: contain; }

.add-card:hover .add-icon {
  opacity: 1;
}

.add-text {
  font-weight: 600;
  font-size: 0.875rem;
}

/* Tooltip Card in Grid */
.tooltip-card { background: linear-gradient(to bottom right, #363636, #878787) !important; border: 2px solid rgba(255, 255, 255, 0.2) !important; box-shadow: none !important; }
.tooltip-close { position: absolute; top: 8px; right: 12px; background: none; border: none; color: #FFFFFF; cursor: pointer; font-size: 18px; }
.tooltip-title { font-size: 1.53rem; font-weight: 700; line-height: 1.1; color: #FFFFFF; text-transform: uppercase; }
.tooltip-text { font-size: 0.9rem; line-height: 1.4; color: #FFFFFF; margin-top: 2px; margin-bottom: 12px; }
.tooltip-action { background: none; border: none; color: #FFFFFF; padding: 0; cursor: pointer; font-size: 0.85rem; font-weight: 600; transition: all 0.2s; }
.tooltip-action:hover { background: rgba(255, 255, 255, 0.3); }

/* HISTORY and its caret both read at 40%. The caret is
   part of the button's own text, so one color governs the pair. It was 80%; 60%
   barely moved on a dark ground, so it went to 40%. Hover still hits full white. */
.history-btn { background: none; border: none; color: rgba(255,255,255,0.4); padding: 8px 0; margin: -8px 0; cursor: pointer; font-size: 0.8rem; transition: all 0.2s; }
.history-btn:hover { color: white; }

.pagination { display: flex; justify-content: center; gap: 12px; margin-top: 20px; }
.pagination button { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: white; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
.pagination button:disabled { opacity: 0.5; cursor: not-allowed; }

/* Modal */
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  backdrop-filter: blur(4px);
}

.modal.show {
  display: flex;
}

/* THE SECTION ICON above the add/edit popup: the app's own icon rides a
   shell in NORMAL FLOW, centered over the box - and the overlay
   scrolls, so on a short viewport nothing clips off the top: icon and
   popup stay reachable. Auto margins on the shell beat the overlay's
   centring exactly when space runs out (the scroll-safe pattern). */
.modal { overflow-y: auto; }
.modal-shell { display: flex; flex-direction: column; align-items: center;
               /* 300px at 320-and-up (300/320 = 93.75vw exactly), scaling
                  in proportion below the floor like the photograph rule. */
               width: min(300px, 93.75vw); max-width: min(300px, 93.75vw);
               margin: auto; padding: 16px 0; }
.modal-app-icon { height: 40px; width: auto; margin: 0 0 10px; }
.modal-shell .modal-content { width: 100%; }

.modal-content {
  background: rgba(26, 26, 26, 0.95); /* neutral panel - matches BILLS/FEED */
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--border-radius-xl);
  padding: 0.5rem var(--space-6) var(--space-6) var(--space-6);
  width: 90%;
  max-width: 480px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8);
  transform: scale(0.9);
  transition: transform var(--transition-normal);
  backdrop-filter: blur(10px);
}

.modal.show .modal-content {
  transform: scale(1);
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.5rem;
  margin-top: 0;
}

.modal-header h2 {
  font-size: 1.05rem;
  font-weight: 700;
  color: #ffffff;
  margin: 0;
  line-height: 1;
}

.close-btn {
  background: none;
  border: none;
  font-size: 1.5rem;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  padding: var(--space-1);
  transition: color var(--transition-fast);
}

.close-btn:hover {
  color: rgba(255, 255, 255, 0.9);
}

/* Form */
.form-group {
  margin-bottom: var(--space-4);
}

.form-group label {
  display: block;
  margin-bottom: var(--space-2);
  font-weight: 600;
  font-family: var(--font-sans);
  color: #888888;
  font-size: 0.8rem;
  text-transform: uppercase;
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: var(--space-3);
  border: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--border-radius);
  font-size: 1rem;
  font-family: var(--font-sans);
  transition: border-color var(--transition-fast);
  background: rgba(0, 0, 0, 0.3);
  color: #ffffff;
}

.form-group textarea {
  resize: vertical;
  min-height: 80px;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: #26a11c;
  box-shadow: 0 0 0 3px rgba(38, 161, 28, 0.1);
}

.checkbox-group .checkbox-label {
  display: flex;
  align-items: center;
  cursor: pointer;
  margin-bottom: 0;
}

.checkbox-group input[type="checkbox"] {
  width: auto;
  margin-right: var(--space-2);
  transform: scale(1.1);
  cursor: pointer;
  background: #000000 !important;
  border: 2px solid rgba(255, 255, 255, 0.2) !important;
}

.checkbox-text {
  font-weight: 600;
  color: #888888;
  font-size: 0.8rem;
  user-select: none;
}

.recurring-checkboxes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.recurring-checkboxes .checkbox-label {
  display: flex;
  align-items: center;
  cursor: pointer;
  margin-bottom: 0;
}

.recurring-checkboxes input[type="checkbox"] {
  width: auto;
  margin-right: var(--space-2);
  transform: scale(1.1);
  cursor: pointer;
  background: #000000 !important;
  border: 2px solid rgba(255, 255, 255, 0.2) !important;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border-radius: 3px;
  width: 16px;
  height: 16px;
  position: relative;
}

.recurring-checkboxes input[type="checkbox"]:checked {
  background: #26a11c !important;
  border-color: #26a11c !important;
}

.recurring-checkboxes input[type="checkbox"]:checked::after {
  content: '✓';
  position: absolute;
  color: white;
  font-size: 12px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.recurring-checkboxes .checkbox-text {
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.8);
  user-select: none;
}

.form-actions {
  display: flex;
  /* STACKED, ALWAYS: every action button owns one full row, whatever
     the mix (SAVE alone; DELETE when the card exists; END RECUR when
     ticked) - so the buttons read the same on every card and screen. */
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.submit-btn {
  flex: 1;
  background: #26a11c;
  color: var(--white);
  border: none;
  border-radius: var(--border-radius);
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  cursor: pointer;
  text-transform: uppercase;
  transition: background var(--transition-fast);
}

.submit-btn:hover {
  background: #1e8a15;
}

.delete-btn {
  flex: 1;
  background: #333333;
  color: var(--white);
  border: none;
  border-radius: var(--border-radius);
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  cursor: pointer;
  text-transform: uppercase;
  transition: background var(--transition-fast);
}

.color-picker {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.color-picker input[type="radio"] {
  display: none;
}

.color-option {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  cursor: pointer;
  border: 3px solid transparent;
  transition: all var(--transition-fast);
  position: relative;
}

.color-option:hover {
  transform: scale(1.1);
  border-color: rgba(255, 255, 255, 0.3);
}

.color-picker input[type="radio"]:checked + .color-option {
  border-color: #ffffff;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.3), 0 0 20px rgba(255, 255, 255, 0.4);
  transform: scale(1.15);
}

.color-picker input[type="radio"]:checked + .color-option::after {
  content: '✓';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  font-size: 20px;
  font-weight: bold;
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}

.delete-btn:hover {
  background: #555555;
}

.end-recur-btn {
  flex: 1;
  background: #333333;
  color: var(--white);
  border: none;
  border-radius: var(--border-radius);
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition-fast);
}

.end-recur-btn:hover {
  background: #555555;
}

/* Success Animation */
.success-animation {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(38, 161, 28, 0.9);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  animation: successPulse 1s ease-out forwards;
}

.success-animation.show {
  display: flex;
}

.success-checkmark {
  animation: checkmarkPop 0.5s ease-out forwards;
}

/* Card completion animation */
.bill-card.completing {
  background: #26a11c !important;
  border: none !important;
  animation: greenFadeOut 1s ease-out forwards;
}

@keyframes greenFadeOut {
  0% {
    background: #26a11c;
    opacity: 1;
    transform: scale(1);
  }
  50% {
    background: #26a11c;
    opacity: 1;
    transform: scale(1.02);
  }
  100% {
    background: #26a11c;
    opacity: 0;
    transform: scale(0.98);
  }
}

/* Animations */
@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes successPulse {
  0% { opacity: 0; }
  50% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes checkmarkPop {
  0% { transform: scale(0) rotate(45deg); }
  50% { transform: scale(1.2) rotate(45deg); }
  100% { transform: scale(1) rotate(45deg); }
}

@keyframes completeBounce {
  0%, 20%, 53%, 80%, 100% { transform: translate3d(0, 0, 0); }
  40%, 43% { transform: translate3d(0, -8px, 0); }
  70% { transform: translate3d(0, -4px, 0); }
  90% { transform: translate3d(0, -2px, 0); }
}

/* Responsive Design */
@media (max-width: 768px) {
  /* .app-container padding DELETED (July 9, 2026). var(--space-2) put 8px on every
     side below 768px: it pushed the whole page down 8px and pulled the black box in
     8px, and FEED - confirmed correct - has no such rule at any
     width. Do not reintroduce it without changing FEED in the same breath. */

  /* BOTH AXES MATTER, and they broke in opposite directions.
     HORIZONTAL: 42px, not var(--space-4)'s 16px. The black box keeps its 40px margin
     until 480px, so a 16px inset threw the wordmark, gear and avatar 24px OUTSIDE
     the box across the whole tablet range.
     VERTICAL: 0, not var(--space-3)'s 12px. 12px top and bottom made the header 24px
     taller and dropped the black box 32px down the screen. FEED's header-content is
     `0 42px` at every width; these apps must read the same. */
  .header-content {
    padding: 0 42px;
  }

  .app-title {
    font-size: 1.5rem;
  }

  /* columns now live in the standard block at the foot of this file */
  .bills-grid {
    gap: var(--space-3);
  }

  .modal-content {
    margin: var(--space-4);
    padding: var(--space-4);
  }

  .form-actions {
    flex-direction: column;
  }
}

@media (max-width: 480px) {
  .header-actions {
    gap: var(--space-2);
  }

  .auth-btn {
    padding: var(--space-2) var(--space-3);
    font-size: 0.875rem;
  }

  /* Cushions match FEED's: 12px, and the header comes with them. */
  .main-content {
    margin: 0 12px 40px 12px;
    padding: 0 12px 40px 12px;
  }

  /* vertical 0, exactly as FEED's 480 block. Only the inset follows the cushion. */
  .header-content {
    padding: 0 12px;
  }
}
/* NEW HOME card style: gray->black gradient border, transition starts ~80% (left->right).
   Masked overlay so rounded corners + overflow:hidden + cap band are preserved.
   Excludes add-card (dashed) and tooltip-card (solid outline). */
.bill-card:not(.add-card):not(.tooltip-card) { border: 0 !important; }
.bill-card:not(.add-card):not(.tooltip-card)::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 12px;
  padding: 2px;
  background: linear-gradient(to right, #4b505a 80%, #000000 100%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 3;
}

/* ADD-CARD, July 9 2026: it should read as an unpainted ticket, not a
   different object. Two layers:
   ::before = the GRAY.png band on the right edge at 50%, placed exactly as the
              real cards place theirs (background-image cannot take an opacity,
              so the band needs its own layer).
   ::after  = the SAME gray->black gradient the cards use, transition at 80%,
              pulled through a DASHED mask so the slot still reads as empty.
              CSS cannot paint a gradient onto a dashed border directly. */
.add-card { border: 0 !important; }
.add-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 12px;
  background-image: url('images/GRAY.png');
  background-repeat: no-repeat;
  background-position: right center;
  background-size: auto 100%;
  opacity: 0.25;
  pointer-events: none;
  z-index: 1;
}
.add-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 12px;
  background: linear-gradient(to right, #4b505a 80%, #000000 100%);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 180' preserveAspectRatio='none'%3E%3Crect x='1' y='1' width='358' height='178' rx='12' ry='12' fill='none' stroke='white' stroke-width='2' stroke-dasharray='8 5'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 180' preserveAspectRatio='none'%3E%3Crect x='1' y='1' width='358' height='178' rx='12' ry='12' fill='none' stroke='white' stroke-width='2' stroke-dasharray='8 5'/%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  pointer-events: none;
  z-index: 3;
}
.add-card:hover::after {
  background: linear-gradient(to right, #6b7280 80%, #000000 100%);
}

/* HOME icon beside the category name - flush left with the title,
   icon sized up 20% */
.bill-amount {
  display: flex;
  align-items: center;
  gap: 6px;
}
.bill-amount .cat-home-icon {
  height: 19px;
  width: auto;
  margin-left: -5px;
}

/* CARD TEXT HIERARCHY:
   title AND category read at full strength - description, due-in
   warning, and date step back to 75% (25% transparent). */
.bill-card .bill-description,
.bill-card .bill-due,
.bill-card .bill-actual-date {
  opacity: 0.75;
}

/* =========================================================================
   THE BREAKPOINT STANDARD. One set of numbers for
   every app:
       >= 1024px   desktop   3 columns
       600-1023px  tablet    2 columns   <- new; an iPad in portrait showed 1
       <= 599px    phone     1 column
       <= 480px    tight cushions (not a column change)
   FEED is exempt: it is one river in date order and has no columns at any
   width. These sit at the FOOT of the file on purpose - equal specificity to
   the 3-column rule above, so source order decides, and the narrower block
   must come last. Do not move them up.
   ========================================================================= */
@media (max-width: 1199px) {
  .bills-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 799px) {
  .bills-grid { grid-template-columns: 1fr; }
}

/* ── THE APP-BAR (ported from FEED, July 10, 2026) ─────────────────────────────
   The feed's chooser, now the ONE navigation on every page. Here the .ab elements
   are anchors (links to feed.kronker.com/#app=<app>), not the feed's filter
   buttons, but the look is identical - same 34px cap, same 0.3 / 0.6 / 1 opacity
   for off / hover / lit. The lit one (.on) is the app you are on.
   Numbers are the feed's, to the pixel; keep them in sync with FEED's copy. */
.app-bar {
  display: flex;
  gap: 8px;
  padding: 18px 0 0 4px;
}
.ab {
  background: none; border: none; padding: 0; line-height: 0;
  cursor: pointer;
  opacity: 0.4;
  transition: opacity 0.12s ease;
  flex: 0 1 34px; min-width: 0;
  text-decoration: none;
}
.ab img { width: 100%; height: auto; display: block; }
/* THE ROOM DOORS ON PHONES: under 600px the header tiles are hidden,
   so the dock and the kronk list each carry the OTHER room's door at
   the right end of the picker row - the two rooms stay one tap apart
   and nothing crowds the phone header. Desktop never shows these;
   the header tiles carry the doors there. */
.ab-room { display: none; }
/* BUILDER MODE: the bar's tools span (its add-card already hidden
   there) must not split the flex auto-margins with the room door -
   hidden entirely, the door right-pins to the black margin. */
body.kr-builder .bar-tools { display: none; }
@media (max-width: 599px) {
  .ab-room {
    display: block;
    /* Right-justified: the lone flex auto-margin pushes the room door
       to the row's far end while the picker icons stay left-packed. */
    margin-left: auto;
    /* 40% smaller than the 34px picker icons. */
    flex: 0 0 20px;
    align-self: center;
  }
}
/* :not(.on) so a hover never dims the app you are on. */
.ab:not(.on):hover { opacity: 0.6; }
.ab.on { opacity: 1; }
@media (max-width: 480px) {
  .app-bar { gap: 5px; }
}


/* ── THE VIEW SWITCHER (EDITOR · FEED · ADD), ported from FEED July 10, 2026 ──────
   Rides the right end of the app-bar. EDITOR is lit here (you are on the editor);
   FEED is this app's single-column feed; ADD is the feed's mini-add card, wired in
   app.js to the page's own add-card. Tiles are white art; opacity carries state. */
.bar-tools { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.header-tiles { display: flex; align-items: center; gap: 17px; margin-right: -14px; }   /* view tiles live in the header now */
.view-tile { display: flex; align-items: center; opacity: 0.3; transition: opacity 0.12s ease; }
.view-tile.on { opacity: 1; }
.view-tile:not(.on):hover { opacity: 0.6; }
.view-tile img { display: block; height: 26px; width: auto; }
/* .mini-add (the bar's little add card) RETIRED - the full add card
   leads this page's grid and is the one adder. icons/add.png stays. */
@media (max-width: 480px) {
  .bar-tools { gap: 8px; }
  .view-tile img { height: 22px; }
}


/* ── THE WORDS CAME BACK.
   R2.55
   hid the app's name under 400px because it could not fit beside the header's
   view tiles; R2.63 removed the tiles from phones entirely and the arithmetic
   flipped - wordmark ~139 + name (~66 for BIG DAYS, the widest) + gear 55 +
   8px gap + avatar 32 + 24 cushions ≈ 324: inside a 360 phone with room. The
   name now rides at EVERY width.
   THE SEATBELT IS ALL THAT REMAINS: on widths nobody plans (display-zoom
   ~320, split screen) the widest name would still kiss the gear, so the flex
   chain lets the NAME ALONE give way - it trims to an ellipsis instead of
   riding under the buttons. At every normal width it never fires.
   min-width:0 is what permits a flex child to narrow below its content;
   flex-shrink:0 keeps the wordmark art whole; overflow:hidden both trims and
   (per the flex spec) is what allows the shrink at all. */
.logo-section { min-width: 0; }
.logo-section img { flex-shrink: 0; }
.brand-stack { min-width: 0; }
.app-subhead { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* ── THE TOP BAR SITS TIGHT AT PHONE WIDTHS.
   The header always
   wore 40px of air above and 20px below at every width. At <=480 the 40
   above drops to 7, so the bar rides nearly against the top of
   the screen. The 20 below stays exactly as it always was - it went to 7 for
   one deploy, wrongly, and came straight back. A second 480 block on purpose
   - appended after the first, it wins the tie by order, and nothing inside
   the original block had to be touched in seven files. */
@media (max-width: 480px) {
  .header { padding-top: 7px; }
  /* THE VIEW TILES ARE DESKTOP FURNITURE.
   On a phone the pair vanishes; the
     wordmark is still the door to the FEED (it always was), and the feed's
     tickets and mini-add are the door back. Tablets and desktops keep the
     tiles exactly as they are.
     MOVED July 12, 2026
: the fence now
     rides the constitution's own phone line, <=599 - the rule lives in THE
     DOCK section at the foot of this file. */
  /* THE LOGO STEPS 4px BACK ONTO THE MARGIN.
   The art is not the culprit - the wordmark
     PNG is flush to its first pixel, 0 transparent columns, measured - it is
     the 10px drop-shadow glow. At the desktop's 42px inset the glow never
     approaches the margin line; at the 12px cushion it spills toward the
     screen edge and the logo READS as overshooting the margin by ~4. (The
     crossover also costs 2px of relative inset: desktop letters sit at 42
     against a box at 40; the phone had both at 12.) His eye, his number:
     the whole logo-section steps 4px right - subhead and all, so the pair
     never splits - at phone widths only. */
  .logo-section { margin-left: 4px; }
}

/* ── THE FOOTER SITS AT THE BOTTOM OF THE SCROLL, NOT THE WINDOW.
   IT NEVER LEFT THE APPS - THAT WAS THE
   MISTAKE TO FIND. The cure shipped July 9 on FEED ONLY, where
   the footer is styled from the sheet; the seven apps carried the footer's
   whole old recipe - position:fixed;bottom:12px;z-index:50 - as an INLINE
   style in index.html, where no stylesheet fix could ever reach it (inline
   always wins). The inline style is now gone from the markup and the class
   rules live here, word for word FEED's: the footer is the last element in
   <body>, so ordinary flow puts it below the black box at the end of the
   scroll; main-content's own bottom margin gives it air; the padding keeps
   it off the window's edge. No position, no z-index. If this ever "returns"
   again, look for an inline style FIRST. */
.site-footer {
  text-align: center;
  font-size: 12px;
  color: #6b7280;
  padding: 0 20px 24px 20px;
}
.site-footer a { color: #6b7280; }

/* ── THE EXPORT-PASSWORD BOX GLOWS brand green WHEN SELECTED
.
   This was the LAST data box in the family with no focus style - its tag
   carries inline border styling and no sheet rule existed, so the browser
   drew its own default blue ring. Same glow as every .form-group input.
   !important because the resting border rides the tag inline - a sheet rule
   loses to it without the override. */
.export-password-field input:focus {
  outline: none;
  border-color: #26a11c !important;
  box-shadow: 0 0 0 3px rgba(38, 161, 28, 0.1);
}

/* ── EVERY TICK IS brand green IN EVERY BROWSER.
   The
   gear's boxes are hand-drawn (appearance:none) and were already green; the
   MODAL's checkboxes (Recurring, Autopay and kin) were native, and a native
   tick wears the browser's own blue unless told otherwise. accent-color is
   the one lever Chrome, Safari, Firefox and Edge all honour for native
   ticks; it is inert wherever appearance:none already rules. */
input[type="checkbox"], input[type="radio"] { accent-color: #26a11c; }

/* The body must always cover the viewport now that the canvas below it is the
   GREEN BASE LAYER - without this a short page would show green below the
   gray instead of only in the overscroll. */
body { min-height: 100vh; }


/* ═════════════════ THE DOCK - KRONKS PHASE 3 (July 12, 2026) ═════════════════
   The main page is the KRONK FEED-SELECTION room. One row: five seats
   (seated kronks fill from the left; every empty seat is a plus circle)
   and the KRONK LIST icon. Unseating lives only on the kronk list page.
   Each circle wraps OFF -> ON -> LIVE -> OFF. OFF stays seated, dimmed -
   gray-not-gone. */

/* THE VIEW TILES' FENCE MOVED TO THE TABLET LINE.
   Was <=480 (R2.63 era); the old rule left that block the same day. */
@media (max-width: 599px) {
  .header-tiles { display: none; }
}

/* THE TOOLTIP'S CROWN SLOT
 - the announcement card paints above
   the dock, full width, and vanishes inside the builder. */
.tooltip-slot { padding-top: 14px; }
.tooltip-slot:empty { padding-top: 0; }
.tooltip-slot .bill-card { width: 100%; }
/* The builder shows the full tooltip like every room (founder ruling
   - the SAVE strip retired with the save button). No hiding rule. */


/* ══ THE KRONK CARD + IDENTITY PANEL WARDROBE (shared by k- and the
   builder) ══ Moved here from kronk-list.html's inline sheet so the
   kronk card and the identity panel are ONE machine on both pages -
   no same-breath copies. */
/* TICKET-TRUE CARD: title in the ticket's size and place, the
   description in the ticket's slot, the emoji as the BIG
   WATERMARK at 60% - one card language across the program. */
.kl-card {
    position: relative; box-sizing: border-box; overflow: hidden;
    display: block;
    height: 180px; padding: 12px 46px 16px 16px;
    border-radius: 12px; cursor: pointer;
    border: 2px solid rgba(255,255,255,0.12);
    transition: transform 0.12s ease, border-color 0.12s ease;
}
.kl-card:hover { transform: translateY(-2px); border-color: rgba(255,255,255,0.35); }
.kl-card-title { position: relative; z-index: 1; font-size: 1.53rem; font-weight: 700;
                 color: #ffffff; line-height: 1.1; word-break: break-word; }
.kl-card-desc { position: relative; z-index: 1; margin-top: 6px; font-size: 1.0rem;
                color: rgba(255,255,255,0.6); line-height: 1.4; }
.kl-watermark { position: absolute; top: 50%; right: 16px; transform: translateY(-50%);
                font-size: 96px; line-height: 1; opacity: 0.6; pointer-events: none; }
.kl-wm-letter { font-weight: 800; color: #ffffff; }
/* An ORG timeline's seal stands in the same slot at the same weight as
   the emoji it replaces - 96px, the glyph's own size. Same breath as
   the FEED's #feedHome .kl-wm-seal img. */
.kl-wm-seal img { height: 96px; width: auto; display: block; }

/* THE COUPLED MEMBER'S SKIN (B6) NEEDS NOTHING FROM THIS SHEET. Every
   surface - html, body, the header and the wordmark - is painted inline
   by kr-brand.js. The bottom safe-area is body PADDING on this tree
   (`body { padding-bottom: env(safe-area-inset-bottom) }`), so it shows
   the body's own painted background and follows the group's colour for
   free. DO NOT add a `body.kr-coupled::after` rule here: this tree has
   no base `body::after` strip, and a pseudo-element with no `content`
   generates no box - the rule would be dead. Only the FEED, which does
   carry a fixed strip, needs that override. */

/* THE COUPLED MEMBER'S BAR (his ruling): SCHOOL hops two places left
   and sits right beside the colour wheel - for a school family that is
   the tab they reach for. Pure flex ORDER, so no markup moves and an
   uncoupled account's bar is untouched. Matched on TITLE because the
   FEED's bar is buttons carrying data-app while HOME's is links
   carrying hrefs - but both carry the same titles. Everything else
   keeps its default order 0 and follows in source order. */
body.kr-coupled .app-bar [title="EVERYTHING"] { order: -2; }
body.kr-coupled .app-bar [title="SCHOOL"]     { order: -1; }
/* THE THREE CORNERS OF POWER (founder layout):
   bottom-left THE DOTS - one add-to-dock button that shows the
   dock's occupancy (N filled of 5) and toggles this kronk in or
   out; top-right THE X - delete for good, behind the one
   constitutional are-you-sure; bottom-right THE WHEEL - the
   power button: live from here, docking first if needed. */
/* THE GREEN LIGHT (the 5-dot occupancy meter retired with the 11-cap):
   a LIVE kronk's corner is the plus circle's OWN ring - same white
   circle, same green border, the radio language - with a tiny green
   dot seated dead-centre (the white gap is its border) wearing a
   TIGHT green glow. Same seat, same size, same click (the one power
   button; tap to undock). */
.kl-livelight { position: absolute; left: 16px; bottom: 12px; z-index: 2;
                width: 24px; height: 24px; border-radius: 50%; box-sizing: border-box;
                background: #fff; border: 2px solid #26a11c;
                /* The dot centres by FLEX, not coordinates: the shelf's corner
                   row un-positions this ring (position: static there), and an
                   absolutely-placed dot would anchor to the card and slide off.
                   Flex centring holds in every room. */
                display: flex; align-items: center; justify-content: center;
                cursor: pointer; }
.kl-livelight::before {
    content: ''; display: block;
    width: 10px; height: 10px; border-radius: 50%;
    background: #26a11c;
    box-shadow: 0 0 6px 2px rgba(38, 161, 28, 0.9);
    transition: box-shadow 0.12s ease;
}
.kl-livelight:hover::before { box-shadow: 0 0 9px 3px rgba(38, 161, 28, 1); }

/* THE GHOST CARD - a docked evergreen between cards: the next ticket's
   place held by a see-through promise on the FEED SCHEDULE. Inert by
   law - no doors, no brandmark, no clicks - dimmed and desaturated,
   wearing the real ticket's own anatomy: kronk name in the title slot,
   the warning as the relative line, the true date in the date slot. */
.bill-card.kr-ghost { opacity: 0.4; filter: saturate(0.5); pointer-events: none; }
.kr-ghost .kr-ghost-warn { color: #fff; font-weight: 800; }
/* The date, the redo indicator and the fixed count ride ONE level line. */
.kr-ghost .card-time { display: flex; align-items: center; gap: 4px; }
/* Undocked cards wear a single plus circle instead - the dock's
   own plus language, small; gone entirely at a full dock. */
.kl-dockplus { position: absolute; left: 16px; bottom: 12px; z-index: 2;
               width: 24px; height: 24px; border-radius: 50%; box-sizing: border-box;
               background: #fff; border: 2px solid #26a11c; color: #26a11c;
               cursor: pointer;
               transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease; }
.kl-dockplus:hover { background: #26a11c; color: #fff; }
.kl-dockplus::before, .kl-dockplus::after {
    content: ''; position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    background: currentColor; border-radius: 1px;
}
.kl-dockplus::before { width: 10px; height: 2px; }
.kl-dockplus::after  { width: 2px; height: 10px; }
/* Delete sits on the ABSOLUTE top-right corner in a gray circle (founder), out
   of the home icon's way. */
.kl-x { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px;
        z-index: 3; cursor: pointer; color: rgba(255,255,255,0.85);
        background: rgba(70,74,84,0.9); border-radius: 50%;
        transition: color 0.12s ease, background 0.12s ease; }
.kl-x:hover { color: #fff; background: rgba(96,100,112,1); }
.kl-x::before, .kl-x::after { content: ''; position: absolute; top: 50%; left: 50%;
        width: 11px; height: 2px; background: currentColor; border-radius: 1px; }
.kl-x::before { transform: translate(-50%,-50%) rotate(45deg); }
.kl-x::after  { transform: translate(-50%,-50%) rotate(-45deg); }
/* THE TOP-RIGHT HOME ICON (founder's two-state design): a SECOND handle on the
   dock toggle, placed where the HOME icon sits on a docked kronk's feed ticket.
   MUTED (grey + dim) = undocked; FULL COLOUR (.kl-home-on) = docked; the whole
   thing glows on hover. Clicking it routes through data-dock / data-cart-dock to
   toggleDock, exactly like the bottom-left control. */
.kl-home { position: absolute; top: 10px; right: 12px; z-index: 2;
           width: 34px; height: 34px; cursor: pointer;
           opacity: 0.6;   /* MUTED but clearly visible when undocked (no grayscale
                              - HOME.png is already grey, so it only dimmed it out) */
           transition: opacity 0.15s ease, filter 0.15s ease; }
.kl-home img { display: block; width: 100%; height: 100%; }
.kl-home:hover { opacity: 0.9; filter: drop-shadow(0 0 6px rgba(255,255,255,0.55)); }
.kl-home.kl-home-on { opacity: 1; filter: drop-shadow(0 0 5px rgba(38,161,28,0.6)); }
.kl-home.kl-home-on:hover { opacity: 1; filter: drop-shadow(0 0 9px rgba(38,161,28,0.85)); }
/* THE TICKET COUNT (RL9, unified): the number rides inside
   .kl-ever-corner's flex row wearing .kl-ever-count itself - the
   evergreen anatomy, one dress, self-centred. No rule of its own. */
.kl-wheel { position: absolute; right: 12px; bottom: 12px; z-index: 2;
            line-height: 0; cursor: pointer; }
/* 30px: the wheel's own just-under-600 size (5vw at 600 = 30 exactly)
   held all the way up - it used to DROP to 20 crossing the line. */
.kl-wheel img { width: 30px; height: 30px; display: block;
                filter: grayscale(1); opacity: 0.35;
                transition: filter 0.12s ease, opacity 0.12s ease; }
.kl-wheel:hover img { opacity: 0.6; }
.kl-wheel-live img, .kl-wheel-live:hover img { filter: none; opacity: 1; }
/* COMING SOON tag on an inert shelf teaser - sits where the wheel
   sits on a live cartridge (bottom-right), all caps. 80% WHITE, not
   fixed gray - the colour law: translucent white reads on any face. */
.kl-soon { position: absolute; right: 12px; bottom: 12px; z-index: 2;
           font-size: 0.68rem; font-weight: 800; letter-spacing: 0.08em;
           color: rgba(255, 255, 255, 0.8); text-transform: uppercase; }

/* The kronk colour gradients, scoped to their wearers (cards on
   either page, panel swatches). */
.kl-card.c-blue, .klc-swatch.c-blue { background: linear-gradient(to bottom right, #3b82f6, #1e40af); }
.kl-card.c-green, .klc-swatch.c-green { background: linear-gradient(to bottom right, #10b981, #047857); }
.kl-card.c-yellow, .klc-swatch.c-yellow { background: linear-gradient(to bottom right, #f59e0b, #b45309); }
.kl-card.c-orange, .klc-swatch.c-orange { background: linear-gradient(to bottom right, #f97316, #c2410c); }
.kl-card.c-red, .klc-swatch.c-red { background: linear-gradient(to bottom right, #ef4444, #b91c1c); }
.kl-card.c-pink, .klc-swatch.c-pink { background: linear-gradient(to bottom right, #ec4899, #be185d); }
.kl-card.c-purple, .klc-swatch.c-purple { background: linear-gradient(to bottom right, #a855f7, #7e22ce); }
.kl-card.c-gray, .klc-swatch.c-gray { background: linear-gradient(to bottom right, #6b7280, #4b5563); }
.kl-card.c-black, .klc-swatch.c-black { background: linear-gradient(to bottom right, #2a2a2a, #000000); }
.kl-card.c-teal, .klc-swatch.c-teal { background: linear-gradient(to bottom right, #42aca2, #09655c); }
.kl-card.c-navy, .klc-swatch.c-navy { background: linear-gradient(to bottom right, #5065a4, #14275e); }
.kl-card.c-beige, .klc-swatch.c-beige { background: linear-gradient(to bottom right, #a5927a, #5f4e3a); }
.kl-card.c-forest, .klc-swatch.c-forest { background: linear-gradient(to bottom right, #387d68, #003c29); }

/* THE ADMIN LOOK FOR SCHOOL TIMELINES (his ruling, Aug 9: "school
   built kronks should match the look exactly as seen in the admin"):
   an org kronk's card wears the ORG palette's FLAT ground under the
   one card overlay - exactly org.html's tile - never the personal
   two-tone. The seal machinery already rides every painter. The
   must-agree org palette list includes this block, kronk-list's and
   the FEED's twins, and .kr-org above; change one, change all. */
.kl-card.ko-blue   { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #1381f2; }
.kl-card.ko-green  { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #26a11c; }
.kl-card.ko-yellow { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #f5d63d; }
.kl-card.ko-orange { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #e8871e; }
.kl-card.ko-red    { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #dc2626; }
.kl-card.ko-pink   { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #ec4899; }
.kl-card.ko-purple { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #c722ef; }
.kl-card.ko-gray   { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #5e6269; }
.kl-card.ko-black  { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #000000; }
.kl-card.ko-teal   { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #0d9488; }
.kl-card.ko-navy   { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #1e3a8a; }
.kl-card.ko-beige  { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #8b7355; }
.kl-card.ko-forest { background: linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6)) #00583d; }

/* THE CREATE PANEL - a modal, reusing the app's .modal show/hide. BUILT
   VERTICALLY and 320-SAFE
. box-sizing:border-box + a vw-capped width stop the
   old content-box overflow (90% width PLUS padding was wider than the
   screen); every child is a full-width stacked block; the swatches wrap
   and never exceed the panel; padding tightens on the smallest phones. */
.kronk-panel .modal-content {
    box-sizing: border-box;
    width: 300px;
    max-width: 300px;
}
.kronk-panel .modal-content * { box-sizing: border-box; }
.klc-field { margin-bottom: 16px; }
.klc-label { display: block; font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
             color: rgba(255,255,255,0.7); margin-bottom: 6px; }
.klc-name { display: block; width: 100%; padding: 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.25);
            background: rgba(0,0,0,0.3); color: #fff; font-size: 1rem; }
.klc-name:focus { outline: none; border-color: #26a11c; box-shadow: 0 0 0 3px rgba(38,161,28,0.1); }
.klc-swatches { display: flex; flex-wrap: wrap; gap: 10px; max-width: 100%; }
.klc-swatch { width: 34px; height: 34px; border-radius: 50%; cursor: pointer;
              border: 3px solid transparent; flex: 0 0 auto; }
.klc-swatch.sel { border-color: #fff; }
.klc-build { display: block; width: 100%; background: #26a11c; color: #fff; border: none; border-radius: 8px;
             padding: 0.75rem 1rem; font-size: 0.95rem; font-weight: 600; text-transform: uppercase; cursor: pointer;
             margin-top: 4px; transition: background 150ms ease-in-out; }
.klc-build:hover { background: #1e8a15; }
/* DELETE KRONK - the destructive twin of SAVE in the edit dialog; red outline,
   fills red on hover. Same function as the retired card X (builderDeleteKronk). */
.klc-delete { display: block; width: 100%; background: transparent; color: #e05a5a;
              border: 1px solid rgba(224,90,90,0.55); border-radius: 8px;
              padding: 11px; font-size: 0.95rem; font-weight: 700; cursor: pointer; margin-top: 10px; }
.klc-delete:hover { background: #c0392b; color: #fff; border-color: #c0392b; }
/* THE EMOJI PICKER.
   Search filters; tap sets the kronk's dot. */
.klc-emoji-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
                  gap: 6px; max-height: 150px; overflow-y: auto; padding: 5px;
                  background: rgba(0,0,0,0.25); border-radius: 8px; }
.klc-emoji-cell { aspect-ratio: 1/1; display: flex; align-items: center; justify-content: center;
                  font-size: 1.3rem; line-height: 1; cursor: pointer; border-radius: 8px;
                  border: 2px solid transparent; }
.klc-emoji-cell.sel { border-color: #26a11c; background: rgba(38,161,28,0.18); }
/* THE SMALLEST PHONES (<=360, covers 320): tighter panel padding + a hair
   smaller swatches, so eight of them wrap to two clean rows with room. */
@media (max-width: 360px) {
    .kronk-panel .modal-content { padding: 0.5rem 14px 18px 14px; }
    .klc-swatch { width: 30px; height: 30px; }
    .klc-swatches { gap: 8px; }
}

/* ── THE KRONK CARD IDENTITY ─
   A kronk's ticket wears its kronk: the BIRTH COLOUR as the card
   background and the kronk's emoji as the big faded watermark - the
   same slot the feed's big-days tickets use (centred on the card's
   height, right 44px, 12px clear of the 32px cap). The muted tones
   are the dock palette pulled 45%/55% toward the #1a1a1a base;
   kr-live swaps in the FULL dock palette, so LIVE and review sit
   side by side in one feed and the eye learns LIVE at a glance.
   The colour rides as a SECOND background-image layer UNDER the cap
   PNG (cap listed first = painted on top); the `background`
   shorthand stays banned, per the base card rule. An unknown colour
   word leaves --kr-grad unset, the declaration goes invalid, and
   the card falls back to the plain base - gray-safe. Keep these
   pairs in the same breath as .kronk-dock .c-<x> above. */
.bill-card .card-bg {
  position: absolute; top: 50%; right: 44px;
  transform: translateY(-50%);
  font-size: 96px; line-height: 1;
  /* The emoji carries state (founder): 40% off, 60% live. */
  opacity: 0.4;
  pointer-events: none;
}
.bill-card.kr-live .card-bg { opacity: 0.6; }
/* Evergreen (read-only) tickets: no edit door, so no pointer lure. */
.bill-card.kr-locked .bill-content { cursor: default; }
.bill-card.kr-card {
  background-image: url('images/GRAY.png'), var(--kr-grad);
  /* The cap TILES down the edge (repeat-y, natural size) so it stays crisp when a
     user kronk's ticket expands, instead of stretching; the gradient still covers. */
  background-repeat: repeat-y, no-repeat;
  background-position: right top, center;
  background-size: auto, cover;
}
/* muted (kronk ON) - the review tones */
.bill-card.kr-card.color-blue   { --kr-grad: linear-gradient(to right, #29497d, #1c2b5d); }
.bill-card.kr-card.color-green  { --kr-grad: linear-gradient(to right, #166248, #104435); }
.bill-card.kr-card.color-yellow { --kr-grad: linear-gradient(to right, #7d5513, #5f3412); }
.bill-card.kr-card.color-orange { --kr-grad: linear-gradient(to right, #7e4218, #662c14); }
.bill-card.kr-card.color-red    { --kr-grad: linear-gradient(to right, #7a2d2d, #621b1b); }
.bill-card.kr-card.color-pink   { --kr-grad: linear-gradient(to right, #782f53, #641938); }
.bill-card.kr-card.color-purple { --kr-grad: linear-gradient(to right, #5a357d, #471e6b); }
.bill-card.kr-card.color-gray   { --kr-grad: linear-gradient(to right, #3e4248, #30353b); }
.bill-card.kr-card.color-black  { --kr-grad: linear-gradient(to right, #171717, #000000); }
.bill-card.kr-card.color-teal   { --kr-grad: linear-gradient(to right, #245f59, #053833); }
.bill-card.kr-card.color-navy   { --kr-grad: linear-gradient(to right, #2c385a, #0b1534); }
.bill-card.kr-card.color-beige  { --kr-grad: linear-gradient(to right, #5b5043, #342b20); }
.bill-card.kr-card.color-forest { --kr-grad: linear-gradient(to right, #1f4539, #002117); }
/* LIVE - the full dock palette */
.bill-card.kr-live.color-blue   { --kr-grad: linear-gradient(to right, #3b82f6, #1e40af); }
.bill-card.kr-live.color-green  { --kr-grad: linear-gradient(to right, #10b981, #047857); }
.bill-card.kr-live.color-yellow { --kr-grad: linear-gradient(to right, #f59e0b, #b45309); }
.bill-card.kr-live.color-orange { --kr-grad: linear-gradient(to right, #f97316, #c2410c); }
.bill-card.kr-live.color-red    { --kr-grad: linear-gradient(to right, #ef4444, #b91c1c); }
.bill-card.kr-live.color-pink   { --kr-grad: linear-gradient(to right, #ec4899, #be185d); }
.bill-card.kr-live.color-purple { --kr-grad: linear-gradient(to right, #a855f7, #7e22ce); }
.bill-card.kr-live.color-gray   { --kr-grad: linear-gradient(to right, #6b7280, #4b5563); }
.bill-card.kr-live.color-black  { --kr-grad: linear-gradient(to right, #2a2a2a, #000000); }
.bill-card.kr-live.color-teal   { --kr-grad: linear-gradient(to right, #42aca2, #09655c); }
.bill-card.kr-live.color-navy   { --kr-grad: linear-gradient(to right, #5065a4, #14275e); }
.bill-card.kr-live.color-beige  { --kr-grad: linear-gradient(to right, #a5927a, #5f4e3a); }
.bill-card.kr-live.color-forest { --kr-grad: linear-gradient(to right, #387d68, #003c29); }

/* THE SERVED DRESS (his eye, Aug 9): an ORG kronk's ticket wears in
   the builder EXACTLY what the member's feed serves - the org
   palette's SOLID ground under the one card overlay (clear at the
   top left, falling to 0.6 black at the bottom right), never the
   personal two-hex gradient. The ground hexes are the org palette -
   the must-agree list (org.html EDGE, org-serve.php $grounds et al)
   grows to include this block; change one, change all. The cap PNG
   stays the top layer, tiling the right edge as ever. */
.bill-card.kr-org {
  background-image: url('images/GRAY.png'), linear-gradient(to bottom right, rgba(0,0,0,0), rgba(0,0,0,0.6));
  background-repeat: repeat-y, no-repeat;
  background-position: right top, center;
  background-size: auto, cover;
}
.bill-card.kr-org.color-blue   { background-color: #1381f2; }
.bill-card.kr-org.color-green  { background-color: #26a11c; }
.bill-card.kr-org.color-yellow { background-color: #f5d63d; }
.bill-card.kr-org.color-orange { background-color: #e8871e; }
.bill-card.kr-org.color-red    { background-color: #dc2626; }
.bill-card.kr-org.color-pink   { background-color: #ec4899; }
.bill-card.kr-org.color-purple { background-color: #c722ef; }
.bill-card.kr-org.color-gray   { background-color: #5e6269; }
.bill-card.kr-org.color-black  { background-color: #000000; }
.bill-card.kr-org.color-teal   { background-color: #0d9488; }
.bill-card.kr-org.color-navy   { background-color: #1e3a8a; }
.bill-card.kr-org.color-beige  { background-color: #8b7355; }
.bill-card.kr-org.color-forest { background-color: #00583d; }

/* Evergreen "cartridge" faces (ground colour + still, bottom-right) are now
   painted INLINE by the render, from the shared registry in kr-evergreens.js,
   keyed on the kronk NAME so they travel like the emoji and never go stale.
   The .kr-ever class stays as a marker; the paint lives on the element. */

/* THE DOCK ROW. Circles run SLIGHTLY LARGER than the 34px picker icons
  . */
.kronk-dock { padding: 26px 4px 0; }  /* the dock sits a beat lower off the tooltip */
/* On the DOCK PAGE ONLY, the REVIEW FEED line rides closer under the
   dock (the shared 24px read as a hole there). The builder's TICKETING
   and k-'s TIMELINES keep the shared rhythm. */
body:not(.building) .dhr-main { margin-top: 12px; }
/* THE REVIEW FEED IS A FEED (founder ruling): on the dock page every
   ticket takes the FULL WIDTH of its row at every screen size - one
   river in date order, like the FEED itself (constitution 11 exempts
   feeds from columns). The BUILDER keeps the 3/2/1 workroom grid.
   This selector outranks the breakpoint rules, so no media query can
   re-column the feed. */
body:not(.building) .bills-grid { grid-template-columns: 1fr; }
/* The dock rides EVERY home room now (the kicker ruling) - the builder
   included, above the kronk card. No hiding rule exists on purpose. */
/* ROWS OF SIX ARE THE LAW: the living grid caps at 11 kronks + the
   list icon = 12 cells = two perfect rows. Grid columns enforce the
   six-wide law at every width; the renderer never counts rows. */
.kd-row { display: grid; grid-template-columns: repeat(5, max-content); align-items: start; gap: 8px; }
/* THE BUILDER'S KRONK ROW: the word, the fade line, and the kronk
   icon at the line's end - the door back to the kronk list. BUILDER
   MODE ONLY; a finger-sized tap zone rides the door. THE DOCK STANDS
   DOWN on builder pages - the row takes the dock's own top seat, the
   same place KRONK DOCK holds on the kronk page. */
.dhr-kronk { display: none; }
body.kr-builder .dhr-kronk { display: flex;
  /* Ruled 3px above the unified 24px seat - a deliberate deviation
     on the builder alone. */
  margin-top: 21px; }
body.kr-builder .kronk-dock { display: none; }
/* ZERO HEIGHT IN THE ROW'S MATH (the close-icon lesson): the 28px
   icon must not inflate the header row, or centering seats the KRONK
   text lower than KRONK DOCK's text-only row. The door's box is zero
   tall; the icon centers on the row's own line and stays tappable. */
.dhr-door { flex: none; height: 0; display: flex; align-items: center;
            line-height: 0; opacity: 0.5;
            padding: 14px; margin: -14px; }
.dhr-door:hover { opacity: 1; }
/* SEAT WIDTH = CIRCLE WIDTH. Every dock item is the same width, so the
   row's rhythm is even, and the fixed gap does the distribution. The
   name box below is capped at this same width - two lines max, with
   the two-line height always reserved - so no text can ever widen a
   column, change the row's height, or move a neighbour. Static. */
.kd-seat { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 57px; }
.kd-circle {
  position: relative;
  width: 57px; height: 57px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; border: 2px solid #6b7280;  /* the card's border gray, both states */
  transition: opacity 0.12s ease, border-color 0.12s ease,
              box-shadow 0.12s ease, filter 0.12s ease;
}
.kd-emoji { font-size: 1.65rem; line-height: 1; }
.kd-initial { font-size: 1.4rem; font-weight: 800; color: #fff; }
.kd-name {
  font-size: 0.6rem; letter-spacing: 0.03em; text-align: center;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85); line-height: 1.15; max-width: 51px;
  /* TWO LINES, ALWAYS TWO LINES' HEIGHT. Clamped at two lines, and the
     two-line height (2 x 1.15 line-height = 2.3em) is always reserved -
     a one-line name and a two-line name hold the row at the same
     height, so nothing below ever moves. Long words break rather than
     clip sideways; past two lines the clamp ellipsizes. */
  min-height: 2.3em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: break-word;
}

/* the kronk colours - the BIG DAYS palette, scoped to the dock (the list
   page carries its own inline copy; keep the two in the same breath) */
.kronk-dock .c-blue   { background: linear-gradient(to bottom right, #3b82f6, #1e40af); }
.kronk-dock .c-green  { background: linear-gradient(to bottom right, #10b981, #047857); }
.kronk-dock .c-yellow { background: linear-gradient(to bottom right, #f59e0b, #b45309); }
.kronk-dock .c-orange { background: linear-gradient(to bottom right, #f97316, #c2410c); }
.kronk-dock .c-red    { background: linear-gradient(to bottom right, #ef4444, #b91c1c); }
.kronk-dock .c-pink   { background: linear-gradient(to bottom right, #ec4899, #be185d); }
.kronk-dock .c-purple { background: linear-gradient(to bottom right, #a855f7, #7e22ce); }
.kronk-dock .c-gray   { background: linear-gradient(to bottom right, #6b7280, #4b5563); }

/* LOADED vs LIVE - one language on circle, wheel and cards alike:
   muted until the kronk is LIVE, then everything blooms full colour
   together. The circle's ring is the card-border gray in BOTH
   states (a vivid ring once read as a lure to click - it is not).
   OFF and the green ring stay retired. */
/* The mute veils the colour field, and the FACE carries state too
   (founder): 40% opacity off, 100% live. No vivid-ring leak is
   possible - the border is solid opaque gray, nothing glows
   through it. */
.kd-circle.kd-mute::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: rgba(26, 26, 26, 0.45);
}
.kd-emoji, .kd-initial { position: relative; z-index: 1; }
.kd-circle.kd-mute .kd-emoji,
.kd-circle.kd-mute .kd-initial { opacity: 0.4; }

/* THE LIVE WHEEL - a 20px copy of the picker wheel, centred above EVERY
   dock item (seats and pluses alike, so the marker row is constant).
   Muted grayscale = not live; full colour, exactly as the picker shows
   it = LIVE. Above a plus it is the same door as the plus. */
.kd-wheel { line-height: 0; display: block; cursor: pointer; }
.kd-wheel img {
  width: 20px; height: 20px; display: block;
  filter: grayscale(1); opacity: 0.35;
  transition: filter 0.12s ease, opacity 0.12s ease;
}
.kd-wheel:hover img { opacity: 0.6; }
.kd-wheel-live img, .kd-wheel-live:hover img { filter: none; opacity: 1; }

/* THE PLUS - an empty chair; every open seat shows one.
   Drawn as two crossing bars, not a font glyph: a font's "+" sits on
   text metrics that differ per platform and never centre exactly.
   Bars are positioned at the circle's geometric centre and painted
   with currentColor, so the gray-to-white hover still carries. */
.kd-plus {
  position: relative;
  width: 57px; height: 57px; border-radius: 50%; flex-shrink: 0;
  background: #2a2a2a; border: 2px dashed rgba(255, 255, 255, 0.25);
  /* Bar colour muted 40% from the old #9ca3af (each channel at 60%). */
  color: #5e6269; text-decoration: none;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.kd-plus::before, .kd-plus::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  background: currentColor; border-radius: 1px;
}
.kd-plus::before { width: 12px; height: 2px; }
.kd-plus::after  { width: 2px; height: 12px; }
.kd-plus:hover { border-color: rgba(255, 255, 255, 0.5); color: #fff; }

/* THE LIST ICON - last in the row, the second way in.
   Same art as the header's EDITOR tile on purpose: same door, same
   face, same SIZE (26px, 22px on phones) - and the same RENDERED
   mute: the header tile sits on the gray site background, this icon
   on the pure-black backer, so the same 0.3 opacity comes out darker
   here (measured 73 vs 88 luminance). 0.35 rest / 0.64 hover lands
   both doors at identical on-screen brightness. The margin-top
   centres it against the CIRCLE row per rung: the 20px wheel + 4px
   column gap above every item push the circles down 24px, so the
   icon's offset is 24 + (circle - icon)/2. */
.kd-list { margin-top: 36px; line-height: 0; opacity: 0.35; transition: opacity 0.12s ease; }
.kd-list img { height: 26px; width: auto; display: block; }
.kd-list:hover { opacity: 0.64; }

/* The teaching line - a label, never a dialog. */
.kd-note { font-size: 0.8rem; color: #9ca3af; padding: 8px 2px 0; }

/* THE MAIN PAGE HAS NO ADD.
   The grid's add-card is ruled per-render in app.js; the bar's
   mini-add hides here and lives on inside the builder. */
body:not(.building) .bar-tools { display: none; }

/* THE DOCK PAGE HAS NO HISTORY: the REVIEW FEED line runs clean to the
   right margin. The builder keeps its HISTORY button. */
body:not(.building) .history-btn { display: none; }

/* THE TOAST - labels and toasts teach; never a dialog. */
.app-toast {
  display: none; position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  background: #1a1a1a; color: #fff; padding: 12px 22px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.25); z-index: 1000; font-size: 0.9rem;
  max-width: 90vw; text-align: center;
  /* Toasts read ALL CAPS, always - enforced at the render so every
     toast, present and future, obeys without per-string edits. */
  text-transform: uppercase;
}

/* THE 365 LINE: from 365 up the dock is ONE geometry - 57px
   circles + 8px gaps = 317px, the content width at exactly 365,
   edge to edge. Below 365 the row scales with the screen like the
   navigator: the calc keeps five circles + four gaps spanning the
   content (100vw minus the 48px page chrome) at every width,
   meeting 57px seamlessly at the line. */
@media (max-width: 365px) {
  .kd-row { gap: 2.192vw; }
  .kd-seat { width: calc((100vw - 48px - 8.768vw) / 5); }
  .kd-circle, .kd-plus { width: calc((100vw - 48px - 8.768vw) / 5); height: calc((100vw - 48px - 8.768vw) / 5); }
  .kd-plus::before { width: 3.29vw; height: 0.548vw; }
  .kd-plus::after  { width: 0.548vw; height: 3.29vw; }
  .kd-emoji { font-size: 7.2vw; }
  .kd-initial { font-size: 6.14vw; }
  .kd-name { max-width: calc((100vw - 48px - 8.768vw) / 5); }
  .kd-wheel img { width: 5.48vw; height: 5.48vw; }
}

/* THE IPHONE SAFE AREA, CLAIMED: viewport-fit=cover lets the page paint
   edge to edge in an installed app, so the bottom home-indicator zone
   (white by default) wears the site's own gray; the env() cushion keeps
   content clear of the home bar. */
html { background: #1a1a1a; }
body { padding-bottom: env(safe-area-inset-bottom); }

/* THE CALENDAR OPENER SHOWS ITSELF: browsers draw the date field's
   calendar icon black by default - invisible on our dark fields, so
   the picker's one button was hiding in plain sight. Inverted to
   white; typing in the date slots stays exactly as it was. */
.form-group input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.8;
  cursor: pointer;
}

/* THE PHOTOGRAPH RULE (tiny screens): below 600px a card keeps a 2.3:1
   width-to-height shape and everything inside it - text AND graphics -
   scales with the screen, anchored at the 600px line (a size there
   = size/6 in vw units). The card shrinks like a photograph. The
   compact feed view is deliberately untouched (its own pass). */
@media (max-width: 599px) {
  .bill-card { height: auto; aspect-ratio: 2.4 / 1; width: 100%; padding: 3vw 4vw 4vw 4vw; border-radius: 2vw; }
  /* width definite: MORE's grow sets an inline height, and height +
     ratio would compute WIDTH from it (the room-tile lesson). */
  /* MORE-opened: the ratio yields to the text. On arrival the grow
     hands the box height:auto - with a definite width the 2.4 ratio
     would recapture it and snap the card small under the unfurled
     text. Surfaced the first time phones walked a HOME grid page
     (the TICKETS room hop). Open means content-tall. */
  .bill-card.card-open { aspect-ratio: auto; }
  .bill-content { gap: 2vw; }
  /* Titles read up to TWO clamped lines instead of clipping at one.
     The clamp bites the NAME TEXT only (.tclip) - the play button /
     link favicon / collapse icon are siblings the clamp can never
     eat (a long title was eliding its own play button). */
  .card-title .tclip { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.1; vertical-align: bottom; }
  .card-desc { font-size: 2.4vw; max-width: 56.667vw; }
  .desc-more { font-size: 2.133vw; }
  .card-time { gap: 1.333vw; }
  .kronk-count { font-size: 2.267vw; }
  .recurring-indicator img { width: 4vw; height: 4vw; }
  .card-bg { right: 7.333vw; }
  .card-bg img { width: 24vw; height: 24vw; }
  .card-bg-emoji { font-size: 15vw; }
  .autopay-dot { width: 1vw; height: 1vw; }
  .payment-favicon { width: 2.667vw; height: 2.667vw; }
  .bill-card img[src*="BRANDMARK"] { width: 8vw; height: 8vw; }
  /* Section icon unified onto the brandmark mechanic (8vw box, 6.8vw icon
     centred inside - the old flex-flow + fixed 34px drifted off-centre as
     the layout scaled).
     PINNED 16px, NOT 32. The reference is the CAP, which is drawn to the
     card's height and so scales with the screen (32 x height/180 = 7.407vw
     here, ~29px on a 390px phone - it is not a fixed 32px as on desktop).
     Held at 32 the icon floated ~20px inside the cap at every size, well
     into the text beside it; at 16 its art lands 4-7px clear of the cap
     across the phone range, which is close to the flush relationship
     desktop gets from its 16px padding + 16px margin against a 32px cap.
     Still a fixed value, so it still drifts - half as far, and always on
     the safe side of the cap until ~599px, the last pixel before the
     desktop layout takes over. */
  .card-bucket { position: absolute; top: 3vw; right: 16px; width: 8vw; margin-right: 0; }
  .card-bucket-icon { width: 6.8vw; height: 6.8vw; }
}

/* THE PHOTOGRAPH RULE for the kronk tiles + evergreen shelf (ruled
   with the cards: "3 to 1 for kronks too") - same 600px anchor. */
@media (max-width: 599px) {
  /* min-height 0: a GRID ITEM refuses to shrink under its content's
     height, so the title+desc stack set a ~180px floor and the tile's
     height PLATEAUED while width kept narrowing. Released, the 2.4:1
     law truly rules and the tile reduces like the tickets do;
     overflow hidden crops what no longer fits. */
  .kl-card { height: auto; aspect-ratio: 2.4 / 1; min-height: 0; padding: 3vw 8.667vw 4vw 4vw; border-radius: 2vw; }
  .kl-card-title { font-size: 4.59vw; }
  .kl-card-desc { font-size: 4.0vw; margin-top: 1vw; max-width: 90%; }
  .kl-watermark { right: 3.333vw; font-size: 16vw; }
    .kl-wm-seal img { height: 16vw; }
  .kl-ever { padding-right: 21.667vw; }
  .kl-livelight, .kl-dockplus { left: 3.333vw; bottom: 3vw; width: 4vw; height: 4vw; }
  .kl-livelight::before { width: 1.667vw; height: 1.667vw; }
  .kl-dockplus::before { width: 1.667vw; height: 0.333vw; }
  .kl-dockplus::after { width: 0.333vw; height: 1.667vw; }
  .kl-ever-corner { left: 3.333vw; bottom: 3vw; }
  .kl-ever-count { font-size: 3.4vw; }
  .kl-ever-count img { width: 2.333vw; height: 2.333vw; }
  .kl-home { top: 2.333vw; right: 2.667vw; width: 5.667vw; height: 5.667vw; }
  .kl-wheel { right: 2.667vw; bottom: 3vw; }
  .kl-wheel img { width: 5vw; height: 5vw; }
  .kl-soon { right: 2.667vw; bottom: 3vw; font-size: 2.72vw; }
  .kl-plus-card::before { width: 3.667vw; height: 0.333vw; }
  .kl-plus-card::after { width: 0.333vw; height: 3.667vw; }
}

/* PHONES: the wordmark yields up to 20% (32px -> 25.6px) so the
   header's text boxes fit whole instead of truncating. !important
   only because the logo's height rides an inline style attribute
   in every page's markup - one rule here beats ten markup edits. */
@media (max-width: 599px) {
  .logo-section img[src="icons/kronker-logo.png"] { height: 25.6px !important; }
}

/* THE ALL-CAPS BUTTON LAW (the toast law's sibling, ruled July 17,
   2026): every button speaks in caps, enforced at the render -
   never per-string. The avatar rows' spans ride along (Sign In /
   Sign Out are buttons in spirit). */
button, .avatar-item span { text-transform: uppercase; }

/* THE PLAY BUTTON (the media evergreens' first slice) - a small round
   press beside the title; the triangle flips to pause bars while its
   audio plays. Same-breath with the FEED's copy. */
.kr-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin-left: 6px; padding: 0;
  border: none; border-radius: 50%; background: #26a11c;
  cursor: pointer; vertical-align: middle; flex: none; line-height: 0;
}
/* The caret: flex centers its 7px box; the 1px nudge puts the triangle's
   VISUAL weight (left-heavy) dead centre in the circle. */
.kr-play::before {
  content: ''; display: block; width: 0; height: 0;
  border-left: 7px solid #fff;
  border-top: 4.5px solid transparent; border-bottom: 4.5px solid transparent;
  margin-left: 1px;
}
/* Playing: a solid square, the caret's size, dead centre. */
.kr-play.kr-playing::before {
  width: 8px; height: 8px; border: none; margin-left: 0;
  background: #fff;
}

/* THE ARCHIVE PLAYS ON (founder, July 20, 2026): a completed CARTRIDGE
   ticket keeps its full colour and a living play button - the builder's
   archive is a jukebox, not a graveyard. Completed cards only render in
   the builder (the feed and dock carry incomplete ones), so this rule
   reaches exactly that room. The gray border stays as the archive mark;
   ordinary completed cards keep their fade. */
.bill-card.kr-ever.completed { opacity: 1; }

/* THE SPONSORED STATE: personal tool icons ride muted - never
   hidden, always navigable; the feed and the school door stay
   bright (the newsletter's own rooms). */
/* ONE LAYER OF MUTING, AND THE PAGE YOU ARE ON ALWAYS LIGHTS
           (his ruling). Two faults lived here: this rule put a SECOND
           opacity on the IMG on top of the .ab button's own 0.4, which
           compounded to 0.14 and all but buried the icons; and because
           it dressed the IMG while .on lifts the BUTTON, .on could never
           reach it - so the current page's icon stayed muted too. The
           dimming is now the .ab opacity ALONE; a sponsored account
           additionally gets a GRAYSCALE tell on the apps its plan cannot
           open yet, and :not(.on) keeps that off the icon you are on. */
/* THE PICKER HAS TWO STATES: ON AND OFF. Nothing else may touch it.
   The sponsored dress used to add a THIRD - grayscale on every icon except
   EVERYTHING and SCHOOL - so a sponsored member saw on (opacity 1), off
   (0.4 in colour) and off-and-grayed all at once, a distinction no other
   account had. It also arrived LATE, since the class lands only after the
   server answers, which is what read as darkness jumping on after the paint.
   The sponsored state still says its piece where it belongs: the upgrade tip
   and the captured add roads. The navigator just navigates. */
body.kr-sponsored .tooltip-card.kr-up-tip { cursor: pointer; }


/* ---- THE ROOM WEARS THE GROUP'S MARK (the canvas stays KRONKER's) ----
   The KRONK ROOM keeps its dark background on purpose - that change of
   dress IS the threshold between the feed's world and this one. The
   WORDMARK is the exception: it says whose account you are in, so a
   coupled member's group mark belongs here even though the room does
   not change colour. index.html and kronk-list.html therefore set
   KR_BRAND_PAINT = false and leave KR_BRAND_MARK alone.
   Arriving from the feed, the mark is held blank and fades on as the
   group's - kr-brand.js stamps the hold before anything renders and
   lifts it only once that image has actually loaded. The hold is both
   stamped and lifted by that script, so if it ever fails to load the
   mark simply shows the ordinary way.
   The KRONKER glow above is keyed to img[src="icons/kronker-logo.png"]
   and drops away by itself the moment the src is swapped. */
html.kr-mark-hold .brand-stack img { opacity: 0; }
.brand-stack img { transition: opacity 0.35s ease; }

/* THE TEXT LADDER - card text only, no other breakpoints touched:
   title, section line and date each read 10% under their desktop
   size at 599 and 20% under their old 320 size at the floor, one
   straight calc ramp between the anchors. Below 320 the floor block
   keeps scaling in vw; 600 and up stays the desktop sizes. */
@media (max-width: 599px) {
    .card-title { font-size: calc(16.78px + 0.877vw); }
    .card-section { font-size: calc(7.13px + 0.373vw); }
    .card-time { font-size: calc(1.92px + 2.199vw); }
    .card-relative { font-size: calc(-1.47px + 2.165vw); }
}
@media (max-width: 320px) {
    .card-title { font-size: 6.12vw; }
    .card-section { font-size: 2.6vw; }
    .card-time { font-size: 2.8vw; }
    .card-relative { font-size: 1.706vw; }
}

/* Selected text inside the add/edit dialogue's boxes: the native
   selection painted black text on dark blue in these dark inputs -
   unreadable. The selection keeps a blue ground; the text stays
   white. */
.modal input::selection, .modal textarea::selection {
    background: rgba(28, 101, 247, 0.6);
    color: #ffffff;
}

/* THE BUILDER'S CARDS WEAR THE FEED'S LOOK (his ruling: "feed is
   correct"). No description preview - the expand icon opens the card
   and the text arrives, the feed's own flow; the opener wears the
   feed's dress (36px art, 0.75 -> 1); the type wears the feed's
   exact sizes and its ladder. BUILDER SCOPE ONLY - the dock page
   and the apps' own editors keep their dress. */
body.kr-builder .bill-card:not(.card-open) .card-desc { display: none; }
body.kr-builder .desc-more--icon { line-height: 0; opacity: 0.75;
  /* the closer's own title-row seat, nudged 2 up by his eye */
  vertical-align: middle; position: relative; top: -2px; }
body.kr-builder .desc-more--icon:hover { opacity: 1; text-decoration: none; }
body.kr-builder .card-title { font-size: 1.575rem; }
body.kr-builder .card-section { font-size: 1.05rem; }
body.kr-builder .card-time { font-size: 1.275rem; }
body.kr-builder .card-relative { font-size: 0.84rem; }
@media (max-width: 599px) {
  /* The feed sizes its arrows 6vw on phones; the builder pair rides
     the same rule so the two surfaces' arrows match at every width. */
  body.kr-builder .desc-more--icon img,
  body.kr-builder .desc-compact img { width: min(6vw, 30px); height: min(6vw, 30px); }
  body.kr-builder .card-title { font-size: calc(6.42px + 2.715vw); }
  body.kr-builder .card-section { font-size: calc(1.35px + 2.3vw); }
  body.kr-builder .card-time { font-size: calc(2.30px + 2.681vw); }
  body.kr-builder .card-relative { font-size: calc(-1.56px + 2.28vw); }
}
@media (max-width: 320px) {
  body.kr-builder .card-title { font-size: 4.72vw; }
  body.kr-builder .card-section { font-size: 2.72vw; }
  body.kr-builder .card-time { font-size: 3.4vw; }
  body.kr-builder .card-relative { font-size: 1.792vw; }
}

/* THE VERTICAL SQUEEZE (~15%): the dialogue's rhythm compressed -
   group gaps, label gaps, the boxes' vertical padding, the header's
   air and the buttons' top gap all come down together so the form
   reads tighter. Modal scope only; nothing outside the popup moves. */
.modal-content .modal-header { margin-bottom: 0.4rem; }
.modal-content .form-group { margin-bottom: 0.85rem; }
.modal-content .form-group label { margin-bottom: 0.4rem; }
.modal-content .form-group input,
.modal-content .form-group select,
.modal-content .form-group textarea { padding: 0.65rem 0.75rem; }
.modal-content .form-actions { margin-top: 1.25rem; gap: 0.65rem; }

/* THE BUILDER'S ARCHIVER: the box at the KRONK BUILDER line's end.
   Zero height in the row's math (the door's own lesson), dim at
   rest, full when open or under the pointer. Shown by applyBar in
   builder mode only - never in the TICKETS room, never on the dock. */
.dhr-arch { display: none; flex: none; height: 0; align-items: center;
            line-height: 0; opacity: 0.5; background: none; border: none;
            cursor: pointer; padding: 14px; margin: -14px; }
.dhr-arch:hover, .dhr-arch.on { opacity: 1; }
/* The archive view's arrow-only back - the archiver's seat, the same
   zero-height dress. */
.dhr-back { display: none; flex: none; height: 0; align-items: center;
            line-height: 0; opacity: 0.75; background: none; border: none;
            cursor: pointer; color: #ffffff; font-size: 1rem;
            padding: 14px; margin: -14px; }
.dhr-back:hover { opacity: 1; }

/* ICON SEATS MATCH THE FEED (the placement audit): the brandmark at
   the feed's bottom 14 / right 42 wearing the feed's 39px mark, and
   the section icon pinned top-right at the feed's own seat - one
   vertical line, the feed's R2.379 law; the feed's phone tiers ride
   along (top + size scale at 599, the column swaps to 10.5vw at
   380). Builder scope only. */
body.kr-builder .bill-brandmark { bottom: 14px; right: 42px; }
body.kr-builder .bill-brandmark img { width: auto; height: 39px; }
/* THE ANCHOR ARITHMETIC: .card-bucket's positioned ancestor is
   .bill-content, which sits INSIDE the card's 12/16px padding (3vw/4vw
   on phones) - so the feed's card-edge offsets (top 14 / right 42,
   380-tier 10.5vw) are reached by subtracting the padding at each
   tier. The brandmark needs none of this: it anchors to the card
   itself. */
body.kr-builder .card-bucket { position: absolute; top: 2px; right: 26px; margin-right: 0; width: auto; }
body.kr-builder .card-bucket-icon { width: 34px; height: 34px; }
@media (max-width: 599px) {
  body.kr-builder .card-bucket { top: 0.333vw; right: calc(42px - 4vw); }
  body.kr-builder .card-bucket-icon { width: 5.667vw; height: 5.667vw; }
}
@media (max-width: 380px) {
  body.kr-builder .card-bucket { right: 6.5vw; }
  body.kr-builder .bill-brandmark { bottom: 3.333vw; right: 10.5vw; }
  body.kr-builder .bill-brandmark img { height: 9.75vw; }
}

/* THE ADD CARD'S BUFFER: a card due TODAY leads the grid with no
   section header, so it sat against the add card with only the bare
   grid gap - every other group gets its air from the header above
   it. The add card carries the buffer itself on the single-column
   widths where the two stack. */
@media (max-width: 799px) {
  .add-card { margin-bottom: 18px; }
}

/* THE BUILDER'S TOP IS GEAR + AVATAR ALONE: the header's feed and
   kronk-list tiles stand down on builder pages - the KRONK row's own
   door carries the way out. */
body.kr-builder .header-tiles { display: none; }

/* THE BUILDER TITLE ROW IS A FLEX ROW (the feed's R2.300 structure):
   the text clamps and shrinks as a flex child while the arrow pair
   rides as unwrappable siblings - the icons can never fall to the
   subtitle's line however long the title runs. */
body.kr-builder .card-title { display: flex; align-items: center; min-width: 0; }
body.kr-builder .card-title .tclip { flex: 0 1 auto; min-width: 0; }
body.kr-builder .card-title .desc-more--icon,
body.kr-builder .card-title .desc-compact { flex: none; }
