:root {
  --bg1: #0b1b3a;
  --bg2: #103B47;
  --teal: #2ad8c8;
  --violet: #7c6cff;
  --amber: #ffbb5c;
  --green: #4ade80;
  --yellow: #f6d32d;
  /* Default ink for anything sitting directly on the page background, outside
     a .panel/.card (which redefine --paper to --panel-text for their own
     descendants — see the .panel rule below). Tied to --panel-color so ALL
     unpanelled text follows the user's chosen accent, same as page titles. */
  --paper: var(--panel-color);
  --line: rgba(255, 255, 255, 0.12);
  --panel: rgba(255, 255, 255, 0.06);
  --panel-hi: rgba(255, 255, 255, 0.1);
  --muted: var(--panel-color);
  --panel-text: #ffffff;
  --panel-muted: rgba(255, 255, 255, 0.65);
  --panel-line: rgba(255, 255, 255, 0.16);
  --panel-color: #ffffff;
  --danger: #ff6b6b;
  /* Contrast-aware variants of the six fixed accent hues above, used as plain text (e.g.
     Budget's "Category colours" list rows — see BudgetDashboard's PANEL_ACCENT). Static
     fallback matching the vivid hues for first paint; theme.ts recomputes each to a deeper
     shade whenever the panel surface itself is light/bright, where the vivid hue alone
     wouldn't have enough contrast to read as text. */
  --danger-ink: var(--danger);
  --teal-ink: var(--teal);
  --green-ink: var(--green);
  --amber-ink: var(--amber);
  --violet-ink: var(--violet);
  --yellow-ink: var(--yellow);
  --radius: 12px;
  /* Contrast-safe ink for text on accent-colored surfaces (the user's panel
     colour, --panel-color, or a pillar's fixed --amber) — deliberately NOT
     tied to --bg1, which is the customizable page-background and must stay
     free to change without breaking button-text contrast. Static fallback
     for first paint; theme.ts recomputes it against the live panel colour. */
  --btn-ink: #14161c;
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html,
body,
#root {
  min-height: 100%;
}

/* Hide the native page scrollbar everywhere — same treatment as .tabstrip/.block-tabstrip below;
   this is a mobile-first PWA (also wrapped as a native Android/iOS app via Capacitor), where a
   visible scrollbar reads as a web-page leftover rather than part of the UI. Scrolling itself
   still works via touch/trackpad/wheel, just without the (often light-themed, clashing) OS chrome. */
html,
body {
  scrollbar-width: none;
}

html::-webkit-scrollbar,
body::-webkit-scrollbar {
  display: none;
}

html {
  min-height: 100vh;
  min-height: 100dvh;
  background: linear-gradient(160deg, var(--bg1) 0%, var(--bg2) 100%);
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  background: linear-gradient(160deg, var(--bg1) 0%, var(--bg2) 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  color: var(--paper);
  font-family: 'Nunito', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  font-family: 'Baloo 2', system-ui, sans-serif;
  font-weight: 700;
  margin: 0;
  color: var(--paper);
}

/* The largest heading in the app — page/screen titles. Capped at 18px so no
   screen title reads bigger than the topbar title next to it. */
h1 {
  font-size: 18px;
}

/* Body copy — same rule as headings: reads against --panel-color outside a
   panel, --panel-text inside one (inherited already via body, stated
   explicitly here so `p` isn't relying on inheritance alone). */
p {
  color: var(--paper);
}

a {
  color: var(--paper);
  font-weight: 700;
  text-decoration: none;

}

.link-button {
  background: none;
  border: none;
  padding: 0;
  color: var(--paper);
  font-weight: 700;
  text-decoration: none;
}

.link-button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* The ←/→ glyphs center on the font's em-box, which sits visibly higher than the
   label's baseline/x-height (and renders inconsistently across platforms, e.g. Android) —
   nudge them down so they read as centered/same-size wherever they appear. */
.back-arrow,
.nav-arrow {
  display: inline-block;
  position: relative;
  top: 1px;
}

.back-arrow {
  margin-right: 2px;
}

button {
  font-family: inherit;
  cursor: pointer;
}

input,
textarea,
select {
  font-family: inherit;
}

input[type='date']::-webkit-calendar-picker-indicator {
  filter: invert(1);
}

/* Ambient background glow — the "purple fade" ambiance behind panels */
.bg-glow {
  position: fixed;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.32;
  pointer-events: none;
  z-index: 0;
}

.bg-glow-a {
  width: 420px;
  height: 420px;
  background: var(--bg1);
  top: -140px;
  left: -120px;
}

.bg-glow-b {
  width: 420px;
  height: 420px;
  background: var(--bg2);
  bottom: -160px;
  right: -140px;
}

/* Layout */
.app {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
}

.container {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: 12px 20px calc(env(safe-area-inset-bottom, 0px) + 96px);
  position: relative;
  z-index: 1;
}

/* Header */
.topbar {
  position: relative;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: calc(env(safe-area-inset-top, 0px) + 16px) 20px 10px;
  max-width: 760px;
  margin: 0 auto;
  width: 100%;
}

/* Global search — hidden by default, slides down from under the topbar when
   the header's search button is toggled on. */
.search-panel {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: 0 20px;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s ease;
  position: relative;
  z-index: 20;
}

.search-panel.open {
  max-height: min(88vh, 720px);
  opacity: 1;
  overflow-y: auto;
  scrollbar-width: none;
  /* Same reservation as `.container`'s own bottom padding — `.nav-bar` floats fixed near the true
     viewport bottom, above this panel in stacking order, so without this, scrolled-to-the-bottom
     content (e.g. EditEntityPanel's Save/Cancel/Delete row once the colour picker is open) can end
     up positioned underneath it: visible but unclickable, the nav bar silently swallowing the tap. */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 96px);
}

