*{box-sizing:border-box;margin:0;padding:0}
:root{
  --bg0:#0c0d10;--bg1:#111419;--bg2:#181c22;--bg3:#1e242d;--bg4:#242c38;
  --border:#2a3340;--border2:#3a4655;
  --text0:#f0f4f8;--text1:#a8b5c4;--text2:#74879a;--text3:#4a5a6a;
  --green:#3ecf8e;--greenDim:#1a3d2e;
  --blue:#4f9eff;--blueDim:#1a2d4a;
  --amber:#f59e0b;--amberDim:#3d2a0a;
  --red:#ff5f57;--redDim:#3d1a1a;
  --purple:#a78bfa;--purpleDim:#2d1f4a;
  --teal:#06b6d4;--tealDim:#0a2a30;
  --orange:#fb923c;--orangeDim:#3d1f0a;
  --r:8px;--r2:12px;--font:'Inter',system-ui,sans-serif;
  /* Shared easing for transitions/animations — without this, ~20 css rules
     using var(--ease) fall back to linear timing which feels mechanical. */
  --ease:cubic-bezier(.4,0,.2,1);
  /* Hover border color for photo + library tiles — silently broken without
     a definition (browser ignored the rule). */
  --border-g:var(--green);
}
html.light{
  /* DESIGN #001 v10: premium light-mode polish pass (cross-AI review
     surfaced too-beige flatness, low secondary text contrast, dull
     quick actions, weak topbar separation). New layer hierarchy:
       bg0 (page)         — slightly warm cream
       bg1 (sidebar/topbar/elevated containers) — near-white
       bg2 (cards / rows) — bright, just below bg1
       bg3 (inputs, muted buttons, hover-fill)
       bg4 (deepest hover, "pressed" state)
     Borders + text contrast bumped a notch for crisper read. */
  --bg0:#f7f5ef;--bg1:#fffdf8;--bg2:#fbfaf6;--bg3:#eee8dc;--bg4:#e4ddcf;
  --border:#d9d0c0;--border2:#bfb4a2;
  --text0:#18221d;--text1:#364238;--text2:#687264;--text3:#969b8d;
  --green:#14966b;--greenDim:#dff3e8;--blueDim:#e3ecf5;--amberDim:#f6ebcc;
  --redDim:#f8e0d6;--purpleDim:#ebe4ef;--tealDim:#d8ede0;--orangeDim:#faecd6;
}
.topbar{box-shadow:0 1px 6px rgba(0,0,0,.05)}
html.light .modal-box{box-shadow:0 12px 40px rgba(20,15,5,.18)}
html.light .card{box-shadow:0 1px 2px rgba(35,28,15,.07),0 4px 14px rgba(35,28,15,.09),0 14px 34px rgba(35,28,15,.06)}
html.light .plant-card{box-shadow:0 1px 3px rgba(35,28,15,.10),0 4px 14px rgba(35,28,15,.07)}
/* DESIGN #001 v9 follow-up: supply rows and recurring-task rows use bg2
   and bg3 in dark mode (where they read as "raised" against the darker
   body). In light mode those tokens are warm beige and create jarring
   beige strips against the near-white page. Override to bg1 (clean
   white) so they read as cards, matching the rest of the app. Subtle
   border keeps row separation legible. Hover keeps a slight tint via
   bg2 so the interaction is visible. */
html.light .supply-card{background:var(--bg2);border-color:var(--border)}
html.light .supply-card:hover{background:var(--bg3);border-color:var(--border2)}
html.light .rtask-item{background:var(--bg2);border-color:var(--border)}
html.light .rtask-item:hover{background:var(--bg3);border-color:var(--border2)}
/* DESIGN v10: task rows / care rows / care-log timeline cards get the
   same paper-white treatment so they don't feel beige-on-beige. */
html.light .task-row,
html.light .care-row{background:var(--bg2);border-color:var(--border)}
html.light .task-row:hover,
html.light .care-row:hover{background:var(--bg3);border-color:var(--border2)}
html.light .tl-card{background:var(--bg2);border-color:var(--border)}
html.light .tl-card:hover{background:var(--bg3);border-color:var(--border2)}
/* DESIGN v10: quick-action cards on the dashboard — same treatment so
   they stop feeling muddy. Hover keeps the green accent border for an
   "interactive" feel. */
html.light .qa-btn{background:var(--bg2);border-color:var(--border)}
html.light .qa-btn:hover{background:var(--bg3);border-color:var(--green)}
/* DESIGN v10: subtle frosted-glass topbar separation. The translucent
   surface + blur reads "app shell" and the darker bottom border keeps
   the line crisp against the slightly-warm page. */
html.light .topbar{background:rgba(255,253,248,.92);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom-color:#ded6c8}
html{background:var(--bg0)}
body{font-family:var(--font);background:var(--bg0);color:var(--text0);min-height:100vh;font-size:14px;transition:background .2s,color .2s;max-width:100%}
*::-webkit-scrollbar{width:4px;height:4px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:var(--border2);border-radius:4px}
button,input,select,textarea{font-family:var(--font)}
button{cursor:pointer}

/* ── LAYOUT ── */
.app{display:flex;min-height:100vh}
/* .main fills the space left of the fixed 220px sidebar. Using flex:1 + min-width:0
   instead of `width: calc(100% - 220px)` is critical — calc(100%) on a flex item
   references the parent's content box, and any overflowing descendant inside .main
   would expand .app, which would expand the 100% reference, causing the entire
   layout to grow wider than the viewport. flex:1 with min-width:0 strictly fills
   the remaining viewport width and contains overflowing content. */
.main{margin-left:220px;display:flex;flex-direction:column;min-height:100vh;flex:1;min-width:0;max-width:calc(100vw - 220px)}

.content{padding:1.5rem;flex:1;width:100%;min-width:0;max-width:100%}
.page{display:none;max-width:100%;min-width:0}.page.active{display:block}

/* ── SIDEBAR ── */
.sidebar{width:220px;background:var(--bg1);border-right:1px solid var(--border);display:flex;flex-direction:column;position:fixed;top:0;left:0;height:100vh;z-index:50;overflow-y:auto}
html.light .sidebar{box-shadow:1px 0 12px rgba(0,0,0,.06)}
.sb-logo{padding:0 1rem;height:48px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;cursor:pointer;flex-shrink:0}
.sb-logo-name{font-size:16px;font-weight:700;color:var(--text0);letter-spacing:-.4px;line-height:1.2}
.sb-logo-tag{font-size:9.5px;color:var(--text2);letter-spacing:.08em;text-transform:uppercase;line-height:1.3}
.sb-sec{padding:.75rem .9rem .25rem;font-size:10px;font-weight:500;color:var(--text3);letter-spacing:.01em;margin-bottom:2px}
.sb-item{display:flex;align-items:center;gap:9px;padding:9px 11px;margin:1px 7px;border-radius:var(--r);cursor:pointer;color:var(--text1);font-size:12.5px;transition:all .14s;user-select:none}
.sb-item:hover{background:var(--bg3);color:var(--text0)}
.sb-item.active{background:var(--greenDim);color:var(--green);font-weight:500}
.sb-item svg{width:15px;height:15px;flex-shrink:0;opacity:.8}
.sb-item.active svg{opacity:1}
.sb-badge{margin-left:auto;font-size:9px;min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--red);color:#fff;font-weight:700;display:flex;align-items:center;justify-content:center}
.sb-divider{height:1px;background:var(--border);margin:.5rem .9rem}
.sb-bottom{margin-top:auto;padding:1rem;border-top:1px solid var(--border)}
.sb-plant-count{font-size:13px;color:var(--text2);text-align:center}
.sb-plant-count b{color:var(--green)}

/* ── TOPBAR ── */
.topbar{height:40px !important;background:var(--bg1);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 1.25rem;gap:10px;position:sticky;top:0;z-index:40;align-self:flex-start;width:100%;flex-shrink:0}
html.light .topbar{box-shadow:0 1px 6px rgba(0,0,0,.05)}
.tb-sp{flex:1}
.tb-page{font-size:15px;font-weight:600;color:var(--text0);letter-spacing:-.2px;min-width:160px}
.theme-btn{display:flex;align-items:center;gap:6px;padding:5px 11px;background:var(--bg3);border:1px solid var(--border);border-radius:20px;cursor:pointer;font-size:11px;color:var(--text1);transition:all .15s;user-select:none}
.theme-btn:hover{background:var(--bg4);border-color:var(--border2);color:var(--text0)}
.th-track{width:28px;height:15px;border-radius:8px;background:var(--bg4);border:1px solid var(--border2);position:relative;transition:all .2s var(--ease);flex-shrink:0}
.th-thumb{position:absolute;top:2px;left:2px;width:9px;height:9px;border-radius:50%;background:var(--text2);transition:all .2s var(--ease)}
html.light .th-track{background:var(--greenDim);border-color:var(--green)}
html.light .th-thumb{left:15px;background:var(--green)}
.btn{padding:7px 15px;border-radius:var(--r);font-size:12px;font-weight:500;border:none;cursor:pointer;transition:all .14s;display:inline-flex;align-items:center;gap:5px;line-height:1}
.btn-primary{background:var(--green);color:#051a0e}.btn-primary:hover{filter:brightness(1.08)}
.btn-secondary{background:var(--bg3);color:var(--text1);border:1px solid var(--border)}.btn-secondary:hover{background:var(--bg4);color:var(--text0)}
.btn-danger{background:transparent;color:var(--red);border:1px solid var(--redDim)}.btn-danger:hover{background:var(--redDim)}
.btn-ghost{background:transparent;color:var(--text2);border:none;padding:5px 8px}.btn-ghost:hover{color:var(--text0)}
.btn-sm{padding:5px 11px;font-size:11px}
.btn-xs{padding:3px 8px;font-size:10px;border-radius:6px}

/* ── CARDS ── */
/* task #304: card MUST be bounded so internal text-overflow:ellipsis on children can compute width */
.card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);padding:1.3rem 1.4rem;max-width:100%;overflow:hidden}
.card-sm{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:.75rem .9rem}
.card-title{font-size:12px;font-weight:600;color:var(--text1);letter-spacing:0;margin-bottom:.75rem;display:flex;align-items:center;justify-content:space-between}
.card-title a{font-size:10px;color:var(--blue);font-weight:400;cursor:pointer;text-transform:none;letter-spacing:0}
.card-title a:hover{color:var(--text0)}


/* ── QUICK ACTION BUTTONS ── */
.qa-btn{display:flex;align-items:center;gap:12px;padding:11px 13px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);cursor:pointer;transition:all .15s;text-align:left;width:100%;color:var(--text0)}
.qa-btn:hover{background:var(--bg4);border-color:var(--border2)}
.qa-icon{font-size:22px;flex-shrink:0;width:36px;height:36px;background:var(--bg2);border-radius:var(--r);display:flex;align-items:center;justify-content:center;border:1px solid var(--border)}
.qa-text{flex:1}
.qa-label{font-size:12.5px;font-weight:500;margin-bottom:2px}
/* BUG #060 fix: --text2 on the qa-btn bg failed WCAG AA (4.5:1). Use --text1
   for the subtitle so it stays readable. The label above already uses
   --text0 implicitly via .qa-btn's color, so this only affects subtitles. */
.qa-sub{font-size:10px;color:var(--text1)}

/* ── METRIC CARDS ── */
.metric-val{font-size:28px;font-weight:700;line-height:1;letter-spacing:-.5px;margin-bottom:4px}
.metric-label{font-size:11px;color:var(--text2);margin-bottom:6px}
.metric-note{font-size:11px}
.metric-val-sm{font-size:18px;font-weight:700;line-height:1.2;letter-spacing:-.3px;margin-bottom:4px}
.metric-bar{height:3px;background:var(--bg4);border-radius:2px;margin-top:9px;overflow:hidden}
.metric-fill{height:100%;border-radius:2px}

