:root {
  --bg: #12141a;
  --surface: #1b1e27;
  --surface-2: #23273233;
  --border: #2c3140;
  --text: #eef0f5;
  --text-dim: #9aa1b2;
  --accent: #7c9eff;
  --accent-active: #ff6b6b;
  --radius: 16px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  /* Static 100vh is a known iOS-PWA trap: after the on-screen keyboard
     opens and closes (e.g. tapping into and out of the description box),
     Safari can leave the layout viewport height "stuck" at the shrunk
     value, so everything below the top of the page — including the
     topbar — renders shifted from where it should be, even after
     switching tabs. dvh recalculates against the real, current viewport,
     so it doesn't get left behind when the keyboard closes. Kept as a
     fallback first for the handful of older browsers that don't know
     dvh — they just ignore the second declaration and keep this one. */
  min-height: 100vh;
  min-height: 100dvh;
}

.app {
  max-width: 640px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
  gap: 16px;
  flex-wrap: wrap;
}

.topbar h1 {
  font-size: 1.4rem;
  margin: 0;
  letter-spacing: -0.01em;
}

.topbar-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  /* Always its own full-width row below the title, rather than only
     sometimes wrapping there depending on available width — that's what
     let the profile button end up stranded short of the actual right edge
     instead of flush against it (space-between on .topbar has nothing to
     push against when this is the only thing left on its wrapped line). */
  flex: 1 1 100%;
}

.profile-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
  cursor: pointer;
}

.profile-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* ---------- profile page ---------- */

.profile-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
}

.profile-title {
  margin: 0;
  font-size: 1.15rem;
}

.profile-email {
  margin: 6px 0 20px;
  color: var(--text-dim);
  font-size: 0.85rem;
  word-break: break-all;
}

.profile-menu {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.profile-menu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: transparent;
  border: none;
  border-radius: 10px;
  color: var(--text);
  padding: 12px 10px;
  font-size: 0.95rem;
  text-align: left;
  cursor: pointer;
}

.profile-menu-item:hover { background: var(--surface-2); }

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

.logout-confirm {
  padding: 10px 10px 6px;
}

.logout-confirm.hidden { display: none; }

.logout-confirm-text {
  margin: 0 0 10px;
  color: var(--text-dim);
  font-size: 0.85rem;
}

.logout-confirm-actions {
  display: flex;
  gap: 8px;
}

.logout-confirm-btn {
  flex: 1;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 999px;
  padding: 10px 16px;
  font-size: 0.85rem;
  cursor: pointer;
}

.logout-confirm-btn-danger {
  border-color: rgba(255, 107, 107, 0.4);
  color: var(--accent-active);
}

.logout-confirm-btn-danger:hover {
  background: rgba(255, 107, 107, 0.12);
}

.tabs {
  display: flex;
  gap: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px;
}

.tab-btn {
  border: none;
  background: transparent;
  color: var(--text-dim);
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.tab-btn.active {
  background: var(--accent);
  color: #0d0f14;
  font-weight: 600;
}

/* Adding the 4th (Insights) tab made the .tabs pill wide enough that on
   narrow phone screens it was crowding the profile button toward the edge
   of the topbar — .topbar-controls' space-between had nothing left to
   distribute once the two together already filled the available width.
   Tightening .tab-btn's horizontal padding/font-size and the gap next to
   the profile button reclaims enough room to fit all four comfortably. */
@media (max-width: 420px) {
  .tabs {
    gap: 2px;
    padding: 3px;
  }

  .tab-btn {
    padding: 8px 7px;
    font-size: 0.78rem;
  }

  .topbar-controls {
    gap: 6px;
  }
}

.tab-panel { display: none; }
.tab-panel.active { display: block; }

.capture-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 32px 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  position: relative;
}