.search-panel::-webkit-scrollbar {
  display: none;
}

.topbar-left,
.topbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
}

.topbar-left {
  flex: 1;
  min-width: 0;
}

.topbar-right {
  flex-shrink: 0;
}

.topbar-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--panel-color);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'Baloo 2', sans-serif;
  font-weight: 700;
  font-size: 20px;
}

.brand-logo {
  width: 30px;
  height: 30px;
  display: block;
  /* Tints via currentColor so it automatically matches whatever ancestor
     sets `color` — literal panel colour in the topbar (.icon-btn), or a
     contrast-safe panel text colour anywhere it sits inside a .panel. */
  background-color: currentColor;
  -webkit-mask-image: url('/atrium-logo.svg');
  mask-image: url('/atrium-logo.svg');
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

/* Icon buttons + dropdown menus */
.icon-btn {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: none;
  color: var(--panel-color);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
  font-size: 18px;
  transition: background 0.12s;
}

.icon-btn:hover {
  background: rgba(255, 255, 255, 0.15);
}

/* Nested inside a .panel, an icon button needs to read against the *panel's*
   colour (--paper, redefined per-panel) rather than the fixed page-chrome
   --panel-color it uses everywhere else (topbar, floating "+" actions) — same
   reasoning as any other panel text. Without this, prev/next month arrows and
   similar controls can go low-contrast against a dark/solid panel. */
.panel .icon-btn {
  color: var(--paper);
}

.icon-btn-active {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.28);
}

.icon-btn-sm {
  width: 33px;
  height: 33px;
  font-size: 14px;
  border: 1px solid var(--panel);
}

/* A small inline (i) button next to a label/heading — distinct from .icon-btn, which is sized
   for topbar chrome (40px) rather than sitting inline next to text. */
.info-btn {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: none;
  background: none;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex-shrink: 0;
  opacity: 0.75;
  transition: opacity 0.12s, background 0.12s;
}

.info-btn:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.12);
}

.panel .info-btn {
  color: var(--paper);
}

.menu-wrap {
  position: relative;
  display: flex;
}

.menu-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 210px;
  background: var(--panel-hi);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 6px;
  /* Text reads against the *panel* colour, same as any other panel surface. */
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.4);
  z-index: 40;
}

.menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--paper);
  padding: 10px 12px;
  border-radius: 12px;
  font-weight: 700;
  font-size: 14px;
}

.menu-item:hover {
  background: rgba(255, 255, 255, 0.08);
}

.menu-item.danger {
  color: var(--danger);
}

.menu-item.selected {
  background: rgba(255, 255, 255, 0.08);
}

/* Nav bar — floats over the scrolling content, anchored to the bottom. The
   pillar tab strip scrolls horizontally inside it; the chat button is a
   sibling outside the scroll area so it's always reachable and never
   scrolled away or covered. */
.nav-bar {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 30;
}

.tabstrip {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  overflow-x: auto;
  padding: 6px 14px;
  scrollbar-width: none;
  /* Without this, attaching pointer handlers to .tab (for long-press reorder)
     makes strict browsers withhold native horizontal panning until JS decides
     whether it's a drag — Android's webview is lax about this, others aren't. */
  touch-action: pan-x;
  background: var(--panel-hi);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: 100px;
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.4);
  /* Text reads against the *panel* colour, same as any other panel surface. */
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
}

.tabstrip::-webkit-scrollbar {
  display: none;
}

.tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
  background: none;
  border: none;
  text-decoration: none;
  padding: 0;
  touch-action: pan-x;
  cursor: pointer;
}

.tab.dragging {
  opacity: 0.6;
  transform: scale(1.08);
  z-index: 5;
}

.tab-circle {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  background: rgba(255, 255, 255, 0.06);
  /* Neutral resting border (glass-aware --panel-line) — a chosen --tab-color only shows once
     this tab is the active one (below), so the strip isn't every tab's colour lit up at once. */
  border: 2px solid var(--panel-line);
  color: var(--paper);
  transition: border-color 0.15s, transform 0.15s;
}

/* Tints a standalone .tab-circle (outside a .tab strip's active/inactive state) with its owner's
   colour — used by Settings' pillar-order list, where every row shows its pillar's colour at once
   rather than only the selected one. */
.tab-circle-tinted {
  border-color: var(--tab-color, var(--paper));
}

.tab.active .tab-circle {
  border-color: var(--tab-color, var(--paper));
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.06);
}