/* ── PLANT CARDS ── */
.ga{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px}
.plant-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);overflow:hidden;cursor:pointer;transition:border-color .15s,transform .15s,box-shadow .15s}
.pc-top{height:68px;display:flex;align-items:center;justify-content:center;background:var(--bg3);position:relative;font-size:36px}
.pc-score-ring{position:absolute;top:8px;right:8px}
.pc-actions{position:absolute;top:6px;left:6px;display:none;gap:4px;z-index:2}
.plant-card:hover .pc-actions{display:flex}
@media(hover:none){.pc-actions{display:flex!important;opacity:.9}.pc-action-btn{min-width:32px;min-height:32px}}
.pc-action-btn{background:var(--bg3);border:1px solid var(--border);border-radius:5px;cursor:pointer;font-size:11px;padding:2px 5px;line-height:1.4;transition:all .14s}
.pc-action-btn:hover{background:var(--bg4)}
.pc-body{padding:12px 13px}
.pc-name{font-size:13px;font-weight:600;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pc-species{font-size:10px;color:var(--text2);font-style:italic;margin-bottom:8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pc-badges{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:9px;max-width:100%;flex-wrap:wrap;overflow:hidden}
/* task #304: long room/badge text needs to wrap/ellipsis instead of overflowing the badge */
/* task #308: user prefers single-line ellipsis. Force display:inline-block
   (overriding .badge's inline-flex) so iOS Safari's text-overflow:ellipsis
   actually works on the pc-badges badges. */
.pc-badges .badge{display:inline-block !important;max-width:100% !important;overflow:hidden !important;text-overflow:ellipsis !important;white-space:nowrap !important;min-width:0;vertical-align:middle;line-height:1.6}

.pc-footer{display:flex;align-items:center;justify-content:space-between;margin-top:6px}
.health-bar{height:3px;background:var(--bg4);border-radius:2px;overflow:hidden;margin-top:6px}
.health-fill{height:100%;border-radius:2px}
.water-status{font-size:10px;display:flex;align-items:center;gap:4px}
.water-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}

/* ── BADGE ── */
.badge{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;border-radius:20px;font-size:10px;font-weight:500;line-height:1.4;word-break:break-all;overflow-wrap:anywhere;max-width:100%}
.bg-g{background:var(--greenDim);color:var(--green)}
.bg-a{background:var(--amberDim);color:var(--amber)}
.bg-r{background:var(--redDim);color:var(--red)}
.bg-b{background:var(--blueDim);color:var(--blue)}
.bg-p{background:var(--purpleDim);color:var(--purple)}
.bg-t{background:var(--tealDim);color:var(--teal)}
.bg-o{background:var(--orangeDim);color:var(--orange)}
.bg-muted{background:var(--bg3);color:var(--text2)}

/* ── FILTER PILLS ── */
.pill-row{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:12px}
.pill{padding:4px 12px;border-radius:20px;font-size:11px;cursor:pointer;border:1px solid var(--border);background:transparent;color:var(--text1);transition:all .14s;user-select:none}
.pill.on{background:var(--greenDim);color:var(--green);border-color:var(--green)}
.pill:hover:not(.on){background:var(--bg3);color:var(--text0)}

/* ── CARE ROWS ── */
/* task #312: .care-row is the DASHBOARD widget class (the Tasks page uses .task-row — different class). All my previous overflow fixes targeted .task-row which is why nothing worked on the dashboard. Use CSS Grid with minmax(0,1fr) for bulletproof column shrinking. */
.care-row{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:10px 13px;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:11px;margin-bottom:6px;cursor:pointer;transition:border-color .14s var(--ease);max-width:100%;overflow:hidden}
.care-row > div{min-width:0;max-width:100%;overflow:hidden}
.care-row > div > div{min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
.care-row > span:first-child,.care-row > .badge,.care-row > button{flex-shrink:0;align-self:center}
.care-row:hover{border-color:var(--border2)}
.care-row.overdue{border-left:3px solid var(--red)}
.care-row.today{border-left:3px solid var(--amber)}
.care-row.upcoming{border-left:3px solid var(--border)}

/* ── TIMELINE ── */
.tl{padding-left:16px;position:relative}
.tl::before{content:'';position:absolute;left:4px;top:6px;bottom:6px;width:1px;background:var(--border)}
.tl-item{position:relative;padding-bottom:14px}
.tl-item:last-child{padding-bottom:0}
/* Action buttons (✏️ / ×) — centred icon, no default browser button padding
   that pushes the glyph off the geometric centre. Used in Care log, Calendar
   day-detail, Story timeline and Harvest log rows. */
.tl-edit-btn{display:inline-flex;align-items:center;justify-content:center;padding:0;line-height:1;background:transparent;border:1px solid var(--border);border-radius:50%;width:28px;height:28px;cursor:pointer;color:var(--text1);transition:background .14s,border-color .14s}
.tl-edit-btn:hover{background:var(--bg3);border-color:var(--border2)}
.tl-dot{position:absolute;left:-14px;top:4px;width:9px;height:9px;border-radius:50%;border:2px solid var(--bg2);flex-shrink:0}
.tl-card{background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);padding:9px 12px;transition:background .14s var(--ease),border-color .14s var(--ease)}
.tl-card:hover{background:var(--bg4);border-color:var(--border2)}
.tl-head{display:flex;align-items:center;gap:7px;margin-bottom:4px}
.tl-type{font-size:11px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.tl-date{font-size:10px;color:var(--text2);margin-left:auto}
.tl-note{font-size:11px;color:var(--text1);line-height:1.55;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;line-clamp:3;-webkit-box-orient:vertical;word-break:break-word;overflow-wrap:anywhere}
.tl-tags{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
.tt{font-size:10px;padding:1px 7px;border-radius:8px;font-weight:500}
.tt-s{background:#1a3d2e;color:#3ecf8e}
.tt-m{background:#3d1a1a;color:#ff5f57}
.tt-e{background:#2d1f4a;color:#a78bfa}
html.light .tt-s{background:#dcfce7;color:#166534}
html.light .tt-m{background:#fee2e2;color:#991b1b}
html.light .tt-e{background:#ede9fe;color:#5b21b6}

/* ── PLANT DETAIL ── */
.det-hero{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);padding:1.4rem;margin-bottom:1rem;display:grid;grid-template-columns:88px 1fr;gap:1.2rem;align-items:start}
/* Task #264: long plant/species names were overflowing past the screen edge on
   mobile because grid items default to min-width:auto — the nowrap text column
   stretched the 1fr track beyond the viewport, defeating text-overflow:ellipsis.
   Forcing min-width:0 on every direct child lets the column shrink correctly. */
.det-hero > *{min-width:0}
.det-emoji-wrap{display:flex;flex-direction:column;align-items:center;gap:8px}
.det-emoji{width:72px;height:72px;background:var(--bg3);border-radius:var(--r2);display:flex;align-items:center;justify-content:center;font-size:36px;border:1px solid var(--border);flex-shrink:0}
.det-name{font-size:21px;font-weight:700;letter-spacing:-.4px;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.det-species{font-size:12px;color:var(--text2);font-style:italic;margin-bottom:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.det-quick{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:12px}
.dq{background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);padding:8px 10px}
.dq-tappable{cursor:pointer;transition:background .12s,border-color .12s,transform .08s}
.dq-tappable:hover{background:var(--bg4);border-color:var(--green)}
.dq-tappable:active{transform:scale(.97)}
.dq-label{font-size:10px;color:var(--text2);margin-bottom:4px}
.dq-val{font-size:12px;font-weight:500}
.stage-wrap{margin-top:10px}
.stage-label{font-size:10px;color:var(--text2);margin-bottom:4px}
.stage-bar{display:flex;gap:3px}
.stage-pip{height:4px;flex:1;border-radius:2px;background:var(--bg4)}
.stage-pip.done{background:var(--green)}
.stage-pip.now{background:var(--amber)}

/* ── TABS ── */
.tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:1rem;gap:0}
.tab-btn{padding:8px 15px;font-size:12px;cursor:pointer;color:var(--text2);border:none;background:none;border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .14s;white-space:nowrap}
.tab-btn.on{color:var(--green);border-bottom-color:var(--green);font-weight:500}
.tab-btn:hover:not(.on){color:var(--text0)}
.tab-panel{display:none}.tab-panel.on{display:block}

/* ── PHOTO ── */
.photo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;width:100%}
.photo-thumb{border-radius:var(--r);overflow:hidden;border:1px solid var(--border);cursor:pointer;position:relative;background:var(--bg3);padding-bottom:100%;height:0}
.photo-thumb:hover{border-color:var(--border-g)}
.photo-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.photo-del-btn{opacity:0;transition:opacity .18s}
.photo-thumb:hover .photo-del-btn{opacity:1}
.photo-edit-btn{opacity:0;transition:opacity .18s}
.photo-thumb:hover .photo-edit-btn{opacity:1}
@media(hover:none){.photo-del-btn,.photo-edit-btn{opacity:.85}}
@media(max-width:700px){
  /* Mobile-only horizontal scroll containment. Kept out of desktop styles so
     that desktop's `position:sticky` topbar continues to work. */
  html,body{overflow-x:clip}
  body.modal-open .mobile-nav,
  body.modal-open .mobile-fab,
  body.modal-open .mobile-fab-menu,
  body.modal-open .mobile-fab-overlay,
  body.modal-open .mobile-more-drawer,
  body.modal-open .mobile-more-overlay{display:none!important;pointer-events:none!important}
  /* Calendar header: filters + month nav don't fit on one row on narrow
     phones. Stack them, and shrink the month label so '<' and '>' aren't
     clipped at the right edge. */
  #page-calendar{max-width:100vw;overflow-x:hidden}
  #page-calendar > *{max-width:100%;min-width:0}
  #page-calendar > .flex.aic.jsb{flex-direction:column;align-items:stretch;gap:8px}
  #page-calendar > .flex.aic.jsb > .flex{justify-content:center;flex-wrap:wrap;min-width:0}
  #page-calendar #cal-label{min-width:90px!important;font-size:13px!important}
  #page-calendar > .flex.aic.jsb > .flex:first-child > select{flex:1;min-width:0}
  /* Day-of-week labels — abbreviate so 'Wednesday' doesn't widen the column */
  #page-calendar .cal-dow{font-size:9px}
  /* Tighten cell padding on phone so 7 columns + their content fits cleanly */
  #page-calendar .cal-cell{padding:4px;min-height:64px}
  #page-calendar .cal-day-num{width:16px;height:16px;font-size:10px}
  #page-calendar .cal-ev{font-size:8px;padding:1px 3px}
}
.photo-meta{position:absolute;bottom:0;left:0;right:0;padding:7px 9px 8px;background:linear-gradient(transparent,rgba(0,0,0,.78));opacity:0;transition:opacity .18s}
.photo-thumb:hover .photo-meta{opacity:1}
.photo-date{font-size:11px;color:#fff;font-weight:500}
.photo-caption{font-size:12px;color:#e8edf3;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.photo-compare{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px}
.photo-compare .pc-img{width:100%;height:110px;object-fit:cover;border-radius:var(--r);border:1px solid var(--border)}
.photo-compare .pc-lbl{font-size:10px;color:var(--text2);margin-top:4px}
.fnw-img-wrap{border:1px solid var(--border);border-radius:var(--r);transition:border-color .14s}
.fnw-img-wrap:hover{border-color:var(--border-g)}
.upload-area{border:1.5px dashed var(--border2);border-radius:var(--r);padding:1.5rem;text-align:center;cursor:pointer;color:var(--text2);font-size:12px;transition:all .15s}
.upload-area:hover{border-color:var(--green);color:var(--text1);background:var(--greenDim)}

/* ── INSIGHT CARDS ── */
/* task #303: same mobile overflow trap as .attn-row */
.insight{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:10px 12px;display:flex;gap:10px;margin-bottom:7px;transition:background .14s var(--ease),border-color .14s var(--ease);max-width:100%;overflow:hidden}
.insight > div[style*="flex:1"]{min-width:0;flex:1 1 0;max-width:100%;overflow:hidden}
.insight[onclick]{cursor:pointer}
.insight[onclick]:hover{background:var(--bg3);border-color:var(--border2)}
/* Dashboard "Needs attention" rows — same hover pattern as other rows. */
/* task #303: mobile overflow — row needs max-width:100% so its child flex:1 wrapper actually has a bounded width to compute against */
.attn-row{display:flex;align-items:center;gap:10px;padding:9px 10px;border-bottom:1px solid var(--border);border-radius:var(--r);transition:background .14s var(--ease);max-width:100%;overflow:hidden}
.attn-row:last-child{border-bottom:none}
.attn-row:hover{background:var(--bg3)}
/* task #300: also constrain attn-msg + ensure attn-name truncates against parent width */
.attn-row > div{min-width:0;flex:1 1 0;max-width:100%}
/* task #305: switch from nowrap+ellipsis to break-all. Long camelCase plant
   names weren't being truncated on iOS — ellipsis requires the parent to have a
   bounded width AND for word-break to allow truncation. break-all wraps to
   multi-line within the card which is uglier but ALWAYS fits. */
.attn-name{font-size:12px;font-weight:600;word-break:break-all;overflow-wrap:anywhere;max-width:100%}
.attn-msg{font-size:11px;color:var(--text2);margin-top:1px;word-break:break-all;overflow-wrap:anywhere;max-width:100%}
.insight-icon{font-size:16px;flex-shrink:0;margin-top:1px}
/* task #300: prevent long plant names + long body text from overflowing the insight card */
/* task #305: word-break:break-all forces character-level breaks even for camelCase
   strings like "Kcbxidken..." that iOS Safari otherwise refuses to break with
   overflow-wrap:anywhere alone. Combined with overflow:hidden on parent .insight
   row, this absolutely prevents horizontal overflow. */
.insight-title{font-size:12px;font-weight:500;margin-bottom:2px;word-break:break-all;overflow-wrap:anywhere;max-width:100%}
.insight-body{font-size:11px;color:var(--text1);line-height:1.55;word-break:break-all;overflow-wrap:anywhere;max-width:100%}

/* ── CALENDAR ── */
.cal-wrap{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);overflow:hidden;width:100%;max-width:100%}
.cal-dow-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));background:var(--bg3);border-bottom:1px solid var(--border)}
.cal-dow{font-size:10px;font-weight:500;color:var(--text2);padding:9px 0;text-align:center;letter-spacing:.06em;min-width:0;overflow:hidden}
.cal-cells{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));width:100%}
.cal-cell{min-height:88px;min-width:0;overflow:hidden;border-right:1px solid var(--border);border-bottom:1px solid var(--border);padding:6px;cursor:pointer;transition:background .12s;background:var(--bg1)}
.cal-cell:nth-child(7n){border-right:none}
.cal-cell.empty{background:var(--bg2);cursor:default;pointer-events:none}
.cal-cell:not(.empty):hover{background:var(--bg3)}
.cal-cell.is-today{background:rgba(62,207,142,.10)}
.cal-cell.is-today .cal-day-num{color:var(--green);font-weight:700}
.cal-day-num{font-size:11px;color:var(--text3);width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:50%;margin-bottom:3px}
.cal-ev{font-size:9px;padding:2px 5px;border-radius:3px;margin-bottom:2px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;line-height:1.4}
.cal-more{font-size:9px;color:var(--text3)}
.cal-detail{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);padding:1rem;margin-top:10px}

/* ── MODAL ── */
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;z-index:200;padding:1rem;backdrop-filter:blur(2px)}
.modal-bg.off{display:none}
/* Pro-locked items — dimmed with a lock badge overlay. Used when a paid
   Pro user downgrades to Free and has data above the Free caps. */
.pro-locked{position:relative;opacity:.45!important;filter:saturate(.4)!important;cursor:not-allowed!important;pointer-events:auto!important}
.pro-locked > *{pointer-events:none!important}
.pro-locked::before{content:'';position:absolute;inset:0;background:repeating-linear-gradient(45deg,transparent 0 8px,rgba(127,87,247,0.06) 8px 16px);border:1px dashed var(--purple);border-radius:inherit;z-index:1;pointer-events:none}
.pro-locked::after{content:'🔒 Pro only';position:absolute;top:8px;right:8px;background:var(--purpleDim);color:var(--purple);padding:3px 9px;border-radius:10px;font-size:10.5px;font-weight:700;pointer-events:none;z-index:2;border:1px solid var(--purple)}
.pro-locked.plant-card::after{top:6px;left:6px;right:auto}
/* task #103: care log + calendar rows — hide edit/delete on locked entries
   and suppress absolute ::after badge in favour of an inline .pro-pill next
   to plant name (consistent across row heights regardless of content). */
.tl-item.pro-locked .tl-edit-btn,
.task-row.pro-locked .tl-edit-btn,
.task-row.pro-locked > div > button.btn{display:none!important}
.tl-item.pro-locked::after,
.task-row.pro-locked::after{display:none}
.pro-pill{display:inline-block;margin-left:6px;padding:1px 6px;border-radius:10px;background:var(--purpleDim);color:var(--purple);font-size:9.5px;font-weight:700;border:1px solid var(--purple);vertical-align:middle;letter-spacing:.2px}
/* Block clicks from descendants of pro-locked from firing inline handlers — the
   global capture-phase listener (set up in JS) takes over and shows the upgrade
   modal instead. This is the safety net that makes the lock impossible to
   bypass via stale render output. */
.modal-box{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);width:100%;max-width:500px;max-height:95vh;overflow-y:auto;overflow-x:hidden;padding:1.4rem}
.modal-header{display:flex;align-items:center;gap:8px;margin-bottom:1.1rem}
/* task #308: modal title can be plant name — break long ones */
.modal-title{font-size:15px;font-weight:600;flex:1;min-width:0;max-width:100%;word-break:break-all;overflow-wrap:anywhere}
.modal-close{background:none;border:none;color:var(--text2);font-size:18px;cursor:pointer;padding:2px 6px;border-radius:4px;line-height:1;transition:all .14s}
.modal-close:hover{color:var(--text0);background:var(--bg4)}
.fg{margin-bottom:11px}
.fg label{display:block;font-size:11px;color:var(--text2);margin-bottom:4px;font-weight:500}
.fg input,.fg select,.fg textarea{width:100%;padding:8px 11px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);color:var(--text0);font-size:12px;outline:none;resize:vertical;transition:border-color .14s var(--ease)}
/* Hide datalist arrow on Chrome/Edge */
input::-webkit-calendar-picker-indicator{display:none!important}
input[list]::-webkit-list-button{display:none!important}
.fg input:focus,.fg select:focus,.fg textarea:focus{border-color:var(--green)}
.fg select option{background:var(--bg2);color:var(--text0)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.modal-footer{display:flex;gap:7px;justify-content:flex-end;margin-top:1.1rem;padding-top:.9rem;border-top:1px solid var(--border)}

/* ── EMOJI PICKER ── */
.emoji-grid{display:flex;flex-wrap:wrap;gap:4px}
.ep{font-size:20px;padding:5px 6px;border-radius:var(--r);border:1.5px solid transparent;cursor:pointer;transition:all .1s;line-height:1}
.ep.on{border-color:var(--green);background:var(--greenDim)}
.ep:hover:not(.on){background:var(--bg4)}

/* ── TAG BUTTONS ── */
.tag-row{display:flex;gap:5px;flex-wrap:wrap}
.tag-opt{padding:3px 10px;border-radius:20px;font-size:11px;cursor:pointer;border:1px solid var(--border);background:transparent;color:var(--text1);transition:all .14s;user-select:none}
.tag-opt.s{background:var(--greenDim);color:var(--green);border-color:var(--green)}
.tag-opt.m{background:var(--redDim);color:var(--red);border-color:var(--red)}
.tag-opt.e{background:var(--purpleDim);color:var(--purple);border-color:var(--purple)}

/* ── EMPTY STATE ── */
.empty{text-align:center;padding:3rem 1rem;color:var(--text2)}
.empty-icon{font-size:36px;margin-bottom:10px}
.empty-title{font-size:13px;font-weight:500;color:var(--text1);margin-bottom:4px}
.empty-sub{font-size:11px;line-height:1.6}

/* ── TASK ROWS ── */
/* task #306: max-width + overflow on .task-row itself — last missing piece */
.task-row{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:10px 13px;display:flex;align-items:center;gap:11px;margin-bottom:6px;transition:background .14s var(--ease),border-color .14s var(--ease);max-width:100%;overflow:hidden}
.task-row:hover{background:var(--bg3);border-color:var(--border2)}
.task-row.overdue{border-left:3px solid var(--red)}
.task-row.duetoday{border-left:3px solid var(--amber)}
.task-row:hover{border-color:var(--border2)}
/* BUG #033 fix: task row children get min-width:0 so flexbox can shrink
   them past their intrinsic content width — without this, long plant names
   pushed the badge and action button off-screen on narrow viewports. */
.task-row > div{min-width:0 !important;max-width:100% !important;flex:1 1 0 !important;overflow:hidden !important;width:0}
/* Calendar day-detail (and any task-row) action wrapper: opt OUT of the
   flex:1 1 0 stretch above so the ✏️/× group stays right-aligned and unwrapped
   on desktop (mobile grid already right-aligns it via the 3rd auto column). */
.task-row > .task-row-actions{flex:0 0 auto !important;width:auto !important;overflow:visible !important;margin-left:auto}
/* task #299: also truncate fs10 sub-line and any other text children — old
   rule only caught fs12/fw6 so the "Water · <long name>" second line still
   overflowed past the viewport on mobile. */
/* task #309: nowrap+ellipsis for single-line task row (user preference) */
.task-row .fw6, .task-row .fs12, .task-row .fs10, .task-row .cmut{white-space:nowrap !important;overflow:hidden !important;text-overflow:ellipsis !important;max-width:100% !important}
.task-row > span:first-child{flex-shrink:0}
.task-row > .badge, .task-row > button{flex-shrink:0}

/* ── GRID HELPERS ── */
.g2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.g3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px}
.g4{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
.ga{display:grid;grid-template-columns:repeat(auto-fill,minmax(265px,1fr));gap:12px}
@media(max-width:700px){
  .app{flex-direction:column}
  .sidebar{position:relative;width:100%;height:auto;border-right:none;border-bottom:1px solid var(--border)}
  .main{margin-left:0;width:100%}
  .content{padding:.75rem;overflow-x:hidden}
  .g2,.g3,.g4{grid-template-columns:1fr}
  .cal-cell{min-height:60px;padding:3px}
  .page-header{flex-wrap:wrap;gap:6px}
  .pill-row{flex-wrap:wrap}
  .card{padding:10px}
  .det-hero{flex-wrap:wrap}
  
  /* task #314 v3: USE CSS GRID — fully removes the scroll-container nature.
     Grid with auto-fit means tabs are positioned cells, not scrollable items.
     There is LITERALLY nothing to scroll. iOS can't rubber-band a grid.
     overscroll-behavior + touch-action belt-and-suspenders. */
  .tabs{
    display:grid !important;
    grid-auto-flow:column !important;
    grid-auto-columns:minmax(0,1fr) !important;
    overflow:hidden !important;
    touch-action:pan-y !important;
    overscroll-behavior:contain !important;
    -webkit-overflow-scrolling:auto !important;
    flex-wrap:initial;
  }
  .tab-btn{
    white-space:nowrap;
    min-width:0;
    padding-left:6px;
    padding-right:6px;
    font-size:12px;
    text-align:center;
    overflow:hidden;
    text-overflow:ellipsis;
    flex:initial;
  }
  .modal-box{margin:.5rem;width:calc(100vw - 1rem);padding-bottom:0}
  .modal-footer{position:sticky;bottom:0;background:var(--bg2);padding:12px 1.5rem calc(env(safe-area-inset-bottom,0px) + 12px);z-index:3;border-top:1px solid var(--border);margin:0 -1.5rem -1.5rem}
  #page-calendar [style*="grid-template-columns:1fr 220px"]{grid-template-columns:1fr!important}
}
@media(max-width:960px){.g4{grid-template-columns:1fr 1fr}.g3{grid-template-columns:1fr 1fr}}

/* ── UTILS ── */
.flex{display:flex}.aic{align-items:center}.jsb{justify-content:space-between}.gap1{gap:6px}.gap2{gap:12px}
.mb1{margin-bottom:6px}.mb2{margin-bottom:12px}.mb3{margin-bottom:20px}
.mt1{margin-top:6px}.mt2{margin-top:12px}
.cg{color:var(--green)}.ca{color:var(--amber)}.cr{color:var(--red)}.cb{color:var(--blue)}.cp{color:var(--purple)}.ct{color:var(--teal)}.co{color:var(--orange)}.cmut{color:var(--text2)}
.fs10{font-size:10px}.fs11{font-size:11px}.fs12{font-size:12px}.fw6{font-weight:600}
.cw{position:relative;height:200px}
.cw-sm{position:relative;height:140px}

/* ── AUTH SCREEN ── */
.auth-screen{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:10000;padding:1rem;overflow:hidden;background:var(--bg0)}
.auth-box{background:var(--bg1);border:1px solid var(--border);border-radius:16px;padding:1.5rem 1.25rem;width:100%;max-width:400px;text-align:center;overflow-y:auto;max-height:90vh}
.auth-logo{display:flex;align-items:center;justify-content:center;gap:9px;margin-bottom:6px}
.auth-logo-name{font-size:22px;font-weight:700;letter-spacing:-.4px}
.auth-tagline{font-size:10px;color:var(--text2);letter-spacing:.1em;text-transform:uppercase;margin-bottom:2rem}
.auth-tabs{display:flex;background:var(--bg3);border-radius:var(--r);padding:3px;margin-bottom:1.5rem;gap:3px}
.auth-tab{flex:1;padding:7px;border-radius:6px;font-size:12px;font-weight:500;cursor:pointer;border:none;background:transparent;color:var(--text2);transition:all .15s}
.auth-tab.on{background:var(--bg1);color:var(--text0);box-shadow:0 1px 3px rgba(0,0,0,.2)}
.auth-field{margin-bottom:11px;text-align:left}
.auth-field label{display:block;font-size:11px;color:var(--text2);margin-bottom:4px;font-weight:500}
.auth-field input{width:100%;padding:10px 12px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);color:var(--text0);font-size:13px;outline:none;transition:border-color .14s var(--ease)}
.auth-field input:focus{border-color:var(--green)}
.auth-btn{width:100%;padding:11px;background:var(--green);color:#051a0e;border:none;border-radius:var(--r);font-size:13px;font-weight:600;cursor:pointer;margin-top:4px;transition:filter .15s}
.auth-btn:hover{filter:brightness(1.08)}
.auth-err{font-size:11px;color:var(--red);margin-top:8px;min-height:16px}
.auth-switch{font-size:11px;color:var(--text2);margin-top:14px}
.auth-switch span{color:var(--blue);cursor:pointer}
.auth-switch span:hover{color:var(--text0)}
/* BUG #057 fix: the auth screen is intentionally a dark glass card on a
   green gradient backdrop — same look regardless of the user's theme
   preference. Without these overrides, light-mode users got cream
   text/inputs inside the dark glass auth box (unreadable). These rules
   force dark-mode colors on auth controls in light mode. */
html.light .auth-field label{color:rgba(255,255,255,.55)}
html.light .auth-field input{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.18);color:#fff}
html.light .auth-field input:focus{border-color:var(--green)}
html.light .auth-tabs{background:rgba(0,0,0,.32)}
html.light .auth-tab{color:rgba(255,255,255,.55)}
html.light .auth-tab.on{background:rgba(255,255,255,.12);color:#fff}
html.light .auth-tagline{color:rgba(255,255,255,.55)}
/* BUG #057 fix v2: Chrome/Edge autofill repaints input backgrounds with a
   forced light yellow + dark text, overriding the CSS above. The result on
   our dark glass auth box: a white block of unreadable text after the
   browser fills in a saved email. The text-fill-color trick is the
   accepted workaround — autofill respects -webkit-text-fill-color but not
   color, and we can force the background via an inset box-shadow that
   matches our intended look. Applied in BOTH themes because Chrome
   autofill is intrusive in either case. */
.auth-field input:-webkit-autofill,
.auth-field input:-webkit-autofill:hover,
.auth-field input:-webkit-autofill:focus,
.auth-field input:-webkit-autofill:active{
  -webkit-text-fill-color:#fff !important;
  -webkit-box-shadow:0 0 0 1000px rgba(20,30,25,.95) inset !important;
  box-shadow:0 0 0 1000px rgba(20,30,25,.95) inset !important;
  caret-color:#fff !important;
  transition:background-color 9999s ease-in-out 0s;
}

/* ── USER AVATAR IN SIDEBAR ── */
.sb-user{display:flex;align-items:center;gap:9px;padding:.75rem .875rem;border-top:1px solid var(--border);cursor:pointer;transition:background .14s}
.sb-user:hover{background:var(--bg3)}
.sb-avatar{width:30px;height:30px;border-radius:50%;background:var(--greenDim);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:var(--green);flex-shrink:0;border:1.5px solid var(--green)}
.sb-user-info{flex:1;min-width:0}
.sb-user-name{font-size:12px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sb-user-email{font-size:9px;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ── ONBOARDING ── */
.ob-screen{position:fixed;inset:0;background:var(--bg0);z-index:10000;display:flex;align-items:center;justify-content:center;padding:1rem}
/* When onboarding is open, hide mobile nav, FAB, drawer & overlays just like a modal would. */
body.onboarding-open .mobile-nav,
body.onboarding-open .mobile-fab,
body.onboarding-open .mobile-fab-menu,
body.onboarding-open .mobile-fab-overlay,
body.onboarding-open .mobile-more-drawer,
body.onboarding-open .mobile-more-overlay{display:none!important;pointer-events:none!important}
.ob-box{background:var(--bg1);border:1px solid var(--border);border-radius:16px;width:100%;max-width:540px;overflow:hidden}
.ob-progress{height:3px;background:var(--bg3)}
.ob-progress-fill{height:100%;background:var(--green);transition:width .4s}
.ob-body{padding:2.25rem 2rem}
.ob-step-num{font-size:10px;color:var(--green);font-weight:600;text-transform:uppercase;letter-spacing:.1em;margin-bottom:10px}
.ob-title{font-size:20px;font-weight:700;letter-spacing:-.3px;margin-bottom:8px}
.ob-sub{font-size:13px;color:var(--text1);line-height:1.65;margin-bottom:1.75rem}
.ob-footer{display:flex;justify-content:space-between;align-items:center;padding:1rem 2rem;border-top:1px solid var(--border);background:var(--bg2)}
.ob-dots{display:flex;gap:6px}
.ob-dot{width:6px;height:6px;border-radius:50%;background:var(--bg4);transition:all .2s var(--ease)}
.ob-dot.on{background:var(--green);width:18px;border-radius:3px}
.ob-feature{display:flex;gap:14px;align-items:flex-start;padding:12px 14px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);margin-bottom:8px}
.ob-feature-icon{font-size:22px;flex-shrink:0;margin-top:1px}
.ob-feature-title{font-size:12.5px;font-weight:500;margin-bottom:2px}
.ob-feature-sub{font-size:11px;color:var(--text2);line-height:1.5}

/* ── REMINDERS ── */
.reminder-item{display:flex;align-items:center;gap:12px;padding:11px 13px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);margin-bottom:7px}
.reminder-icon{font-size:18px;flex-shrink:0}
.reminder-title{font-size:12px;font-weight:500;margin-bottom:1px}
.reminder-time{font-size:10px;color:var(--text2)}
.reminder-toggle{margin-left:auto;position:relative;width:36px;height:20px;flex-shrink:0}
.reminder-toggle input{opacity:0;width:0;height:0;position:absolute}
.toggle-track{position:absolute;inset:0;background:var(--bg4);border-radius:10px;border:1px solid var(--border2);cursor:pointer;transition:all .2s var(--ease)}
.toggle-track::after{content:'';position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--text2);transition:all .2s var(--ease)}
input:checked+.toggle-track{background:var(--greenDim);border-color:var(--green)}
input:checked+.toggle-track::after{left:18px;background:var(--green)}

/* ── SETTINGS ── */
.settings-section{margin-bottom:1rem;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r2);overflow:hidden}
.settings-title{font-size:11px;font-weight:600;color:var(--text1);padding:14px 16px 10px}
.settings-row{display:flex;align-items:center;gap:12px;padding:11px 16px;border-top:1px solid var(--border);transition:background .14s var(--ease)}
.settings-row:hover{background:var(--bg3)}
.settings-row-icon{font-size:18px;flex-shrink:0;width:24px;text-align:center}
.settings-row-body{flex:1;min-width:0}
.settings-row-label{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.settings-row-sub{font-size:11px;color:var(--text2);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.settings-row-action{margin-left:auto;flex-shrink:0;display:flex;align-items:center}
.settings-row-danger{background:rgba(255,95,87,.06)}
.settings-row-danger .settings-row-label{color:var(--red)}
.danger-zone{background:var(--redDim);border-color:var(--red)}

/* ── SEARCH ── */
.search-wrap{position:relative;margin-bottom:12px}
.search-wrap svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--text2);width:13px;height:13px;pointer-events:none}
.search-input{width:100%;padding:9px 12px 9px 32px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);color:var(--text0);font-size:12px;outline:none;transition:border-color .14s var(--ease)}
.search-input:focus{border-color:var(--green)}