.capture-buttons-row {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---------- inline camera box ---------- */
/* Compact, always part of the page layout — never a full-screen takeover.
   Three states toggled via a class on .camera-box: state-off / state-live / state-captured. */

.camera-box {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 260px;
  border-radius: var(--radius);
  overflow: hidden;
  background: #0b0d12;
  border: 1px solid var(--border);
}

.camera-video,
.camera-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Mirror the live preview, like a real mirror / FaceTime, only when it's
   actually the user-facing (selfie) camera — a rear/environment camera
   shows the world the way it really looks, unmirrored. Which camera is
   active, and whether that makes it mirrored, is worked out in
   app-capture.js (updateCameraMirrorState), which toggles this class
   accordingly. The capture itself (capturePhotoFromVideo, also in
   app-capture.js) also only flips the
   canvas when the preview was mirrored, so the saved/sent photo always
   matches what was actually seen while framing it. */
.camera-video.mirrored {
  transform: scaleX(-1);
}

/* Hide everything by default; each state reveals only what it needs. */
.camera-video,
.camera-photo,
.camera-placeholder,
.camera-toggle-btn,
.camera-flip-btn,
.camera-shutter-btn,
.camera-photo-actions {
  display: none;
}

.camera-box.state-off .camera-placeholder { display: flex; }
.camera-box.state-live .camera-video { display: block; }
.camera-box.state-live .camera-toggle-btn { display: flex; }
.camera-box.state-live .camera-flip-btn { display: flex; }
.camera-box.state-live .camera-shutter-btn { display: block; }
.camera-box.state-captured .camera-photo { display: block; }
.camera-box.state-captured .camera-photo-actions { display: flex; }

.camera-placeholder {
  position: absolute;
  inset: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  color: var(--text-dim);
}

.camera-enable-btn {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 18px;
  border-radius: 999px;
  font-size: 0.85rem;
  cursor: pointer;
}

.camera-enable-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.camera-toggle-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.25);
  color: #fff;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.camera-flip-btn {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.25);
  color: #fff;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.camera-shutter-btn {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid rgba(255, 255, 255, 0.4);
  cursor: pointer;
  transition: transform 0.1s ease;
}

.camera-shutter-btn:active { transform: translateX(-50%) scale(0.92); }

.camera-photo-actions {
  position: absolute;
  bottom: 12px;
  left: 0;
  right: 0;
  justify-content: center;
  gap: 10px;
}

.photo-retake-btn {
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.3);
  color: #fff;
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 0.82rem;
  cursor: pointer;
}

.photo-remove-btn {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

/* ---------- photo thumbnail inside a log entry ---------- */

.entry-photo {
  width: 100%;
  max-height: 220px;
  object-fit: cover;
  border-radius: 8px;
  margin-bottom: 8px;
  display: block;
}

/* Entries render as bare tiles grouped by day, side by side, in one gallery
   grid — photo entries as image tiles, text-only entries as text-preview
   tiles (.entry-text-tile-btn below) of the same size. No time/edit/send/
   delete chrome on either until one is enlarged in the lightbox. */

.entry-thumb-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 8px;
  margin-bottom: 12px;
}

.entry-thumb-tile {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
}

.entry-thumb-btn {
  width: 100%;
  height: 100%;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  border-radius: 8px;
  overflow: hidden;
  line-height: 0;
  display: block;
}

.entry-thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 8px;
}

/* Text-only tile — same footprint as a photo tile, but a small preview of
   the entry's text instead of an image. Declared after .entry-thumb-btn so
   these overrides (padding, line-height, layout) win over the base rules
   meant for an image-filled button. */

.entry-text-tile-btn {
  background: var(--surface);
  border: 1px solid var(--border);
  line-height: 1.35;
  display: flex;
  align-items: center;
  padding: 10px;
  text-align: left;
}

.entry-text-tile-snippet {
  color: var(--text);
  font-size: 0.78rem;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
  white-space: pre-wrap;
}

.entry-text-tile-snippet mark {
  background: rgba(124, 158, 255, 0.5);
}