.tab-label {
  font-size: 9px;
  font-weight: 700;
  color: var(--muted);
  max-width: 46px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tab.active .tab-label {
  color: var(--paper);
}

/* Anchors a .menu-panel to the block tab strip's "+Add" end without imposing .menu-wrap's flex layout on the strip itself. */
.block-tabstrip-wrap {
  position: relative;
}

/* Per-pillar block tab strip — static, top-of-page (distinct from the floating .tabstrip) */
.block-tabstrip {
  display: flex;
  align-items: center;
  gap: 10px;
  overflow-x: auto;
  padding: 4px 2px 14px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
  scrollbar-width: none;
  /* See .tabstrip — same long-press-reorder vs. native-pan tradeoff. */
  touch-action: pan-x;
}

.block-tabstrip::-webkit-scrollbar {
  display: none;
}

/* Docked at the right end of .nav-bar, outside the scrolling .tabstrip —
   always reachable no matter how many pillars there are or how far the
   strip is scrolled. */
.chat-fab {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-hi);
  backdrop-filter: blur(14px);
  border: 2px solid var(--line);
  /* The circle itself *is* --panel-hi, so the icon needs the contrast-safe
     panel text colour — a literal colour here can equal its own backdrop
     (e.g. in Solid mode, where --panel-hi literally is the panel colour). */
  color: var(--panel-text);
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.4);
  transition: transform 0.15s;
  outline: none;
}

.chat-fab.active,
.chat-fab:focus-visible {
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.16), 0 10px 24px rgba(0, 0, 0, 0.4);
}

.tab-circle.add {
  border-style: dashed;
  color: var(--muted);
}

/* Icon picker — an inline dropdown that expands the surrounding panel (not a
   floating popup), so the full grid is always on-screen and never clipped. */
.icon-picker-panel {
  margin-top: 8px;
  background: var(--panel-hi);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 8px;
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
}

.icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 8px;
  padding: 4px;
  /* ~3 rows of 34px icon buttons + gaps; a 4th row scrolls instead of growing further. */
  max-height: 130px;
  overflow-y: auto;
}

.icon-choice {
  width: 34px;
  height: 34px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  color: var(--paper);
  font-size: 17px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.icon-choice.selected {
  border-color: var(--paper);
  background: color-mix(in srgb, var(--panel-color) 20%, transparent);
}

/* Clears the icon back to the tab strip's own initial-letter fallback (see BlockTabs.tsx) —
   dashed to read as "unset" rather than a real icon choice, same convention as .tab-circle.add. */
.icon-choice-clear {
  border-style: dashed;
  color: var(--muted);
}

/* Colour picker */
.color-picker-wrap {
  position: relative;
  margin-top: 6px;
}

.color-swatch-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 8px 12px;
  color: var(--paper);
}

.color-swatch {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--line);
  display: block;
}

.color-swatch-hex {
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.02em;
}

/* An inline dropdown (not an absolutely-positioned popup): it sits in normal
   flow below the swatch button, so the panel around it grows to fit rather
   than the picker floating over — and potentially hiding — other content. */
.color-picker-panel {
  margin-top: 8px;
  width: 100%;
  max-width: 320px;
  box-sizing: border-box;
  background: var(--panel-hi);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px;
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
}

.color-sv-square {
  position: relative;
  width: 100%;
  height: 140px;
  border-radius: 12px;
  touch-action: none;
  cursor: crosshair;
}

.color-sv-thumb {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.color-hue-slider {
  position: relative;
  width: 100%;
  height: 16px;
  margin-top: 12px;
  border-radius: 100px;
  touch-action: none;
  cursor: pointer;
  background: linear-gradient(
    to right,
    #ff0000,
    #ffff00,
    #00ff00,
    #00ffff,
    #0000ff,
    #ff00ff,
    #ff0000
  );
}

.color-hue-thumb {
  position: absolute;
  top: 50%;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.color-hex-input {
  text-transform: uppercase;
}

.color-quick-swatches {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  flex-wrap: wrap;
}

.color-quick-swatch {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--line);
  padding: 0;
}

/* "No colour" quick swatch — a checkerboard rather than a flat fill, the conventional way to
   represent transparent, since an actually-transparent circle would just look identical to
   empty space around it. Sets Block/PillarSection.color to the literal CSS keyword
   'transparent', which --tab-color (see .tab-circle) and every other border-color consumer
   already understand natively. */
.color-quick-swatch-transparent {
  background:
    linear-gradient(45deg, rgba(128, 128, 128, 0.5) 25%, transparent 25%, transparent 75%, rgba(128, 128, 128, 0.5) 75%),
    linear-gradient(45deg, rgba(128, 128, 128, 0.5) 25%, transparent 25%, transparent 75%, rgba(128, 128, 128, 0.5) 75%);
  background-size: 8px 8px;
  background-position: 0 0, 4px 4px;
}

/* Marks the selected preset with the same neutral chrome line every other border in the app
   uses (glass-aware --panel-line — see .tab-circle), offset out from the swatch's own edge —
   not any palette colour (including var(--panel-color)), which would vanish the moment that's
   the swatch actually selected (e.g. a teal ring around the teal swatch). */
.color-quick-swatch.selected {
  box-shadow: 0 0 0 3px var(--panel-line);
}

/* Panels & cards — text inside always reads against the panel's own
   colour (--panel-text/--panel-muted), not the page background, so a dark
   panel on a light page (or vice versa) stays legible. Redefining --paper/
   --muted here lets every descendant (.muted, .field, headings, …) pick it
   up through normal inheritance without touching each one individually. */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 12px;
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  color: var(--paper);
}

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  text-align: left;
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  color: var(--paper);
  width: 100%;
  transition: transform 0.12s, border-color 0.12s;
}

.card:hover {
  transform: translateY(-2px);
}

/* Dashboard block sizing: a 'full' block spans the whole grid row. */
.card-full {
  grid-column: 1 / -1;
}