/* ── PLANT LIBRARY DROPDOWN ── */
.lib-dropdown{position:absolute;top:100%;left:0;right:0;background:var(--bg2);border:1px solid var(--border2);border-radius:var(--r);z-index:100;max-height:220px;overflow-y:auto;margin-top:2px;box-shadow:0 8px 24px rgba(0,0,0,.3)}
.lib-item{display:flex;align-items:center;gap:10px;padding:9px 12px;cursor:pointer;transition:background .12s;font-size:12px}
.lib-item:hover{background:var(--bg3)}
.lib-item-name{font-weight:500}
.lib-item-sub{font-size:10px;color:var(--text2)}

/* ── TOAST NOTIFICATIONS ── */
.toast-wrap{position:fixed;top:calc(env(safe-area-inset-top) + 12px);right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}
.toast{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:11px 15px;font-size:12px;display:flex;align-items:center;gap:9px;min-width:260px;max-width:360px;pointer-events:none;box-shadow:0 4px 16px rgba(0,0,0,.3);animation:slideIn .2s ease}
.toast.success{border-left:3px solid var(--green)}
.toast.warning{border-left:3px solid var(--amber)}
.toast.info{border-left:3px solid var(--blue)}
@keyframes slideIn{from{transform:translateX(20px);opacity:0}to{transform:translateX(0);opacity:1}}
@keyframes fadeOut{to{opacity:0;transform:translateX(20px)}}