.entry-thumb-tile.selected .entry-thumb-img,
.entry-thumb-tile.selected .entry-text-tile-snippet {
  opacity: 0.6;
}

/* Select mode: the whole tile is the tap target — a checkmark badge in the
   corner reflects selection state instead of a separate checkbox to hit. */

.entry-thumb-tile.selected {
  border-radius: 8px;
  box-shadow: 0 0 0 3px var(--accent);
}

.entry-thumb-check {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.85);
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  color: transparent;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.entry-thumb-tile.selected .entry-thumb-check {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d0f14;
}

/* ---------- lightbox ---------- */

.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.92);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Disables the double-tap-to-zoom gesture inside the lightbox (an easy
     accidental tap when quickly tapping through photos) without blocking
     normal panning/scrolling or deliberate pinch-zoom. Double-tap-zooming
     the page here was the trigger for the nav arrows ending up stuck
     underneath the photo — see the z-index note on .lightbox-nav-btn. */
  touch-action: manipulation;
}

.lightbox.hidden { display: none; }

/* Locks the root scroll position while the lightbox is open. Locking only
   <body> isn't reliable across browsers — <html> needs it too. This alone
   doesn't stop iOS Safari from scrolling the page to reveal a focused
   input (e.g. the edit textarea) — app-lightbox.js additionally pins
   <body> to position: fixed for that (see lockPageScroll/unlockPageScroll). */
html.lightbox-locked,
html.lightbox-locked body {
  overflow: hidden;
}

.lightbox-content {
  display: flex;
  flex-direction: column;
  width: min(560px, 92vw);
  /* Fixed (not max-) height so the frame's on-screen position stays put
     from entry to entry no matter how long a caption or text entry is —
     only the content inside it scrolls. Headroom above the strict minimum
     keeps growing along with the chrome around it (action buttons, the
     timestamp row) so none of it gets clipped by this box's own
     overflow: hidden. */
  height: 83vh;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
}

/* Timestamp now lives above the photo instead of squeezed into the button
   row below it — that row is icon buttons only now, so it can be bigger
   without fighting the meta text for space. */
.lightbox-top-meta {
  flex: none;
  padding: 10px 16px 8px;
  text-align: center;
}