.wishlist-boards-full .wishlist-item-img {
  height: 400px;
}


.wishlist-item-img {
  width: 100%;
  height: 200px;
  object-fit: cover;
  border-radius: 12px;
  background: var(--panel-hi);
}

.wishlist-item-img-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
}

/* Fixed 2-up grid — used for wishlist items (always half-width, no matter the
   viewport) and, when a Wishlist pillar's board display is set to "Half
   row", for its board cards too. Unlike the shared `.grid`, this never
   collapses to 1 column or expands to 3–4. */
.wishlist-items-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

/* Fixed 1-up grid — a Wishlist pillar's board display (or a board's own item
   display) set to "Full row": one full-width card per row at every viewport. */
.wishlist-boards-full {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}

/* Compact list rows — a Wishlist pillar's board display set to "List": name
   + item count, no card chrome. */
.wishlist-board-rows {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* A `.row row-between` composition — see usage (`className="row row-between
   wishlist-board-row"`). Also used on plain `<button>` rows (e.g. DayTrackerScreen),
   hence the button-chrome reset below — harmless no-ops on an `<a>`. */
.wishlist-board-row {
  width: 100%;
  background: none;
  border: none;
  font: inherit;
  cursor: pointer;
  padding: 14px 4px;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  text-align: left;
  color: var(--paper);
  font-weight: 700;
}

.wishlist-board-row:last-child {
  border-bottom: none;
}

/* Compact list rows for a board's own items — a board's item display set to
   "List": thumbnail + name/price, no card chrome. */
.wishlist-items-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* A `.row` composition — see usage (`className="row wishlist-item-row"`). */
.wishlist-item-row {
  padding: 10px 4px;
  border-bottom: 1px solid var(--line);
}

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

.wishlist-item-row-img {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: 12px;
  background: var(--panel-hi);
  font-size: 20px;
  cursor: pointer;
}

.wishlist-item-row-info {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  cursor: pointer;
}

/* A board's "Show checkboxes" option — checking an item moves it into the
   Completed panel at the bottom of the board (see BoardDetail.tsx). */
.wishlist-checkbox {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  accent-color: var(--panel-color);
  cursor: pointer;
}

.wishlist-item-name-done {
  text-decoration: line-through;
  opacity: 0.6;
}

.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}

@media (max-width: 520px) {
  .grid {
    grid-template-columns: 1fr;
  }
}

/* Tablet+ */
@media (min-width: 900px) {
  .container,
  .topbar,
  .search-panel {
    max-width: 980px;
  }

  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 1200px) {
  .container,
  .topbar,
  .search-panel {
    max-width: 1120px;
  }

  .grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: inline-block;
}

.small {
  font-size: 13px;
}

/* Forms */
.field {
  display: block;
  margin: 12px 0 12px 0;
  font-weight: 700;
  font-size: 14px;
  color: var(--paper);
}

/* Separates stacked edit/settings sections (Name/Icon/Border, Panel layout/colours, etc.) that
   would otherwise run together with only a .field label's margin between them. */
.divider {
  border: none;
  border-top: 1px solid var(--line);
  margin: 18px 0;
}

.reveal-toggle {
  background: none;
  border: none;
  color: var(--panel-color);
  font-weight: 700;
  padding: 0;
}

.or-divider {
  text-align: center;
  color: var(--muted);
  font-weight: 700;
  margin: 12px 0;
}

.input,
.textarea,
.select {
  width: 100%;
  margin-top: 6px;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.06);
  color: var(--paper);
  font-size: 14px;
  font-weight: 600;
}

.select {
  padding-right: 46px;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 8l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 18px center;
  background-size: 12px;
  cursor: pointer;
}

.textarea {
  min-height: 90px;
  resize: vertical;
}

.input::placeholder,
.textarea::placeholder {
  color: rgba(255, 255, 255, 0.45);
}

input[type='color'].input {
  padding: 4px;
  height: 44px;
  cursor: pointer;
}