/* ── IN-APP REMINDER BELL ── */
.reminder-bell{position:relative;cursor:pointer;padding:6px;border-radius:var(--r);color:var(--text2);transition:color .14s}
.reminder-bell:hover{color:var(--text0);background:var(--bg3)}
.bell-badge{position:absolute;top:2px;right:2px;width:8px;height:8px;border-radius:50%;background:var(--amber);border:2px solid var(--bg1);display:none}





/* ── PRODUCT TOUR ── */
/* BUG #062 fix: tour overlay used to have pointer-events:none, so any
   click went through to whatever happened to be underneath. Users could
   accidentally open modals, navigate away, etc. mid-tour. Now the overlay
   catches all clicks. The Next/Skip buttons sit on top (pointer-events:all
   already on .tour-tooltip) so they still work. The highlighted element
   stays non-interactive — the tour is purely demonstrative. */
.tour-overlay{position:fixed;inset:0;z-index:10000;pointer-events:auto;cursor:default}
.tour-highlight{position:fixed;z-index:10001;border-radius:8px;box-shadow:0 0 0 4px var(--green),0 0 0 9999px rgba(0,0,0,.55);pointer-events:none;transition:left .3s cubic-bezier(.4,0,.2,1),top .3s cubic-bezier(.4,0,.2,1),width .3s cubic-bezier(.4,0,.2,1),height .3s cubic-bezier(.4,0,.2,1),opacity .2s linear}
.tour-tooltip{position:fixed;z-index:10002;background:var(--bg1);border:1px solid var(--green);border-radius:12px;padding:1rem 1.1rem;width:280px;box-shadow:0 8px 32px rgba(0,0,0,.4);pointer-events:all;opacity:0;transition:opacity .22s ease}
.tour-tooltip.show{opacity:1}
/* When the tour is open, hide mobile nav / FAB / drawer just like a modal would.
   Otherwise the bottom nav (z-index:9000) would cover the tour's Skip/Next buttons. */
body.tour-open .mobile-nav,
body.tour-open .mobile-fab,
body.tour-open .mobile-fab-menu,
body.tour-open .mobile-fab-overlay,
body.tour-open .mobile-more-drawer,
body.tour-open .mobile-more-overlay{display:none!important;pointer-events:none!important}
/* Lock page scroll while the product tour is active — applies to desktop
   AND mobile so the spotlight stays anchored to the target element. */