.lightbox-media {
  position: relative;
  flex: none;
  height: 55vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

/* width/height: 100% (not just max-width/max-height) is what actually makes
   object-fit: contain do anything — with only max- constraints and no
   explicit size, an <img> with no other layout box just renders at its own
   intrinsic pixel size whenever that's smaller than the available space
   (max- only ever clamps DOWN, never scales up to fill). That was
   invisible before cropping existed, because every photo came straight
   from the capture pipeline at up to 1280px — always bigger than this box,
   so it always got clamped down and looked identical to object-fit:
   contain "just working". A tightly zoomed crop can genuinely be much
   smaller in pixels than that, and without this, it renders at its real
   tiny size — a small photo adrift in a sea of empty space — instead of
   being scaled up (while still respecting its own aspect ratio, same as
   before) to fill the frame. */
.lightbox-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.lightbox-img.hidden { display: none; }

/* Crop mode overlay — sits on top of the img/text-frame inside the same
   .lightbox-media box, clipped by its overflow: hidden, so the frame IS
   the crop boundary: whatever's visible here is exactly what "Save crop"
   keeps. touch-action: none stops the browser's own scroll/zoom gestures
   from fighting the pointer-drag panning below.
   No fixed position/size here — left/top/width/height are set in JS
   (positionCropFrame, app-lightbox.js) to match the PHOTO's own aspect
   ratio rather than this box's, so cropping never silently reshapes a
   photo that wasn't actually cropped. A visible outline marks its edges
   since it's usually smaller than the surrounding .lightbox-media box. */
.lightbox-crop {
  position: absolute;
  z-index: 1;
  overflow: hidden;
  outline: 1px solid rgba(255, 255, 255, 0.5);
  outline-offset: -1px;
  touch-action: none;
  cursor: grab;
}

.lightbox-crop.hidden { display: none; }

.lightbox-crop:active { cursor: grabbing; }

/* Positioned/scaled entirely via a JS-driven transform (translate + scale)
   in app-lightbox.js — transform-origin: 0 0 keeps that math simple (the
   translate is the image's own top-left corner in frame coordinates). */
.lightbox-crop-viewport {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.lightbox-crop-img {
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* Text-only entry: a bordered frame standing in for the photo, same
   footprint as the image, holding the entry's full text. */
.lightbox-text-frame {
  width: 88%;
  height: 88%;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 10px;
  padding: 20px;
  display: flex;
  align-items: center;
  /* Not justify-content: center — combined with align-items: center that
     clips a scrolling child symmetrically at both ends. margin: auto 0 on
     the inner content (below) centers short text without that clipping. */
  overflow-y: auto;
  overflow-x: hidden;
}

.lightbox-text-frame.hidden { display: none; }

.lightbox-text-frame-content {
  margin: auto 0;
  color: #fff;
  font-size: 1rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  text-align: center;
}

.lightbox-close-btn {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 1;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox-counter {
  position: absolute;
  top: 22px;
  left: 20px;
  z-index: 1;
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.85rem;
}

/* These sit at the left/right edges of the lightbox, which on a narrow
   phone screen can actually overlap .lightbox-content's edge (it's
   min(560px, 92vw) wide, not far in from the screen edge on a small
   phone) — without an explicit z-index here, plain DOM order decides who
   paints on top, and .lightbox-prev-btn happens to come *before*
   .lightbox-content in the markup, so it could end up rendered underneath
   it (invisible and unclickable) in exactly that overlap zone. Explicit
   z-index guarantees the controls always stay on top, regardless of DOM
   order or how the browser's own zoom/layout happens to shuffle things. */
.lightbox-nav-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox-nav-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.lightbox-prev-btn { left: 12px; }
.lightbox-next-btn { right: 12px; }

/* Info panel now flows normally below the fixed-height media box instead of
   sitting as an absolutely-positioned gradient overlay on top of it.
   overflow-y: auto (not hidden) is a safety net for the on-screen keyboard:
   when editing, the keyboard can eat enough of the visible screen that the
   Save/Cancel row wouldn't otherwise fit below the header + textarea — this
   makes sure it's always reachable by scrolling instead of stuck off-screen
   with no way to save. */
.lightbox-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 16px 20px 20px;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

.lightbox-info-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
  flex: none;
}

.lightbox-meta {
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.75rem;
  white-space: nowrap;
}

.lightbox-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-end;
  flex: none;
}

.lightbox-actions.hidden { display: none; }

/* Icon-only (with a title/aria-label for accessibility) — a row of four
   text-labeled pills didn't fit next to the timestamp on a narrow phone
   screen and pushed the later buttons off the edge. Small circular icon
   buttons match the style already used everywhere else in the app (the
   camera controls, the photo remove button) and take up far less width.
   Delete lives on the opposite end of the header from Add/Edit/Share
   (not inside .lightbox-actions) — putting real distance between the
   destructive action and the others cuts down on mis-taps. Sized up again
   now that the timestamp moved out of this row (see .lightbox-top-meta),
   since it no longer has to share space with that text. */
.lightbox-action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.25);
  color: #fff;
  border-radius: 50%;
  width: 44px;
  height: 44px;
  padding: 0;
  flex: none;
  cursor: pointer;
}

.lightbox-action-btn:hover { background: rgba(255, 255, 255, 0.2); }

.lightbox-action-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.lightbox-delete-btn {
  color: #ff9a9a;
  border-color: rgba(255, 107, 107, 0.4);
}

.lightbox-delete-btn:hover { background: rgba(255, 107, 107, 0.18); }