.row {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

/* Row modifiers — combine with `.row` instead of ad hoc inline flex styles,
   so every pillar composes the same row behaviour. */
.row-between {
  justify-content: space-between;
}

.row-end {
  justify-content: flex-end;
}

.row-top {
  align-items: flex-start;
}

.row-nowrap {
  flex-wrap: nowrap;
}

.row-tight {
  gap: 6px;
}

.shrink-0 {
  flex-shrink: 0;
}

/* Spacing scale — named sizes, not pixel values, so a glance at the class
   tells you the gap without doing math: xs/sm/md/lg/xl only, ever.
   `-none` is the one deliberate exception in each direction: it cancels a
   class's built-in margin (e.g. `.header-row`) when content follows with
   no gap. */
.mt-none { margin-top: 0; }
.mt-xs { margin-top: 4px; }
.mt-sm { margin-top: 8px; }
.mt-md { margin-top: 12px; }
.mt-lg { margin-top: 16px; }
.mt-xl { margin-top: 24px; }

.mb-none { margin-bottom: 0; }
.mb-xs { margin-bottom: 4px; }
.mb-sm { margin-bottom: 8px; }
.mb-md { margin-bottom: 12px; }
.mb-lg { margin-bottom: 16px; }
.mb-xl { margin-bottom: 24px; }

.ml-sm { margin-left: 8px; }

/* Generic layout/utility primitives — for the handful of static, non-data-
   driven inline styles that don't fit a spacing/row modifier above. */
.cursor-pointer { cursor: pointer; }
.relative { position: relative; }
.hidden { display: none; }
.block { display: block; }
.w-full { width: 100%; }
.flex-1 { flex: 1 1 0%; }
.flex-full { flex: 1 1 100%; }
.nowrap { white-space: nowrap; }
/* Preserves line breaks in free-text (e.g. multi-line notes from a CSV import) while still
   wrapping normally — plain text otherwise collapses embedded newlines into a single space. */
.preserve-lines { white-space: pre-line; }
.text-center { text-align: center; }
.text-left { text-align: left; }
/* Same weight/size as `.field`'s label text, for a plain non-form label
   (e.g. next to a standalone toggle) that isn't wrapping an input. */
.emoji-lg { font-size: 36px; }

/* A panel nested inside another panel needs a subtly different fill so the
   boundary between the two still reads, without inventing a whole new
   surface colour. */
.panel-nested { background: rgba(255, 255, 255, 0.03); }
/* A fixed "success" ink, independent of the user's chosen --panel-color —
   signals a boolean state (key saved, passkey added), not page branding. */
.text-success { color: var(--teal); }

/* A Link/anchor wrapping non-link-looking content (a card, a row) — undoes
   the global `a` rule's implied "this is a hyperlink" look. Also used on
   plain <button>s wrapping non-button content, so it resets native button
   chrome (background/border/padding/font) too — otherwise the browser's
   default grey button box shows through around the wrapped content. */
.link-reset {
  text-decoration: none;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: inherit;
  cursor: pointer;
}

/* Status dot fill — paired with the `.dot` shape, for a boolean on/off
   indicator (key saved, passkey added) that's always teal/muted regardless
   of panel colour, same reasoning as `.text-success`. */
.dot-on { background: var(--teal); }
.dot-off { background: var(--muted); }

/* The "add new item" row at the top of every pillar shell list. */
.shell-actions {
  justify-content: flex-end;
  margin-bottom: 18px;
}

/* A field's label sits directly above its control — every control type
   (.input/.textarea/.select and a `.row` of controls, e.g. mood emoji or
   quick actions) shares the same top offset so spacing above and below
   stays symmetric regardless of which control the field wraps. */
.field > .row {
  margin-top: 6px;
}

/* Buttons */
.btn {
  border: none;
  border-radius: 100px;
  padding: 12px 22px;
  font-weight: 800;
  font-size: 14px;
  background: var(--panel-color);
  color: var(--btn-ink);
  transition: filter 0.12s, transform 0.12s;
}

.btn:hover:not(:disabled) {
  filter: brightness(1.08);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* A <button disabled> can't be used where the element must stay an <a> (e.g. a mailto: link that's
   only "clickable" once a field is filled in) — this is the same visual treatment for that case. */
.btn-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

.btn-ghost {
  background: transparent;
  color: var(--paper);
  border: 1px solid var(--line);
}

.btn-danger {
  background: transparent;
  color: var(--danger);
  border: 1px solid rgba(255, 107, 107, 0.4);
}

.btn-sm {
  padding: 7px 14px;
  font-size: 13px;
}

/* Centered auth screen */
.center {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  position: relative;
  z-index: 1;
}

.center .panel {
  max-width: 420px;
  width: 100%;
}

.notice {
  font-size: 13px;
  line-height: 1.6;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px;
  margin-top: 16px;
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  color: var(--paper);
}

.error {
  color: var(--danger);
  font-size: 13px;
  font-weight: 700;
  margin-top: 8px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Same vertical padding + font-size as .btn-sm so a pill sitting next to a
     small button is always the same height. */
  padding: 7px 14px;
  border-radius: 100px;
  background: var(--panel-hi);
  border: 1px solid var(--line);
  font-size: 13px;
  font-weight: 700;
}

/* Text-colour modifiers for a .pill used as a confirmed/not-confirmed badge (Profit-First's
   transfer check, Debt-Elimination's this-cycle payment check) — same ink tokens PANEL_ACCENT
   already uses for contrast-safe text. */
.pill-success {
  color: var(--green-ink);
}

.pill-warn {
  color: var(--danger-ink);
}

/* Chat */
.chat-log {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Clears the fixed .chat-compose bar below, which sits outside normal document flow — without
     this the last message can end up hidden behind it. */
  margin-bottom: 72px;
}

.bubble {
  padding: 12px 16px;
  border-radius: 12px;
  max-width: 85%;
  white-space: pre-wrap;
  line-height: 1.5;
  font-weight: 600;
  font-size: 14px;
}

/* Both bubbles follow --panel/--panel-text — the same glass-or-solid-respecting pair every
   .card/.panel uses — not --panel-color (the raw chosen accent, always opaque regardless of
   theme mode). Using --panel-color here made a bubble always render as a solid block of that
   colour even in glass mode, and paired it with --panel-text computed for the *glass* surface
   (bg1) — with the default white accent colour, that's white text on a white bubble. */
.bubble.user {
  align-self: flex-end;
  background: var(--panel);
  border-bottom-right-radius: 4px;
  color: var(--panel-text);
}

.bubble.assistant {
  align-self: flex-start;
  background: var(--panel);
  border-bottom-left-radius: 4px;
  color: var(--panel-text);
}

/* Fixed (not sticky) to the true viewport bottom, same as .nav-bar — sticky only engages once its
   scrolling ancestor actually overflows, so on a short conversation (nothing to scroll) it just sat
   in normal flow right after the last bubble instead of staying pinned above the nav bar. */
.chat-compose {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 96px);
  max-width: 760px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  z-index: 20;
}