body.tour-open{overflow:hidden!important}
.tour-tooltip::before{content:'';position:absolute;width:10px;height:10px;background:var(--bg1);border:1px solid var(--green);transform:rotate(45deg);display:none}
.tour-tooltip.arrow-left::before,
.tour-tooltip.arrow-right::before,
.tour-tooltip.arrow-top::before,
.tour-tooltip.arrow-bottom::before{display:block}
.tour-tooltip.arrow-left::before{left:-6px;top:18px;border-right:none;border-top:none}
.tour-tooltip.arrow-right::before{right:-6px;top:18px;border-left:none;border-bottom:none}
.tour-tooltip.arrow-top::before{top:-6px;left:20px;border-bottom:none;border-right:none}
.tour-tooltip.arrow-bottom::before{bottom:-6px;left:20px;border-top:none;border-left:none}
.tour-step-label{font-size:10.5px;font-weight:600;color:var(--green);letter-spacing:.04em;margin-bottom:6px}
.tour-title{font-size:14px;font-weight:700;margin-bottom:5px;color:var(--text0)}
.tour-text{font-size:12px;color:var(--text1);line-height:1.65;margin-bottom:.9rem}
.tour-footer{display:flex;align-items:center;justify-content:space-between}
.tour-dots{display:flex;gap:4px}
.tour-dot{width:5px;height:5px;border-radius:50%;background:var(--bg4);transition:all .2s var(--ease)}
.tour-dot.on{background:var(--green);width:14px;border-radius:3px}
.tour-actions{display:flex;gap:6px}
.tour-skip{font-size:11px;color:var(--text2);cursor:pointer;padding:4px 6px;border-radius:4px;border:none;background:none;transition:color .14s}
.tour-skip:hover{color:var(--text0)}
.tour-next{padding:6px 14px;background:var(--green);color:#051a0e;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;transition:filter .14s}
.tour-next:hover{filter:brightness(1.1)}
.help-btn{cursor:pointer;width:32px;height:32px;border-radius:50%;background:var(--bg3);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:var(--text1);transition:all .15s;flex-shrink:0}
.help-btn:hover{background:var(--bg4);color:var(--text0);border-color:var(--border2)}

.snooze-opt{display:flex;align-items:center;gap:11px;padding:8px 13px;background:var(--bg3);border:1px solid var(--border);border-top:none;cursor:pointer;transition:all .14s;text-align:left;width:100%;color:var(--text0)}
.snooze-opt:first-child{border-top:1px solid var(--border);border-radius:var(--r) var(--r) 0 0}
.snooze-opt:last-child{border-radius:0 0 var(--r) var(--r);margin-bottom:8px}
.snooze-opt:hover{border-color:var(--green);background:var(--greenDim)}
.ai-banner{background:linear-gradient(135deg,var(--blueDim),var(--purpleDim));border:1px solid var(--blue);border-radius:var(--r2);padding:13px 15px;margin-bottom:14px;display:flex;gap:12px}
.rain-banner{background:linear-gradient(135deg,var(--tealDim),var(--blueDim));border:1px solid var(--teal);border-radius:var(--r2);padding:13px 15px;margin-bottom:14px;display:flex;gap:12px}

/* ── RECURRING TASKS ── */
/* task #313: same overflow fix as .care-row — use CSS Grid with minmax(0,1fr)
   for the middle column so long unbroken notes can't push the row past viewport. */
.rtask-item{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:10px;padding:9px 12px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);margin-bottom:6px;transition:background .14s var(--ease),border-color .14s var(--ease);max-width:100%;overflow:hidden}
.rtask-item > div{min-width:0;max-width:100%;overflow:hidden}
.rtask-item > div > div{min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
.rtask-item > span:first-child,.rtask-item > button,.rtask-item > .badge{align-self:center}
.rtask-item:hover{background:var(--bg3);border-color:var(--border2)}
.rtask-icon{font-size:16px}
.rtask-label{font-size:12px;font-weight:500}
.rtask-freq{font-size:10px;color:var(--text2)}

/* ── GOALS PROGRESS ── */
.goal-bar{height:6px;background:var(--bg4);border-radius:3px;overflow:hidden;margin:5px 0}
.goal-fill{height:100%;border-radius:3px;transition:width .5s}

/* ── HEALTH CHART TAB ── */
.health-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px}


/* ── MY SUPPLIES ── */
.supply-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:11px 14px;margin-bottom:8px;display:flex;align-items:flex-start;gap:12px;transition:background .14s var(--ease),border-color .14s var(--ease)}
.supply-card:hover{background:var(--bg3);border-color:var(--border2)}
.supply-icon{font-size:22px;flex-shrink:0;margin-top:1px}
.supply-name{font-size:13px;font-weight:600;margin-bottom:2px}
.supply-meta{font-size:11px;color:var(--text2);margin-bottom:3px}
.supply-notes{font-size:11px;color:var(--text1);line-height:1.5;margin-top:4px;font-style:italic}
.supply-actions{display:flex;gap:6px;margin-left:auto;flex-shrink:0}
@keyframes taskDone{0%{background:var(--greenDim);transform:scale(1.01)}100%{background:transparent;transform:scale(1)}}
.task-done-flash{animation:taskDone .4s ease-out}
@keyframes pulseBtn{0%,100%{box-shadow:0 0 0 0 rgba(62,207,142,.5);transform:scale(1)}50%{box-shadow:0 0 0 10px rgba(62,207,142,0);transform:scale(1.03)}}
@keyframes floatEmoji{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}

/* ── MOTION SYSTEM ── */
/* Modal entrance */
@keyframes modalIn {
  from { opacity:0; transform:scale(.96) translateY(8px); }
  to   { opacity:1; transform:scale(1)   translateY(0); }
}
@keyframes modalBgIn {
  from { opacity:0; }
  to   { opacity:1; }
}
@keyframes pageIn {
  from { opacity:0; transform:translateY(6px); }
  to   { opacity:1; transform:translateY(0); }
}
@keyframes taskDone {
  0%   { transform:scale(1); }
  40%  { transform:scale(1.06); }
  70%  { transform:scale(.97); }
  100% { transform:scale(1); opacity:.4; }
}
@keyframes skeletonPulse {
  0%,100% { opacity:.45; }
  50%      { opacity:.9; }
}
@keyframes scoreRingIn {
  from { stroke-dashoffset:var(--dash-total,251); }
  to   { stroke-dashoffset:var(--dash-offset,0); }
}
@keyframes healthPulse {
  0%,100% { box-shadow:0 0 0 0 rgba(62,207,142,.35); }
  50%     { box-shadow:0 0 0 6px rgba(62,207,142,0); }
}
@keyframes plantCardIn {
  from { opacity:0; transform:translateY(12px) scale(.98); }
  to   { opacity:1; transform:translateY(0)    scale(1); }
}
@keyframes fadeSlideIn {
  from { opacity:0; transform:translateY(4px); }
  to   { opacity:1; transform:translateY(0); }
}

/* Modal entrance animation */
.modal-bg:not(.off) { animation:modalBgIn .18s ease; }
.modal-bg:not(.off) .modal-box { animation:modalIn .22s cubic-bezier(.34,1.26,.64,1); }

/* Page transition */
.page.active { animation:pageIn .2s cubic-bezier(.4,0,.2,1); }

/* Card base transition for themed changes only */
.card {
  transition:box-shadow .18s ease !important;
}

/* Plant cards stagger in */
.plant-card {
  animation:plantCardIn .28s cubic-bezier(.34,1.1,.64,1) both;
}
.plant-card:hover {
  border-color:var(--green) !important;
  transform:translateY(-3px) !important;
  box-shadow:0 8px 28px rgba(0,0,0,.22) !important;
}
.plant-card:nth-child(1){ animation-delay:.03s }
.plant-card:nth-child(2){ animation-delay:.06s }
.plant-card:nth-child(3){ animation-delay:.09s }
.plant-card:nth-child(4){ animation-delay:.12s }
.plant-card:nth-child(5){ animation-delay:.15s }
.plant-card:nth-child(6){ animation-delay:.18s }
.plant-card:nth-child(n+7){ animation-delay:.2s }

/* Tab indicator — smooth sliding underline */
.tabs { position:relative; }
.tab-btn {
  transition:color .18s ease, border-bottom-color .18s ease !important;
  position:relative;
}
.tab-btn.on {
  transition:color .18s ease !important;
}

/* Task row completion flash */
.task-done-anim { animation:taskDone .45s cubic-bezier(.4,0,.2,1) forwards; }

/* Sidebar items */
.sb-item { transition:background .16s ease, padding-left .16s ease !important; }
.sb-item.active { padding-left:18px; }
/* Tools subitems (Analytics / Recurring Tasks / My Supplies) are visually
   indented under the "Tools" header. Previously this was done via inline
   `style="padding-left:22px"`, which overrode the .active indent-in effect
   so those three items felt "dead" when selected. The class-based approach
   restores the same hover/active feel as the top-level items, just shifted
   over by one indent level. */
.sb-item.sb-sub { padding-left:22px; }
.sb-item.sb-sub.active { padding-left:29px; }

/* Buttons — spring press */
.btn { transition:all .14s cubic-bezier(.34,1.26,.64,1) !important; }
.btn:active { transform:scale(.96); }

/* Snooze options entrance */
.snooze-opt {
  animation:fadeSlideIn .18s cubic-bezier(.4,0,.2,1) both;
}
.snooze-opt:nth-child(1){animation-delay:.03s}
.snooze-opt:nth-child(2){animation-delay:.07s}
.snooze-opt:nth-child(3){animation-delay:.11s}
.snooze-opt:nth-child(4){animation-delay:.15s}

/* ── FEATURE 3: REDUCE BORDERS / BREATHING ── */
/* Cards rely on shadow, not hard border in dark mode */
.card { border-color:rgba(255,255,255,.04) !important; }
.light .card { border-color:var(--border) !important; }

/* Sidebar softer border */
.sidebar { border-right-color:rgba(255,255,255,.05) !important; }
.light .sidebar { border-right-color:var(--border) !important; }

/* Modal softer border */
.modal-box { border-color:rgba(255,255,255,.07) !important; }
.light .modal-box { border-color:var(--border) !important; }

/* Task rows — no hard bottom border, use spacing */
.task-row { border-bottom-color:rgba(255,255,255,.04) !important; }
.light .task-row { border-bottom-color:var(--border) !important; }

/* Timeline items */
.tl-item { border-left-color:rgba(255,255,255,.06) !important; }
.light .tl-item { border-left-color:var(--border) !important; }

/* ── FEATURE 5: LIVING UI SIGNATURE ── */
/* Health ring pulse — only on thriving plants */
.health-pulse {
  animation:healthPulse 2.4s ease-in-out infinite;
  border-radius:50%;
  display:inline-flex;
}
/* Score ring draw animation */
.score-ring-arc {
  animation:scoreRingIn .8s cubic-bezier(.4,0,.2,1) both;
  animation-delay:.1s;
}

/* ── FEATURE 6: SKELETON LOADERS ── */
.skeleton {
  background:var(--bg3);
  border-radius:6px;
  animation:skeletonPulse 1.4s ease-in-out infinite;
}
.skeleton-line  { height:12px; margin-bottom:8px; }
.skeleton-line.short { width:60%; }
.skeleton-line.med   { width:80%; }
.skeleton-title { height:18px; margin-bottom:12px; }
.skeleton-card  {
  height:80px;
  border-radius:var(--r2);
  background:var(--bg3);
  animation:skeletonPulse 1.4s ease-in-out infinite;
}

/* ── ROOM COLLECTIONS ── */
.room-group { margin-bottom: 24px; }
.room-group-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  /* task #297: same flex-overflow trap as #264 — children need min-width:0
     to shrink, else a long room name pushes past the viewport edge on mobile. */
  min-width: 0;
  max-width: 100%;
}
.room-group-header > * { min-width: 0; }
.room-group-icon { font-size: 16px; flex-shrink: 0; }
.room-group-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text0);
  letter-spacing: -.01em;
  /* Truncate long room names with ellipsis instead of overflowing the page */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}
.room-group-count {
  font-size: 11px;
  color: var(--text2);
  background: var(--bg3);
  border-radius: 20px;
  padding: 2px 8px;
  margin-left: 2px;
  flex-shrink: 0;
}
.room-group-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}

/* task #303: parent containers also bounded so children can't push them */
.attention-list{max-width:100%;overflow:hidden}
.attention-list > div:last-child{border-bottom:none!important}
#dash-attention,#dash-insights{max-width:100%;overflow:hidden}