.lightbox-caption {
  color: #fff;
  font-size: 0.95rem;
  line-height: 1.5;
  margin: 0;
  white-space: pre-wrap;
  /* Fixed height (not max-height) — same reasoning as .lightbox-content
     above: keeps the media box from shifting position between entries with
     captions of different lengths. Only this box scrolls. */
  height: 14vh;
  flex: none;
  overflow-y: auto;
  overflow-x: hidden;
  word-break: break-word;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 8px;
  padding: 10px 12px;
}

.lightbox-caption.hidden { display: none; }

/* Custom scrollbar for the two scrollable boxes above, so an inevitable
   scrollbar doesn't look like a stray system-default sliver against the
   dark lightbox chrome. */
.lightbox-text-frame,
.lightbox-caption {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}

.lightbox-text-frame::-webkit-scrollbar,
.lightbox-caption::-webkit-scrollbar {
  width: 6px;
}

.lightbox-text-frame::-webkit-scrollbar-track,
.lightbox-caption::-webkit-scrollbar-track {
  background: transparent;
}

.lightbox-text-frame::-webkit-scrollbar-thumb,
.lightbox-caption::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.3);
  border-radius: 3px;
}

.lightbox-edit-row.hidden { display: none; }

.lightbox-edit-textarea {
  width: 100%;
  min-height: 80px;
  resize: vertical;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 8px;
  color: #fff;
  padding: 10px 12px;
  font-size: 0.9rem;
  font-family: inherit;
  line-height: 1.5;
}

.lightbox-edit-textarea:focus {
  outline: none;
  border-color: var(--accent);
}

.lightbox-edit-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

.lightbox-edit-cancel,
.lightbox-edit-save {
  border: none;
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.8rem;
  cursor: pointer;
}

.lightbox-edit-cancel {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
}

.lightbox-edit-save {
  background: var(--accent);
  color: #0d0f14;
  font-weight: 600;
}

/* This row stretches to fill whatever's left of .lightbox-info (a flex
   column — see that rule above) below the collapsed caption/header, rather
   than just sitting wherever normal flow puts it. It's a flex column
   itself: the slider stays put right at the TOP of that stretched space
   (where it always was), while only .lightbox-crop-buttons below gets
   margin-top: auto to anchor IT to the bottom — the slider no longer drags
   down with it. */
.lightbox-crop-row {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.lightbox-crop-row.hidden { display: none; }

.lightbox-crop-zoom {
  display: block;
  width: 100%;
  accent-color: var(--accent);
}

/* Cancel (left) / Save (right): pinned to opposite ends via
   space-between, same left-vs-right split the main lightbox-actions row
   uses for its own Delete button — and, critically, each stays fixed in
   its own spot regardless of whether Revert is showing, since Revert lives
   INSIDE the left group (see .lightbox-crop-left-group below) rather than
   being a third item in this row that would otherwise shift Save's
   position around depending on what else is visible. margin-top: auto
   pushes just this row to the bottom of .lightbox-crop-row's stretched
   flex column, leaving the slider above it undisturbed at the top. */
.lightbox-crop-buttons {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  padding-top: 8px;
}

/* Cancel, then Revert right next to it on Cancel's right side (when it's
   showing at all) — grouped together as a unit so Save (the OTHER child of
   .lightbox-crop-buttons, pinned to the row's right edge) never has to
   shift for Revert appearing or disappearing. */
.lightbox-crop-left-group {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Red — the same danger palette used everywhere else a destructive action
   needs to read as one at a glance (.lightbox-delete-btn, .share-bar-delete-
   btn): var(--accent-active) text on a matching soft-red border, with a
   faint red tint on hover. Reverting discards whatever cropping's been done
   and can't be undone, same as those. */
.lightbox-crop-revert-btn {
  border: 1px solid rgba(255, 107, 107, 0.4);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.8rem;
  cursor: pointer;
  background: transparent;
  color: var(--accent-active);
}

.lightbox-crop-revert-btn:hover {
  background: rgba(255, 107, 107, 0.15);
}

.lightbox-crop-revert-btn.hidden { display: none; }

.lightbox-crop-revert-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.mic-btn {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: #0d0f14;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 0 0 0 rgba(124, 158, 255, 0.5);
  transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease;
}

.mic-btn:hover { transform: scale(1.04); }
.mic-btn:active { transform: scale(0.97); }

.mic-btn.recording {
  background: var(--accent-active);
  animation: pulse 1.4s ease-out infinite;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.45); }
  70%  { box-shadow: 0 0 0 18px rgba(255, 107, 107, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 107, 107, 0); }
}