/* The compose input sits directly on the page background (not inside a .panel), so it needs its
   own panel-aware colors instead of the generic .input rule's fixed --line border and hardcoded
   placeholder rgba — otherwise both can end up low-contrast against a customized page background.
   --panel/--panel-text (not --panel-color) so it respects glass-vs-solid theme mode the same way
   every .card/.panel does — --panel-color is the raw chosen accent, always opaque, which (paired
   with --panel-text computed for the *glass* surface) rendered as invisible white-on-white text
   with the default white accent colour. margin-top: 0 overrides the generic .input rule's 6px
   top margin (meant for a field that follows a `<label>` above it, which this one has none of) —
   left in, this pushed the input down out of line with the camera/Send buttons beside it. */
.chat-compose .input {
  margin-top: 0;
  background: var(--panel);
  color: var(--panel-text);
  border: 1px solid var(--panel-line, var(--line));
}

.chat-compose .input::placeholder {
  color: var(--panel-text);
  opacity: 0.6;
}

.chat-bubble-photos {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chat-bubble-photo {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border-radius: 8px;
  display: block;
}

/* Both are `.row row-between` compositions, not standalone layouts — see
   their usages (always `className="row row-between header-row"` etc). */
.header-row {
  /* Matches .btn-sm's rendered height so the title stays put whether or not this
     particular row happens to have a trailing button (e.g. switching between blocks
     that do and don't show one shouldn't make the title jump). */
  min-height: 30px;
  margin-bottom: 18px;
}

.entry-line {
  width: 100%;
  padding: 10px 0;
  border-top: 1px solid var(--line);
  flex-wrap: nowrap;
  /* Overrides .row's align-items: center — every entry-line row pairs a (often multi-line) label
     block with a trailing value/badge, which should sit level with the first line, not centered
     against the whole row's height. */
  align-items: flex-start;
}

.entry-line:first-child {
  border-top: none;
}

/* Keep the trailing value (amount, date, icon…) pinned to the right instead of
   wrapping below when the leading content (e.g. a long imported note) is long
   enough to otherwise overflow the row. */
.entry-line > :first-child {
  min-width: 0;
  flex: 1 1 auto;
}

/* :not(:only-child) — an .entry-line with a single child (no separate trailing amount/badge, e.g.
   a plain Note entry with editingEntries off) would otherwise match *both* this and :first-child
   above; this rule's flex-shrink: 0 (meant only for a genuine trailing value) doesn't need to,
   and previously did, win that tie and cancel :first-child's shrink-to-wrap behavior. */
.entry-line > :last-child:not(:only-child) {
  flex-shrink: 0;
}

/* Generic "this text might be long, never let it push its row wider than the screen" class —
   Ideas' list/card rows, a plain Note block's entry list, and anywhere else a record's title/notes
   text needs it. The base rule alone covers non-flex contexts (e.g. a card in Ideas' grid/full-row
   layout); the `.entry-line >` rule adds the flex-shrink belt-and-braces on top of the
   :not(:only-child) fix above, for when it's a single-child .entry-line's text — guarantees
   shrink-to-wrap at `.entry-line > .foo` specificity regardless of any future rule ordering, since
   min-width: 0 (shrinking) alone doesn't cover a single very long unbroken token. */
.entry-line-text {
  overflow-wrap: break-word;
  word-break: break-word;
}

.entry-line > .entry-line-text {
  min-width: 0;
  flex-shrink: 1;
}

/* Budget feature */
.expense-line-category {
  font-size: 14px;
}

.expense-line-amount {
  font-size: 14px;
  font-weight: 400;
}

.subnav {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.subnav-link {
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border-radius: 100px;
  border: 1px solid var(--panel);
  background: rgba(255, 255, 255, 0.04);
  color: var(--muted);
  font-weight: 700;
  font-size: 13px;
  text-decoration: none;
}

.subnav-link.active {
  background: var(--teal);
  color: var(--btn-ink);
  border-color: transparent;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

@media (min-width: 640px) {
  .stat-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.stat-tile {
  color: var(--panel-text);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
}

/* A stat-tile set to the "Full row" display mode (see BudgetSectionSettings) spans every
   column of its .stat-grid instead of sitting in the normal 2/3-up flow. */
.stat-tile-full {
  grid-column: 1 / -1;
}

/* Per-panel accent colouring for the Budget dashboard (see BudgetSectionSettings' "Panel
   colours" toggle) — the accent itself is set per-tile via the inline `--panel-accent` custom
   property (BudgetDashboard's PANEL_ACCENT map), not hardcoded here, since which panel gets
   which colour is fixed data, not a layout rule. Card mode tints the tile's background+border;
   list mode tints just the row's text. */
.stat-tile-accent {
  background: color-mix(in srgb, var(--panel-accent) 18%, var(--panel));
  border-color: var(--panel-accent);
}

.entry-line-accent {
  color: var(--panel-accent);
}

/* The big percentage inside the budget gauge — bigger than the stat-tile
   values below it, since it's the dashboard's single headline number. */
.gauge-pct {
  font-size: 22px;
}

.stat-tile .stat-value {
  color: var(--panel-text);
  font-family: 'Baloo 2', system-ui, sans-serif;
  font-size: 20px;
  font-weight: 700;
}

.progress-track {
  height: 8px;
  border-radius: 100px;
  border: 1px solid var(--line);
  background: var(--line);
  overflow: hidden;
  margin-top: 8px;
}

.progress-fill {
  height: 100%;
  border-radius: 100px;
  background: var(--panel-color);
}

.progress-fill.over {
  background: var(--danger);
}

.gauge {
  --gauge-pct: 0;
  --gauge-color: var(--amber);
  position: relative;
  width: 140px;
  height: 140px;
  border-radius: 50%;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The ring lives on a pseudo-element (not .gauge itself) so the mask that punches its
   transparent centre only cuts the ring layer, not the .gauge-inner text sitting on top
   of it — masking .gauge directly would erase the text along with the hole. */
.gauge::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(
    var(--gauge-color) calc(var(--gauge-pct) * 1%),
    rgba(255, 255, 255, 0.08) 0
  );
  -webkit-mask-image: radial-gradient(circle, transparent 54px, #000 54px);
  mask-image: radial-gradient(circle, transparent 54px, #000 54px);
}

.gauge-inner {
  position: relative;
  width: 108px;
  height: 108px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

.gauge-pct.over-budget {
  color: var(--danger-ink);
}

/* A smaller ring for the 50/30/20 view's three side-by-side gauges — the base .gauge is
   hard-coded to 140px, which only fits one at a time. Keeps the same ring-thickness proportion
   (roughly 1:0.7 mask-to-outer radius) rather than just shrinking the outer box. */
.gauge-row {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}

.gauge-sm {
  width: 90px;
  height: 90px;
}

.gauge-sm::before {
  -webkit-mask-image: radial-gradient(circle, transparent 32px, #000 32px);
  mask-image: radial-gradient(circle, transparent 32px, #000 32px);
}

.gauge-sm .gauge-inner {
  width: 64px;
  height: 64px;
}

.gauge-sm .gauge-pct {
  font-size: 14px;
}

.toggle-btn {
  border: 1px solid var(--line);
  border-radius: 100px;
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 800;
  background: transparent;
  color: var(--muted);
}

.toggle-btn.on {
  background: var(--panel-color);
  color: var(--btn-ink);
  border-color: transparent;
}

.calendar-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  margin-bottom: 6px;
}

.calendar-weekday {
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
}

.calendar-day {
  aspect-ratio: 1;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: var(--muted);
}

.calendar-day.has-bill {
  border-color: var(--amber);
  color: var(--paper);
  font-weight: 700;
}

.calendar-day.selected {
  background: var(--amber);
  border-color: transparent;
  color: var(--btn-ink);
}

.calendar-day.empty {
  background: none;
  border: none;
}

button.calendar-day {
  padding: 0;
  margin: 0;
}

.date-group {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 10px;
  overflow: hidden;
}

.date-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  gap: 12px;
  padding: 14px 16px;
  background: var(--panel);
  border: none;
  /* Has its own panel-coloured surface, so it reads against --panel-text like
     any other panel, not the page's outside-panel --panel-color ink. */
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  color: var(--paper);
  font-weight: 700;
  font-size: 14px;
  text-align: left;
}

.date-group-caret {
  display: inline-block;
  width: 14px;
  color: var(--muted);
}

.date-group-body {
  padding: 0 16px;
}

.date-group-body .entry-line:first-child {
  border-top: none;
}

/* Confirm dialog */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(4, 8, 20, 0.62);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 20px;
  /* Same reservation as .container's own bottom padding and .search-panel.open — .nav-bar floats
     fixed near the true viewport bottom, above this overlay's centered content in stacking order,
     so without this a tall modal's bottom edge (e.g. Spending habits' Close button) can end up
     rendered underneath it instead of clearing it. */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 96px);
}

.modal-panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 22px;
  max-width: 380px;
  width: 100%;
  max-height: 80vh;
  overflow-y: auto;
  scrollbar-width: none;
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  color: var(--paper);
}

.modal-panel::-webkit-scrollbar {
  display: none;
}

/* Business pillar (Time Tracking) */
.calendar-day.has-time {
  border-color: var(--green);
  color: var(--paper);
  font-weight: 700;
}

/* Harvest (blocks-pro/harvest) — orange rather than Time Tracking's green, so its read-only
   calendar reads as visually distinct from a manually-logged Time Tracking section. */
.calendar-day.has-harvest-time {
  border-color: var(--amber);
  color: var(--paper);
  font-weight: 700;
}

/* Mind pillar (Journal) */
.calendar-day.has-mood {
  border-color: var(--violet);
  color: var(--paper);
  font-size: 16px;
}

.calendar-day.today {
  box-shadow: 0 0 0 2px var(--panel-color);
}

/* Day Tracker */
.calendar-day.has-streak {
  border-color: var(--amber);
  color: var(--paper);
  font-size: 16px;
}

.calendar-day.has-slip {
  border-color: var(--danger);
  color: var(--paper);
  font-weight: 700;
}

.journal-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.journal-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  display: block;
  text-decoration: none;
  /* Has its own panel-coloured surface — same reasoning as .panel/.card. */
  --paper: var(--panel-text);
  --muted: var(--panel-muted);
  color: var(--paper);
  transition: transform 0.12s;
}

.journal-card:hover {
  transform: translateY(-2px);
}

.journal-card-top {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.journal-date-badge {
  flex: 0 0 auto;
  width: 44px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.08);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 0;
}

.journal-date-month {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--muted);
}

.journal-date-day {
  font-family: 'Baloo 2', system-ui, sans-serif;
  font-size: 18px;
  font-weight: 700;
}

.journal-card-title {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.journal-card-title strong {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.journal-draft-pill {
  flex: 0 0 auto;
  background: rgba(255, 187, 92, 0.18);
  color: var(--amber);
}

.journal-bookmark {
  flex: 0 0 auto;
}

/* Forces the bookmark ribbon icon red regardless of its button's own fill/ghost ink. */
.icon-bookmark {
  display: inline-flex;
  vertical-align: middle;
  color: var(--danger);
}

/* An SVG icon sitting inline next to text (e.g. CameraIcon in "Add receipt") defaults to
   `vertical-align: baseline`, which aligns the icon's *bottom* edge to the text baseline — for an
   icon taller than the text's x-height that reads as sitting noticeably too high. */
.icon-inline {
  vertical-align: middle;
}

.journal-photo-grid {
  display: grid;
  gap: 6px;
  margin-top: 12px;
  border-radius: 12px;
  overflow: hidden;
}

.journal-photo-grid.photos-1 {
  grid-template-columns: 1fr;
}

.journal-photo-grid.photos-2,
.journal-photo-grid.photos-3 {
  grid-template-columns: 2fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.journal-photo-grid.photos-1 .journal-photo,
.journal-photo-grid.photos-1 .journal-photo-wrap {
  aspect-ratio: 16 / 9;
}

.journal-photo-grid.photos-2 img:first-child,
.journal-photo-grid.photos-3 img:first-child,
.journal-photo-grid.photos-2 .journal-photo-wrap:first-child,
.journal-photo-grid.photos-3 .journal-photo-wrap:first-child {
  grid-row: 1 / 3;
}

.journal-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  aspect-ratio: 1;
}

.journal-photo-wrap {
  position: relative;
}

.journal-photo-remove {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: none;
  background: rgba(4, 8, 20, 0.75);
  color: var(--paper);
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.journal-card-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
}

.journal-title-input {
  font-family: 'Baloo 2', system-ui, sans-serif;
  font-size: 22px;
  font-weight: 700;
  background: transparent;
  padding: 10px 14px;
}

.journal-body-input {
  background: transparent;
  padding: 12px 14px;
  font-size: 16px;
  min-height: 220px;
}

/* The box sizes to the formatted display text (so long formats like "Wednesday,
   15th November" fit in full) — the real <input type="date"> is stretched to
   cover it via inset:0 so it stays fully clickable/functional while invisible.
   min-width keeps short formats (e.g. "14/07/2026") wide enough that Chromium
   doesn't clip its own calendar-icon control. */
.date-field {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-width: 130px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
}

.date-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 8px 0 0;
  border: none;
  background: transparent;
  color: transparent;
  caret-color: transparent;
}

.date-input-display {
  padding: 10px 32px 10px 10px;
  font-weight: 700;
  font-size: 13px;
  color: var(--paper);
  pointer-events: none;
  white-space: nowrap;
}

.journal-location-input {
  background: none;
  border: none;
  color: var(--paper);
  font-weight: 700;
  font-size: 12px;
  width: 120px;
}

.journal-location-clear {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 11px;
  padding: 0;
}

.journal-mood-selected {
  border: 1px solid var(--paper);
}

/* The "Pillar · Block" identifier a Dashboard widget card shows for itself — deliberately small
   and last in reading order, under the widget's actual data, since the data (not which pillar/
   block it's from) is what the card exists to surface. */
.widget-label {
  font-size: 12px;
  color: var(--muted);
  margin-top: 10px;
}

.widget-label-tick {
  display: inline-flex;
  vertical-align: middle;
  color: var(--green-ink);
  margin-right: 4px;
}

.dashboard-widget-controls {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 5;
}

/* The whole card is the drag handle while editing the dashboard — press and drag anywhere on it
   (other than the Delete button, which stops the pointerdown reaching this) to reorder. */
.dashboard-widget-editing {
  cursor: grab;
  touch-action: none;
}

.card-full.dragging {
  opacity: 0.6;
  transform: scale(1.02);
  z-index: 5;
}

.receipt-photo-row {
  flex-wrap: wrap;
}

.receipt-photo-wrap {
  position: relative;
  width: 64px;
  height: 64px;
}

.receipt-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 8px;
}

.receipt-photo-view {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
}

/* Small circular remove control on a receipt thumbnail — same corner-overlay treatment as
   .journal-photo-remove. */
.receipt-photo-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: none;
  background: rgba(4, 8, 20, 0.75);
  color: var(--paper);
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Full-size receipt viewer, opened by tapping a thumbnail — reuses .modal-overlay for the
   click-outside-to-close backdrop, but its own panel sized for an image, not a form. */
.receipt-lightbox {
  position: relative;
  max-width: min(90vw, 640px);
  max-height: 90vh;
}

.receipt-lightbox-image {
  display: block;
  max-width: min(90vw, 640px);
  max-height: 90vh;
  border-radius: 12px;
  object-fit: contain;
}

.receipt-lightbox-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: rgba(4, 8, 20, 0.75);
  color: #fff;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}