/* ═══════════════════════════════════════
   MOBILE LAYOUT  (max-width: 700px)
═══════════════════════════════════════ */
@media(max-width:700px){
  /* Hide desktop sidebar */
  .sidebar { display:none !important; }
  .main { margin-left:0 !important; width:100% !important; max-width:100% !important; }
  .topbar { padding:0 10px; gap:6px; }
  .content { padding:12px 12px 80px; overflow-x:hidden; max-width:100%; }

  /* Topbar: title on the left, action buttons hug the right edge. The
     spacer keeps flex:1 so it pushes Demo / ? / 🔔 / 🌙 all the way right
     (matches desktop). Title can ellipsis-truncate if it gets long. */
  .tb-page { min-width:0 !important; flex-shrink:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .tb-sp { flex:1 1 auto !important; min-width:0; }

  /* iOS Safari zooms when an input has font-size <16px and refuses to zoom
     back out. Force 16px on all editable form fields on mobile only. */
  input[type="text"], input[type="number"], input[type="date"], input[type="time"],
  input[type="email"], input[type="search"], input[type="tel"], input[type="url"],
  input[type="password"], textarea, select { font-size:16px !important; }

  /* Body scroll lock during product tour — matches the modal/onboarding
     pattern. Without this users can scroll the page while the tour spotlight
     stays fixed in place, breaking the experience. */
  body.tour-open { overflow:hidden !important; touch-action:none; }
  #demo-btn { padding:5px 9px !important; font-size:10.5px !important; }
  .help-btn { width:28px !important; height:28px !important; font-size:13px; }
  .reminder-bell { padding:4px; }
  .reminder-bell svg { width:15px; height:15px; }
  /* Theme button: collapse to icon-only on small screens */
  .theme-btn { padding:4px 7px; gap:0; }
  .theme-btn .th-track, .theme-btn #th-lbl { display:none; }

  /* Bottom nav */
  .mobile-nav {
    display:flex !important;
    position:fixed;bottom:0;left:0;right:0;z-index:9000;
    background:var(--bg1);border-top:1px solid var(--border);
    padding:0 0 env(safe-area-inset-bottom,0);
    height:74px !important;align-items:stretch;overflow:visible;
  }
body.auth-visible .mobile-nav,
body.auth-visible .mobile-fab-overlay,
body.auth-visible #mobile-fab-menu,
body.auth-visible #mobile-more-drawer,
body.auth-visible #mobile-more-overlay { display:none !important; }
body.auth-visible { overflow:hidden !important; touch-action:none; }
  .mobile-nav-btn {
    flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:4px;background:none;border:none;color:var(--text2);font-size:11.5px;font-weight:500;
    cursor:pointer;padding:6px 4px;min-height:44px;transition:color .15s;
  }
  .mobile-nav-btn.active { color:var(--green); }
  .mobile-nav-btn svg { width:22px;height:22px;flex-shrink:0; }
  .mobile-nav-center { flex:0 0 64px;overflow:visible;position:relative;z-index:10001; }
  .mobile-fab-inner {
    width:44px;height:44px;border-radius:50%;
    background:var(--green);color:#051a0e;
    display:flex;align-items:center;justify-content:center;
    box-shadow:0 4px 14px rgba(62,207,142,.35);
    transform:translateY(-10px);transition:transform .15s,box-shadow .15s;
  }
  .mobile-nav-center:active .mobile-fab-inner {
    transform:translateY(-8px);box-shadow:0 2px 8px rgba(62,207,142,.25);
  }

  /* FAB menu */
  .mobile-fab-overlay {
    display:block !important;
    position:fixed;inset:0;z-index:8998;background:rgba(0,0,0,.5);
    opacity:0;transition:opacity .2s;pointer-events:none;
  }
  .mobile-fab-overlay:not(.off) { opacity:1;pointer-events:all; }
  .mobile-fab-menu {
    display:block !important;
    position:fixed;bottom:70px;left:50%;transform:translateX(-50%) translateY(10px);
    z-index:8999;background:var(--bg2);border:1px solid var(--border);
    border-radius:var(--r2);padding:8px;min-width:180px;
    opacity:0;transition:opacity .18s,transform .18s;pointer-events:none;
    box-shadow:0 8px 32px rgba(0,0,0,.3);
  }
  .mobile-fab-menu:not(.off) { opacity:1;transform:translateX(-50%) translateY(0);pointer-events:all; }
  .mobile-fab-item {
    display:flex;align-items:center;gap:10px;width:100%;padding:11px 14px;
    background:none;border:none;border-radius:var(--r);color:var(--text0);
    font-size:14px;cursor:pointer;transition:background .12s;text-align:left;
  }
  .mobile-fab-item:active { background:var(--bg3); }
  .mobile-fab-icon { font-size:20px;width:28px;text-align:center; }

  /* More drawer */
  .mobile-more-overlay {
    display:block !important;
    position:fixed;inset:0;z-index:8998;background:rgba(0,0,0,.5);
    opacity:0;transition:opacity .2s;pointer-events:none;
  }
  .mobile-more-overlay:not(.off) { opacity:1;pointer-events:all; }
  .mobile-more-drawer {
    display:block !important;
    position:fixed;bottom:0;left:0;right:0;z-index:9001;
    background:var(--bg2);border-top:1px solid var(--border);
    border-radius:var(--r2) var(--r2) 0 0;padding:8px 8px calc(env(safe-area-inset-bottom,0) + 8px);
    transform:translateY(100%);transition:transform .25s cubic-bezier(.4,0,.2,1);
  }
  .mobile-more-drawer:not(.off) { transform:translateY(0); }
  .mobile-more-handle {
    width:36px;height:4px;border-radius:2px;background:var(--border2);
    margin:4px auto 12px;
  }
  .mobile-more-item {
    display:block;width:100%;padding:13px 16px;background:none;border:none;
    border-radius:var(--r);color:var(--text0);font-size:14px;cursor:pointer;
    text-align:left;transition:background .12s;
  }
  .mobile-more-item:active { background:var(--bg3); }

  /* Modals go full screen as bottom-sheet */
  .modal-bg { align-items:flex-end !important; }
  .modal-box {
    width:100% !important;max-width:100% !important;
    margin:0 !important;
    overflow-y:auto;overflow-x:hidden !important;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;
    /* Sheet sizing/padding/border-radius now live in the authoritative
       @media(max-width:600px) block at the END of this file (clean redesign). */
  }
  /* Sticky footer always visible on mobile sheets — the button must be
     reachable no matter how long the form content is. Reinforces the
     desktop-level sticky rule with mobile-specific safe-area padding. */
  .modal-footer {
    position:sticky !important;
    bottom:0 !important;
    background:var(--bg2) !important;
    padding:12px 1.4rem calc(env(safe-area-inset-bottom,0px) + 12px) !important;
    margin:0 -1.4rem -1.4rem !important;
    border-top:1px solid var(--border) !important;
    z-index:3;
  }
  /* Bottom-sheet slide-up: smoother and more native-feeling than the subtle
     scale animation desktop uses. Off-screen → settle. The container fades
     in slightly faster than the sheet slides up for a softer entrance. */
  @keyframes mobileSheetIn {
    from { transform:translateY(100%); }
    to   { transform:translateY(0); }
  }
  .modal-bg:not(.off) .modal-box { animation:mobileSheetIn .3s cubic-bezier(.32,.72,0,1) !important; }
  .modal-bg:not(.off) { animation:modalBgIn .24s ease !important; }

  /* Plant grid 2-column */
  .ga { grid-template-columns:repeat(2,1fr) !important; gap:8px !important; }
  .room-group-grid { grid-template-columns:repeat(2,1fr) !important; gap:8px !important; }

  /* Dashboard grid stacks — catch all the common 2-col inline grid patterns */
  [style*="grid-template-columns:1fr 300px"],
  [style*="grid-template-columns:1fr 1fr"],
  [style*="grid-template-columns:2fr 1fr"],
  [style*="grid-template-columns:1fr 2fr"],
  [style*="grid-template-columns:1fr 220px"] { grid-template-columns:1fr !important; }

  /* Bigger tap targets */
  .btn { min-height:44px; }
  .btn-xs { min-height:36px; }
  .tl-edit-btn { width:30px;height:30px;font-size:13px;display:inline-flex;align-items:center;justify-content:center;padding:0;line-height:1; }
  .task-row { padding:12px 10px !important; }
  .tab-btn { min-height:40px;padding:8px 14px; }

  /* Care log timeline */
  .tl-card { padding:10px 12px; }

  /* Topbar */
  .tb-page { font-size:16px; }

  /* Misc */
  .g2,.g3,.g4 { grid-template-columns:1fr; }

  /* Dashboard metrics — compress so the actionable Tasks list isn't pushed
     half a screen down. My plants + Need attention sit in a 2-col row, the
     weather card spans both columns below them. */
  #dash-metrics { grid-template-columns:1fr 1fr !important; gap:10px !important; }
  #dash-metrics > #weather-metric-card { grid-column:1 / -1; }
  .det-hero { flex-wrap:wrap; }
  
  .tabs { flex-wrap:nowrap;overflow-x:auto; }
  .tab-btn { white-space:nowrap;flex-shrink:0; }
  .cal-cell { min-height:60px;padding:3px; }
  .pill-row { flex-wrap:wrap; }
  #page-calendar [style*="grid-template-columns:1fr 220px"]{grid-template-columns:1fr!important}

  /* Plant card mobile — tighter for 2-col */
  .pc-top { height:56px; }
  .pc-name { font-size:12px; }
  .badge { font-size:9px; padding:2px 5px; }
  .det-name { font-size:18px; }
  .dq { padding:6px 8px; }

  /* Tasks page header: stack the filter pills and the action buttons vertically
     so the row isn't cramped, and let action buttons wrap cleanly. */
  #page-tasks > .flex.aic.jsb {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 10px;
  }
  #page-tasks > .flex.aic.jsb > div:last-child {
    display: flex !important;
    flex-wrap: wrap;
    gap: 6px;
  }
  #page-tasks > .flex.aic.jsb > div:last-child .btn {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 11px;
    padding: 7px 8px;
    min-height: 34px !important;
    line-height: 1.2;
    white-space: nowrap;
  }

  /* Plants page header: same idea — stack tabs (My Plants / Archived) above
     the sort + new-plant controls. Without this they collide on narrow screens. */
  #page-plants > div[style*="justify-content:space-between"] {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 10px;
  }
  #page-plants > div[style*="justify-content:space-between"] > div:last-child {
    flex-wrap: wrap;
    gap: 6px !important;
    justify-content: flex-end;
  }
  #page-plants > div[style*="justify-content:space-between"] #plant-count-label {
    flex: 1 1 100%;
    text-align: left;
    order: -1;
  }
  #page-plants #plant-sort { flex: 1 1 auto; min-width: 0; }
  #page-plants > div[style*="justify-content:space-between"] .btn { white-space: nowrap; }

  /* Make My Plants / Archived tabs fill the full row on mobile (50/50) so
     the right half isn't dead space. The container is wrapped in a flex
     pill row; each button becomes flex:1, centered text. */
  #page-plants #tab-active, #page-plants #tab-archived {
    flex:1 1 0;
    justify-content:center;
    display:flex;
    align-items:center;
    gap:6px;
    text-align:center;
  }
  #page-plants #tab-active, #page-plants #tab-archived,
  #page-plants > div[style*="justify-content:space-between"] > div:first-child {
    width:100%;
  }

  /* Analytics page: the "13 weeks" chart has 13 W1..W13 ticks; on narrow screens
     allow x-axis labels to rotate or shrink so they don't push the canvas past
     viewport (the actual canvas is responsive, but the parent grid was 2fr 1fr;
     fixed above). Also make the harvest banner row stack. */
  #page-analytics [style*="display:grid"] { gap: 12px !important; }
}
.mobile-nav,
.mobile-fab-menu,
.mobile-fab-overlay,
.mobile-more-drawer,
.mobile-more-overlay { display:none; }


/* ── DATE FIELD WITH CALENDAR ICON ── */
/* color-scheme themes the NATIVE date/time picker popover/sheet on every
   modern browser (Chrome, Edge, Safari macOS, iOS Safari 15+, Android Chrome).
   The trick is: it must be set on :root and INHERITED down without per-element
   overrides — otherwise iOS Safari ignores the value and falls back to the
   system theme. Keep it simple: one declaration on :root, one override for
   light mode, no per-input duplication. */
:root{color-scheme:dark}
html.light{color-scheme:light}
/* Explicitly inherit on every container the date/time inputs live in, so the
   computed color-scheme reaches the input element correctly even when modals
   or pages establish a new stacking context. */
body,.page,.modal-bg,.modal-box,.fg,.date-wrap,.auth-field{color-scheme:inherit}
.date-wrap{display:flex;align-items:center;gap:0;position:relative}
input[type="date"]{flex:1;border-radius:var(--r) 0 0 var(--r)}
.date-cal-btn{width:34px;height:100%;min-height:36px;display:flex;align-items:center;justify-content:center;background:var(--bg3);border:1px solid var(--border);border-left:none;border-radius:0 var(--r) var(--r) 0;cursor:pointer;font-size:14px;transition:background .15s;flex-shrink:0}
.date-cal-btn:hover{background:var(--bg4)}

/* task #133 (bug 1): hide app shell during initial auth check so we don't show 1s of cached dashboard before the auth screen appears */
body.auth-checking .content,body.auth-checking .sidebar,body.auth-checking .topbar,body.auth-checking #auth-screen,body.auth-checking .mobile-nav{visibility:hidden!important}
body.auth-checking::before{content:'';position:fixed;top:50%;left:50%;width:32px;height:32px;border:3px solid var(--bg3);border-top-color:var(--green);border-radius:50%;animation:gf-loading-spin 0.8s linear infinite;transform:translate(-50%,-50%);z-index:99999}
@keyframes gf-loading-spin{to{transform:translate(-50%,-50%) rotate(360deg)}}