.mic-status {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.9rem;
  text-align: center;
}

.entry-text-wrap {
  position: relative;
  width: 100%;
}

.entry-text {
  width: 100%;
  min-height: 140px;
  resize: vertical;
  background: #0d0f14;
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  padding: 14px 40px 14px 14px;
  font-size: 1rem;
  font-family: inherit;
  line-height: 1.5;
}

.entry-text:focus {
  outline: none;
  border-color: var(--accent);
}

/* Shared "clear this field" button, positioned differently per field below. */

.input-clear-btn {
  position: absolute;
  border: none;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-dim);
  border-radius: 50%;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}

.input-clear-btn.hidden { display: none; }

.input-clear-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.16);
}

.entry-text-wrap .input-clear-btn {
  top: 10px;
  right: 10px;
}

.capture-actions {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.unsupported-note {
  color: #ffb454;
  font-size: 0.8rem;
}

.upload-date-note {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.82rem;
  text-align: center;
}

.hidden { display: none; }

.save-btn {
  margin-left: auto;
  background: var(--accent);
  color: #0d0f14;
  border: none;
  padding: 10px 22px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  transition: opacity 0.15s ease, transform 0.1s ease;
}

.save-btn:hover { opacity: 0.9; }
.save-btn:active { transform: scale(0.97); }
.save-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.logs-toolbar {
  margin-bottom: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.logs-toolbar-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.search-wrap {
  position: relative;
  width: 100%;
}

.search-input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  padding: 12px 36px 12px 16px;
  font-size: 0.95rem;
}

.search-wrap .input-clear-btn {
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
}

.select-toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 12px;
  padding: 0 18px;
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.03);
}

/* Gated to real pointer devices: on iOS Safari a tap can trigger :hover
   and leave it "stuck" until something else is tapped, which made the
   button look like it was still in select mode right after turning it
   off. Only devices with an actual mouse get the hover tint. */
@media (hover: hover) and (pointer: fine) {
  .select-toggle-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
  }
}

.select-toggle-btn:active {
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* While selecting: label swaps to "Cancel" and the pill switches to a red
   outline/text — press it again (or Cancel) to drop back to grey "Select". */
.select-toggle-btn.active {
  color: var(--accent-active);
  border-color: var(--accent-active);
}

/* Small round icon buttons, not a pill/segmented control — a second pill
   here read as just another set of tabs, too easy to confuse with the
   Capture/Upload/Logs switcher above it. Matches the icon-button style
   used for the profile button and the lightbox actions elsewhere. */

.view-icon-tabs {
  display: flex;
  gap: 6px;
}

.view-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
  cursor: pointer;
}

.view-icon-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.view-icon-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d0f14;
}

.calendar-day-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin: 14px 0 16px;
}

.calendar-day-nav.hidden { display: none; }

.calendar-day-nav-label {
  font-weight: 600;
  font-size: 1rem;
  min-width: 170px;
  text-align: center;
}

.calendar-select-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: -8px 0 16px;
  padding: 0 2px;
}

.calendar-select-bar.hidden { display: none; }

.calendar-select-hint {
  color: var(--text-dim);
  font-size: 0.78rem;
}

.calendar-select-all-btn {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--accent);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.8rem;
  cursor: pointer;
  white-space: nowrap;
}

.calendar-select-all-btn:hover {
  border-color: var(--accent);
}