/* ──────────────────────────────────────────────────────────
   task #228: Mobile polish (QA section 15)
   --------------------------------------------------------
   1. Disable pull-to-refresh. SPAs lose state on a reload-pull,
      and the gesture conflicts with internal scroll containers.
   2. Use dynamic viewport height (100dvh) so iOS Safari's
      collapsing address bar doesn't make the layout jump
      ("flicker / disappear for a millisecond" while scrolling).
   3. Enable iOS momentum scrolling on modals so the in-modal
      scroll feels smooth instead of stuttery.
   4. Prevent rubber-banding on the body so horizontal swipes
      don't trigger the browser's back gesture mid-edit.
   ────────────────────────────────────────────────────────── */
html, body {
  overscroll-behavior: none;
  overscroll-behavior-y: none;
  -webkit-overflow-scrolling: touch;
}
/* Dynamic viewport height — fall back to 100vh if dvh unsupported. */
body { min-height: 100vh; min-height: 100dvh; }
.app { min-height: 100vh; min-height: 100dvh; }
.main { min-height: 100vh; min-height: 100dvh; }
.sidebar { height: 100vh; height: 100dvh; }

/* Momentum scrolling for any vertically-scrolling container so
   touch-flick feels native on iOS. */
.modal-box,
.modal-body,
.tab-panel,
.tl,
.photo-grid,
.content {
  -webkit-overflow-scrolling: touch;
}

/* Topbar repaint smoothing — force GPU compositing so the
   backdrop-filter blur doesn't trigger layout thrash on scroll. */
.topbar {
  will-change: transform;
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
}

/* ──────────────────────────────────────────────────────────
   task #230: Mobile scroll flicker fix
   --------------------------------------------------------
   Three sources of scroll-time repaints identified:
   - .plant-card transitions + plantCardIn animation
   - backdrop-filter blur on the topbar (super expensive on iOS)
   - Chart.js redraws on every viewport resize (toolbar collapse)
   This block kills the first two on mobile. The chart fix is
   in growflow.js (resizeDelay added to all Chart instances).
   ────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* No card animation/transition during scroll — repaints kill perf */
  .plant-card {
    animation: none !important;
    transition: none !important;
  }
  /* Replace expensive blur with solid background on mobile */
  .topbar,
  html.light .topbar {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: var(--bg1) !important;
  }
  /* Modal background blur is small (2px) so usually fine, but on weak
     phones even that triggers compositor churn. Drop it on mobile. */
  .modal-bg {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* task #232: Chart container isolation. With CSS containment, any layout
   change outside the chart wrapper (sidebar reflow, viewport-height tick
   when iOS toolbar moves) can't propagate into the chart's box. The
   ResizeObserver Chart.js attaches won't see size changes, so it won't
   redraw on scroll. */
.cw, .cw-sm {
  contain: layout style paint;
}
/* On mobile specifically, also lock chart container sizes so even a
   flex-1 wrapper inside the dashboard can't change height on scroll. */
@media (max-width: 768px) {
  .cw { height: 200px !important; flex: 0 0 auto !important; }
  .cw-sm { height: 140px !important; }
  /* Defensive: any inline-style "min-height" that flex-1 might honor is
     overridden so we never resize. */
  [class~="cw"][style*="flex"] { flex: 0 0 auto !important; }
}

/* task #234: weather card minimum height + contain — same flicker class
   of fix as the chart wrappers. Even if we ever briefly show a different
   state of the weather widget, the card height stays constant so the
   surrounding layout doesn't reflow. */
#weather-metric-card {
  contain: layout style;
  min-height: 96px;
}

/* task #236: mobile modal full-bottom + sort dropdown sizing.
   --------------------------------------------------------
   - Modal sheet now fills to 100dvh so the Save/Cancel row sits at
     the very bottom edge of the visible viewport (just above iOS
     home indicator via safe-area). Was 92dvh which left a gap.
   - Sort dropdown on Plants page was eating ~65% of the width;
     now caps it and lets "+ New plant" breathe.
   ────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* task #236 modal-box override REMOVED — its max-height:100dvh + border-radius:0
     forced the sheet to the very top, putting the X under the notch. The mobile
     sheet is now defined solely in the @media(max-width:600px) authority block
     at the end of this file. */
  /* Plants page: iOS Safari renders native <select> at its own
     default size and ignores font-size inline. -webkit-appearance:none
     forces our CSS to actually apply. Custom CSS dropdown arrow keeps
     the affordance visible since we removed the native chevron. */
  #plant-sort {
    -webkit-appearance: none !important;
    appearance: none !important;
    /* task #238: full-width like before but slim height — fills the row
       next to "+ New plant" with the same vertical footprint as the
       button so they line up cleanly. */
    flex: 1 1 auto !important;
    width: auto !important;
    max-width: none !important;
    font-size: 13px !important;
    padding: 4px 24px 4px 12px !important;
    line-height: 1.3 !important;
    background-image: linear-gradient(45deg, transparent 50%, var(--text2) 50%),
                      linear-gradient(135deg, var(--text2) 50%, transparent 50%) !important;
    background-position: calc(100% - 14px) 50%, calc(100% - 9px) 50% !important;
    background-size: 5px 5px, 5px 5px !important;
    background-repeat: no-repeat !important;
    background-color: var(--bg3) !important;
    color: var(--text0) !important;
    border: 1px solid var(--border) !important;
    border-radius: var(--r) !important;
  }
}
.mobile-nav,
.mobile-fab-menu,
.mobile-fab-overlay,
.mobile-more-drawer,
.mobile-more-overlay { display:none; }


/* ── DATE FIELD WITH CALENDAR ICON ── */
/* color-scheme themes the NATIVE date/time picker popover/sheet on every
   modern browser (Chrome, Edge, Safari macOS, iOS Safari 15+, Android Chrome).
   The trick is: it must be set on :root and INHERITED down without per-element
   overrides — otherwise iOS Safari ignores the value and falls back to the
   system theme. Keep it simple: one declaration on :root, one override for
   light mode, no per-input duplication. */
:root{color-scheme:dark}
html.light{color-scheme:light}
/* Explicitly inherit on every container the date/time inputs live in, so the
   computed color-scheme reaches the input element correctly even when modals
   or pages establish a new stacking context. */
body,.page,.modal-bg,.modal-box,.fg,.date-wrap,.auth-field{color-scheme:inherit}
.date-wrap{display:flex;align-items:center;gap:0;position:relative}
input[type="date"]{flex:1;border-radius:var(--r) 0 0 var(--r)}
.date-cal-btn{width:34px;height:100%;min-height:36px;display:flex;align-items:center;justify-content:center;background:var(--bg3);border:1px solid var(--border);border-left:none;border-radius:0 var(--r) var(--r) 0;cursor:pointer;font-size:14px;transition:background .15s;flex-shrink:0}
.date-cal-btn:hover{background:var(--bg4)}


/* task #311: NUCLEAR fix using CSS Grid. Flexbox with min-width:0 keeps
   failing on iOS Safari for task-row text. Grid's minmax(0, 1fr) is the
   ONE truly bulletproof way to allow a column to shrink below intrinsic
   content width. The text divs inside become true block-level children
   that text-overflow:ellipsis works on reliably. */
@media(max-width:700px){
  .task-row{
    display:grid !important;
    grid-template-columns:auto minmax(0,1fr) auto !important;
    align-items:center !important;
    gap:11px !important;
    max-width:100% !important;
    overflow:hidden !important;
  }
  .task-row > span,
  .task-row > .badge,
  .task-row > button{
    min-width:0;
    align-self:center;
  }
  .task-row > div{
    min-width:0 !important;
    max-width:100% !important;
    overflow:hidden !important;
    width:auto !important;
  }
  .task-row > div > div{
    min-width:0 !important;
    max-width:100% !important;
    white-space:nowrap !important;
    overflow:hidden !important;
    text-overflow:ellipsis !important;
    display:block !important;
    word-break:normal !important;
    overflow-wrap:normal !important;
  }
}
/* task #133 (bug 1): hide app shell during initial auth check so we don't show 1s of cached dashboard before the auth screen appears */
body.auth-checking .content,body.auth-checking .sidebar,body.auth-checking .topbar,body.auth-checking #auth-screen,body.auth-checking .mobile-nav{visibility:hidden!important}
body.auth-checking::before{content:'';position:fixed;top:50%;left:50%;width:32px;height:32px;border:3px solid var(--bg3);border-top-color:var(--green);border-radius:50%;animation:gf-loading-spin 0.8s linear infinite;transform:translate(-50%,-50%);z-index:99999}
@keyframes gf-loading-spin{to{transform:translate(-50%,-50%) rotate(360deg)}}

/* === iOS Capacitor WebView fixes === */

/* Bug 1: kill overscroll white flash — match dark theme bg everywhere */
html, body {
  background-color: var(--bg0, #0a0a0a) !important;
  overscroll-behavior: none;
}

/* Bug 2 + 3: respect iOS safe area (status bar + dynamic island + home indicator) */
.topbar, .nav, header, .app-header {
  padding-top: max(env(safe-area-inset-top), 12px) !important;
}
/* .topbar has a fixed height (40px) and box-sizing:border-box, so padding-top
   alone would crush the title/Demo button. Grow the bar height by the inset
   so the content actually clears the status bar / dynamic island. */
.topbar {
  height: calc(40px + max(env(safe-area-inset-top), 12px)) !important;
}
.bottom-nav, .tabbar, .mobile-nav {
  padding-bottom: max(env(safe-area-inset-bottom), 8px) !important;
}

/* Bug 4: disable iOS double-tap zoom gesture across the app */
* { touch-action: manipulation; }

/* Bonus polish for native feel */
* { -webkit-touch-callout: none; }  /* no long-press callout menu */
button, .btn, [role="button"], .topbar, .nav, .tab {
  -webkit-user-select: none;
  user-select: none;
}

/* iOS: when an input is focused and keyboard opens, scroll the input into view */
input:focus, textarea:focus, select:focus {
  scroll-margin-bottom: 280px; /* keyboard height + padding */
}

/* (modal-body keyboard padding removed — Keyboard resize:"native" handles it) */

/* Polished modal X close buttons — match photo viewer aesthetic */
.modal-close,
button.modal-close,
[onclick*="closeModal"][class*="modal"] {
  width:36px !important;
  height:36px !important;
  border-radius:18px !important;
  background:rgba(0,0,0,0.25) !important;
  border:none !important;
  color:var(--text0) !important;
  font-size:22px !important;
  line-height:1 !important;
  display:inline-flex !important;
  align-items:center !important;
  justify-content:center !important;
  cursor:pointer !important;
  padding:0 !important;
  transition:background .15s;
}
.modal-close:hover,
.modal-close:active {
  background:rgba(0,0,0,0.45) !important;
}
html.light .modal-close { background:rgba(0,0,0,0.08) !important; }
html.light .modal-close:hover { background:rgba(0,0,0,0.15) !important; }

/* (body.kb-open keyboard-lift rules removed — Keyboard resize:"native" handles it) */

/* (Round-21 .fg tightening block removed — folded into the @media(max-width:600px)
   authority block below.) */

/* ============================================================================
   MOBILE MODAL — single authority (clean redesign). Placed last so it wins.
   Bottom sheet below the notch; sticky header (X) + sticky footer (Save);
   fields scroll inside .modal-box (there is no .modal-body in this app).
   ============================================================================ */
@media (max-width: 600px) {
  /* Destructive-action modals: internal breathing room (auto-sizes to content
     instead of a forced min-height). */
  #m-delete-account .modal-box,
  #m-reset-data .modal-box {
    padding-top: 8px !important;
    padding-bottom: 8px !important;
  }
  #m-delete-account [id^="del-step-"],
  #m-reset-data [id^="reset-step-"] {
    padding-top: 24px !important;
    padding-bottom: 24px !important;
  }
  #m-delete-account [id^="del-step-"] p,
  #m-delete-account [id^="del-step-"] .body-copy,
  #m-reset-data [id^="reset-step-"] p,
  #m-reset-data [id^="reset-step-"] .body-copy {
    margin-bottom: 28px !important;
  }
  #m-delete-account .modal-footer,
  #m-reset-data .modal-footer {
    padding-top: 16px !important;
    /* keep the home-indicator safe-area + 20px breathing room (these are
       bottom-sheets, so a flat 20px would tuck buttons under the home bar) */
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 20px) !important;
    margin-top: 12px !important;
  }
  .modal-bg {
    align-items: flex-end !important;
    padding-top: env(safe-area-inset-top, 0px) !important;
    padding-bottom: 0 !important;
  }

  .modal-box {
    max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 12px) !important;
    will-change: max-height;
    max-width: 100% !important;
    width: 100% !important;
    border-radius: 16px 16px 0 0 !important;
    padding: 0 16px !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
    margin: 0 !important;
  }

  .modal-header {
    position: sticky !important;
    top: 0 !important;
    background: var(--bg2) !important;
    margin: 0 -16px !important;
    padding: 16px 16px 12px 16px !important;
    z-index: 10 !important;
    border-bottom: 1px solid transparent !important;
  }

  .modal-footer {
    position: sticky !important;
    bottom: 0 !important;
    background: var(--bg2) !important;
    margin: 0 -16px !important;
    padding: 12px 16px calc(env(safe-area-inset-bottom, 0px) + 12px) 16px !important;
    z-index: 10 !important;
    border-top: 1px solid var(--border) !important;
  }

  /* Tighten gaps between form fields so more fits */
  .modal-box .fg {
    margin-bottom: 12px !important;
  }
  .modal-box .fg label {
    margin-bottom: 4px !important;
    font-size: 12px !important;
  }
  .modal-box textarea {
    min-height: 70px !important;
  }
}

/* Local UI prefs — hide top-bar Demo / Help buttons when toggled off in Settings */
body.hide-demo #demo-btn,
body.hide-demo .demo-btn { display: none !important; }
body.hide-help .help-btn,
body.hide-help #help-btn { display: none !important; }

/* Style-only "disabled" for destructive confirm buttons (kept tappable on iOS
   so a premature tap can shake the input + toast instead of silently no-op'ing) */
.btn.is-disabled,
button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Shake + red-border feedback when confirming before the keyword is typed */
@keyframes input-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}
.input-shake {
  animation: input-shake 0.4s ease-in-out;
}
.input-error {
  border-color: var(--danger, #ef4444) !important;
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
}

/* Hide bottom nav + FAB while a text input is focused (keyboard open) */
body.kb-open .mobile-nav { display: none !important; }
body.kb-open .mobile-fab,
body.kb-open .mobile-fab-overlay { display: none !important; }

/* Long-name handling — room-tasks modal (m-room-tasks) */
/* Title: clamp to 2 lines with ellipsis instead of wrapping endlessly */
#m-room-tasks .modal-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
/* Plant name in each task row: single-line ellipsis (parent flex item already has min-width:0) */
.rt-row .rt-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Due badge ("Tomorrow"/"Today"/…): never shrink, never break vertically.
   Overrides the global .badge { word-break:break-all } that shattered it. */
.rt-row .rt-due {
  flex: 0 0 auto;
  white-space: nowrap !important;
  word-break: normal !important;
  overflow-wrap: normal !important;
}

/* ISSUE 1: cap the Settings "Default room" dropdown so a long custom room name
   can't grow the action column and crush the row's title/subtitle to "D."/"U."
   (.settings-row-body already has min-width:0 + the labels already ellipsis). */
#set-default-room {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ISSUE 2: the reusable confirm modal must stack ABOVE whatever modal opened it
   (e.g. Delete from inside the Edit Plant modal). All .modal-bg are z-index:200. */
#m-confirm { z-index: 10000 !important; }

/* ISSUE 3: put the confirm modal's X at top-right like every other modal. The
   others push the X right via a flex:1 title; #m-confirm's <h3> didn't. (We do
   NOT make .modal-close absolute vs .modal-box — that would scroll the X out of
   view on long modals whose header is position:sticky.) */
#m-confirm .modal-header h3 { flex: 1 1 auto; min-width: 0; }

/* task #370: rain banner action buttons — single row on desktop,
   2+1 wrap on mobile so Yes/No stay paired and Remind gets its own row */
.rain-actions {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: nowrap !important;
  gap: 6px;
  width: 100%;
}
.rain-actions > .rain-btn {
  flex: 1 1 0 !important;
  min-width: 0 !important;
  width: auto !important;
  font-size: 12px;
  padding: 8px 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 600px) {
  .rain-actions {
    flex-wrap: wrap !important;
  }
  .rain-actions > .rain-btn {
    font-size: 10.5px;
    padding: 7px 4px;
  }
  .rain-actions > .rain-btn-remind {
    flex: 1 1 100% !important;
    margin-top: 2px;
    font-size: 11px;
    padding: 6px;
  }
}

/* task #378: custom single-select picker (gfEnhanceSelect) — tap-to-expand
   overlay mirroring the #376 supply picker. The native <select> is hidden and
   kept in sync; these classes style the overlay. */
.gf-cs { position: relative; width: 100%; }
.gf-cs-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 11px; background: var(--bg3); border: 1px solid var(--border);
  border-radius: var(--r); cursor: pointer; user-select: none;
  font-size: 12px; color: var(--text0);
}
.gf-cs-trigger.is-open { border-color: var(--green); }
.gf-cs-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gf-cs-label.gf-cs-placeholder { color: var(--text2); }
.gf-cs-arrow { font-size: 10px; color: var(--text2); flex: 0 0 auto; }
/* task #378b: picker bottom-sheet overlay (replaces the inline expand panel) */
.picker-sheet-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.5);
  opacity: 0; transition: opacity .25s ease-out;
  z-index: 10050;   /* above modals (200), m-confirm (10000) and the mobile FAB (10001) */
}
.picker-sheet-backdrop.open { opacity: 1; }
.picker-sheet {
  position: fixed; bottom: 0; left: 0; right: 0;
  background: var(--bg2); border: 1px solid var(--border); border-bottom: none;
  border-radius: 20px 20px 0 0; max-height: 70vh;
  display: flex; flex-direction: column;
  transform: translateY(100%); transition: transform .25s ease-out;
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: 0 -12px 40px rgba(0,0,0,.4);
}
.picker-sheet.open { transform: translateY(0); }
.sheet-handle { width: 36px; height: 4px; background: var(--text2); opacity: .4; border-radius: 2px; margin: 12px auto 8px; flex: 0 0 auto; }
.sheet-title { font-weight: 600; color: var(--text1); padding: 8px 20px 12px; font-size: 15px; flex: 0 0 auto; }
.sheet-options { overflow-y: auto; flex: 1 1 auto; -webkit-overflow-scrolling: touch; }
.sheet-option {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px 20px; min-height: 44px; box-sizing: border-box;
  font-size: 15px; color: var(--text1); border-top: 1px solid var(--border);
  cursor: pointer; user-select: none;
}
.sheet-option:first-child { border-top: none; }
.sheet-option:active { background: var(--bg1); }
.sheet-option.selected { background: var(--greenDim); }
.sheet-option .check { color: var(--green); margin-left: auto; opacity: 0; font-weight: 700; flex: 0 0 auto; }
.sheet-option.selected .check { opacity: 1; }
.sheet-option.is-disabled { opacity: .4; cursor: not-allowed; }
.sheet-done {
  margin: 12px 20px; padding: 14px; background: var(--green); color: #fff;
  border: none; border-radius: 12px; font-size: 15px; font-weight: 600; cursor: pointer;
}
body.picker-sheet-open { overflow: hidden; }

/* task #378c: multi-select supply picker sheet — checkbox rows + sticky Done */
.sheet-option.multi-select .check {
  width: 22px; height: 22px; flex: 0 0 22px;
  border: 2px solid var(--text2); border-radius: 5px;
  background: transparent; opacity: 1; color: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; transition: all .15s;
}
.sheet-option.multi-select.selected .check {
  background: var(--green); border-color: var(--green); color: #fff;
}
.sheet-done { flex: 0 0 auto; margin: 12px 20px; }

/* task #378d (revised): desktop inline-expand panel — opens IN-FLOW below the
   trigger (restores the original pre-#378b desktop picker UX). Mobile keeps the
   bottom sheet (.picker-sheet*, untouched). Reuses .sheet-option / .sheet-done
   so row styling stays single-source. */
/* task #382: desktop inline-expand chip panel for the multi-select SUPPLY pickers
   (fertilizer/soil/treatment/pot). Opens in the form flow below the trigger — May
   29 pattern, plus the subtle border #382 asked for. Single-selects are native on
   desktop (#381b); the bottom sheet (.picker-sheet*) is untouched for mobile.
   Reuses .sheet-option rows; checkmark hidden so selection = green-dim chip. */
.picker-inline {
  margin-top: 4px;
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: var(--r);
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height .2s cubic-bezier(.4,0,.2,1), opacity .1s ease-out, padding .2s;
}
.picker-inline.open { max-height: 260px; overflow-y: auto; opacity: 1; padding: 4px 0; }
.picker-inline .sheet-title { padding: 8px 12px 4px; font-size: 11px; color: var(--text2); }
.picker-inline .sheet-options { overflow: visible; flex: none; }
.picker-inline .sheet-option { padding: 9px 12px; min-height: 0; font-size: 13px; border-top: none; }
.picker-inline .sheet-option .check { display: none; }
.picker-inline .sheet-option:hover { background: var(--bg4); }
.picker-inline .sheet-option.selected { background: var(--greenDim); color: var(--green); font-weight: 500; }
.picker-inline .sheet-done { margin: 6px 10px; padding: 9px; }

/* ── task #383 ── */
/* Bug 1: desktop supply-panel "Done" button — compact + right-aligned, not the
   chunky full-width mobile CTA. (.picker-inline only renders on desktop; the
   media query is belt-and-suspenders. align-self won't work in the block panel,
   so right-align via margin-left:auto on a fit-content button.) */
@media (min-width: 769px) and (hover: hover) and (pointer: fine) {
  .picker-inline .sheet-done {
    width: fit-content;
    margin: 8px 10px 8px auto;
    padding: 6px 12px;
    font-size: 12px;
  }
}
/* Bug 2: Plants header — keep Sort + "+ New plant" on one row on desktop (don't
   let the sort control go full-width and push the button below). Native <select>
   (post-#381b) flexes to natural width; .gf-cs override is defensive in case the
   enhanced trigger ever renders here. Mobile header rules are left untouched. */
@media (min-width: 769px) and (hover: hover) and (pointer: fine) {
  #page-plants > div[style*="justify-content:space-between"] > div:last-child {
    flex-wrap: nowrap;
    min-width: 0;
  }
  #page-plants #plant-sort,
  #page-plants > div[style*="justify-content:space-between"] .gf-cs {
    flex: 0 1 auto;
    min-width: 0;
    width: auto;
  }
}

/* task #383b — Plants header on MOBILE: count + Sort + "+ New plant" on ONE row.
   The earlier @media(max-width:768px) block put the count on its own row and left
   the .gf-cs enhanced sort trigger at width:100%, stacking the button below. The
   bottom-sheet picker still opens on tap — only the trigger's inline layout changes.
   (Desktop keeps its own #383 rule: native <select> stays content-width to avoid
   collapsing in the auto-width space-between row.) */
@media (max-width: 768px) {
  #page-plants > div[style*="justify-content:space-between"] > div:last-child {
    flex-wrap: nowrap !important;
  }
  #page-plants > div[style*="justify-content:space-between"] #plant-count-label {
    flex: 0 0 auto !important;
  }
  #page-plants #plant-sort,
  #page-plants > div[style*="justify-content:space-between"] .gf-cs {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    width: auto !important;
  }
}

/* task #384: Add/Edit Plant modal on mobile — stack the two-column field pairs
   (💧 Water every / Last watered, 🌱 Fertilize every / Last fertilized, etc.)
   into a single column. On phones the longer left label wraps to 2 lines while
   the right is 1 line, pushing the inputs onto different baselines. Stacking
   gives each field full width and clean alignment. Desktop keeps 2-col.
   Scoped to #m-plant so the other 8 .row2 uses are untouched. */
@media (max-width: 600px) {
  #m-plant .row2 { grid-template-columns: 1fr; gap: 8px; }
}

/* task #385: Calendar filters ("All plants" / "All rooms") side-by-side on mobile.
   #378 enhanced them (data-gf-cs) so the visible trigger is a .gf-cs wrapper at
   width:100% — which wrapped the two onto separate rows. The existing rule (line
   ~295) only sized the now-hidden native <select>. Make the .gf-cs triggers share
   the filter row 50/50. ≤700px = where the calendar header stacks to column and
   the filter group is full-width (so flex:1 1 0 fills rather than collapses).
   Desktop is native <select> (gfEnhanceSelect bypassed) → already side-by-side. */
@media (max-width: 700px) {
  #page-calendar > .flex.aic.jsb > .flex:first-child > .gf-cs {
    flex: 1 1 0;
    min-width: 0;
    width: auto;
  }
}