/* ---------- calendar view ---------- */

.calendar-view {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 20px;
}

.calendar-view.hidden { display: none; }

.calendar-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.calendar-month-label {
  font-weight: 600;
  font-size: 0.95rem;
}

.calendar-nav-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.calendar-nav-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.calendar-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  color: var(--text-dim);
  font-size: 0.72rem;
  margin-bottom: 6px;
}

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

.calendar-day {
  aspect-ratio: 1;
  border: none;
  background: transparent;
  color: var(--text);
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  cursor: pointer;
  font-size: 0.85rem;
}

.calendar-day:hover { background: var(--surface-2); }

.calendar-day.is-empty {
  cursor: default;
  pointer-events: none;
}

.calendar-day.is-today {
  border: 1px solid var(--accent);
}

.calendar-day.is-selected {
  background: var(--accent);
  color: #0d0f14;
  font-weight: 600;
}

.calendar-day.search-match {
  box-shadow: inset 0 0 0 2px var(--accent-active);
}

.calendar-dot {
  position: absolute;
  bottom: 6px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}

.calendar-day.is-selected .calendar-dot {
  background: #0d0f14;
}

.entry-select-checkbox {
  margin-top: 4px;
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

/* ---------- floating share bar ---------- */

.share-bar {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  width: min(560px, calc(100% - 40px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  z-index: 50;
}

.share-bar.hidden { display: none; }

.share-count {
  flex: 1;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.85rem;
  white-space: nowrap;
}

.share-bar-send-btn {
  background: var(--accent);
  color: #0d0f14;
  border: none;
  border-radius: 999px;
  padding: 8px 20px;
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
}

.share-bar-delete-btn {
  background: transparent;
  border: 1px solid rgba(255, 107, 107, 0.4);
  color: var(--accent-active);
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.85rem;
  cursor: pointer;
}

.share-bar-delete-btn:hover {
  background: rgba(255, 107, 107, 0.12);
}

.search-input:focus {
  outline: none;
  border-color: var(--accent);
}

.day-group {
  margin-bottom: 24px;
}

.day-heading {
  color: var(--text-dim);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 10px 4px;
}

.entry-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 10px;
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.entry-time {
  color: var(--text-dim);
  font-size: 0.78rem;
  white-space: nowrap;
  padding-top: 2px;
  min-width: 60px;
}

.entry-body {
  flex: 1;
  min-width: 0;
}

.entry-text-content {
  margin: 0;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

.entry-text-content.collapsed {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.entry-expand {
  background: none;
  border: none;
  color: var(--accent);
  font-size: 0.78rem;
  cursor: pointer;
  padding: 4px 0 0;
}

.entry-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}

.entry-edit-open,
.entry-send {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 0.75rem;
  padding: 2px 4px;
  border-radius: 6px;
  white-space: nowrap;
}

.entry-edit-open:hover,
.entry-send:hover {
  color: var(--accent);
  background: rgba(124, 158, 255, 0.12);
}

.entry-delete {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 6px;
}

.entry-delete:hover {
  color: var(--accent-active);
  background: rgba(255, 107, 107, 0.1);
}

.entry-card-editing {
  border-color: var(--accent);
}

.entry-edit-textarea {
  width: 100%;
  min-height: 90px;
  resize: vertical;
  background: #0d0f14;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 10px 12px;
  font-size: 0.95rem;
  font-family: inherit;
  line-height: 1.5;
}

.entry-edit-textarea:focus {
  outline: none;
  border-color: var(--accent);
}

.entry-edit-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

.entry-edit-cancel,
.entry-edit-save {
  border: none;
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 0.82rem;
  cursor: pointer;
}

.entry-edit-cancel {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
}

.entry-edit-save {
  background: var(--accent);
  color: #0d0f14;
  font-weight: 600;
}

.empty-state {
  color: var(--text-dim);
  text-align: center;
  padding: 40px 0;
}

mark {
  background: rgba(124, 158, 255, 0.35);
  color: inherit;
  border-radius: 3px;
}

/* ---------- auth screen (sign up / log in) ---------- */

.auth-screen {
  position: fixed;
  inset: 0;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 300;
}

.auth-screen.hidden { display: none; }

.auth-card {
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 32px 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.auth-app-title {
  font-size: 1.3rem;
  margin: 0 0 4px;
  letter-spacing: -0.01em;
}

.auth-title {
  font-size: 1rem;
  color: var(--text-dim);
  font-weight: 500;
  margin: 0 0 18px;
}

.auth-form {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.auth-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.82rem;
  color: var(--text-dim);
}

.auth-field input {
  background: #0d0f14;
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  padding: 11px 12px;
  font-size: 0.95rem;
  font-family: inherit;
}

.auth-field input:focus {
  outline: none;
  border-color: var(--accent);
}

.auth-remember-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.auth-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--text-dim);
  cursor: pointer;
}

.auth-remember input {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
}

.auth-forgot-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 0.82rem;
  cursor: pointer;
  padding: 0;
  white-space: nowrap;
}

.auth-forgot-btn:hover { color: var(--accent); text-decoration: underline; }

.auth-error {
  color: var(--accent-active);
  font-size: 0.82rem;
  margin: 0;
}

.auth-error.hidden { display: none; }

.auth-submit-btn {
  background: var(--accent);
  color: #0d0f14;
  border: none;
  padding: 12px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  transition: opacity 0.15s ease, transform 0.1s ease;
}

.auth-submit-btn:hover { opacity: 0.9; }
.auth-submit-btn:active { transform: scale(0.98); }
.auth-submit-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.auth-toggle {
  margin: 18px 0 0;
  font-size: 0.82rem;
  color: var(--text-dim);
  display: flex;
  gap: 6px;
}

.auth-toggle-btn {
  background: none;
  border: none;
  color: var(--accent);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
}

.auth-toggle-btn:hover { text-decoration: underline; }

.auth-login-panel,
.auth-reset-panel {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Explicit, higher-specificity override — .hidden's plain display:none
   (defined much earlier in this file) would otherwise lose to the
   display:flex above on source order alone, since both are single-class
   selectors of equal specificity. Two classes beats one regardless of
   where each rule sits. */
.auth-login-panel.hidden,
.auth-reset-panel.hidden {
  display: none;
}

.auth-reset-help {
  font-size: 0.82rem;
  color: var(--text-dim);
  text-align: center;
  line-height: 1.4;
  margin: 0 0 16px;
}

.auth-reset-message {
  font-size: 0.82rem;
  color: var(--text-dim);
  line-height: 1.4;
  margin: 0;
}

/* Real problems (bad email, rate-limited, offline) get the same red as
   auth-error. The "check your email" confirmation deliberately does NOT get
   this — it's not an error, and it shows for a nonexistent account too (see
   auth.js) so it needs to read as neutral either way, not alarming. */
.auth-reset-message.is-error { color: var(--accent-active); }

/* ---------- Insights tab (app-insights.js) ---------- */

.insights-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 32px 24px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.insights-title {
  margin: 0;
  font-size: 1.1rem;
}

.insights-help {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-dim);
  line-height: 1.5;
}

.insights-generate-btn {
  background: var(--accent);
  color: #0d0f14;
  border: none;
  padding: 10px 22px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  transition: opacity 0.15s ease, transform 0.1s ease;
}

.insights-generate-btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.insights-status {
  font-size: 0.85rem;
  color: var(--text-dim);
  line-height: 1.4;
  margin: 0;
}

/* Same reasoning as .auth-reset-message.is-error — real failures (rate
   limit hit, the function errored, offline) get the accent-active red;
   the "not enough entries yet" message is expected, not a failure, so it
   stays in the neutral .insights-status color instead. */
.insights-status.is-error { color: var(--accent-active); }

.insights-result {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  white-space: pre-wrap;
}
