:root{
    --soil:#4a3826; --fir:#27432e; --leaf:#3f7d45; --sprout:#7fb069;
    --sun:#e8b04b; --mist:#f0f6ea; --glass:#fffefb; --line:#d8e6cd;
    --blush:#e9a17c;
  }
  *{box-sizing:border-box;margin:0}
  body{
    background:
      radial-gradient(circle at 8% 4%, #e2efd4 0 110px, transparent 130px),
      radial-gradient(circle at 94% 90%, #e6f0db 0 140px, transparent 160px),
      var(--mist);
    color:var(--fir);
    font:16px/1.5 -apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    padding:20px;max-width:680px;margin:0 auto;
  }
  /* wrap rather than crush: logged out, the header carries the title, a
     read-only badge, a password field, a button and the clock, which do not
     fit one phone-width row. Without wrapping, flex shrinks the h1 (breaking
     "OpenSeedling" under the sprout) and squeezes the badge and button into
     two-line text. */
  header{display:flex;align-items:baseline;justify-content:space-between;
    flex-wrap:wrap;row-gap:8px;
    padding-bottom:10px;margin-bottom:6px}
  header h1{flex:0 0 auto;white-space:nowrap}
  h1{font-family:Georgia,'Times New Roman',serif;font-weight:600;font-size:27px}
  #clock{font-variant-numeric:tabular-nums;font-size:15px;color:var(--leaf)}
  .vine{height:14px;margin-bottom:14px;background:
    radial-gradient(circle 5px at 10px 7px, var(--sprout) 4px, transparent 5px) repeat-x;
    background-size:26px 14px;border-radius:7px;opacity:.7}
  .card{background:var(--glass);border:1.5px solid var(--line);border-radius:18px;
    padding:18px;margin-bottom:16px;box-shadow:0 2px 0 var(--line)}
  .phase{display:flex;align-items:center;gap:14px}
  /* soft burst-ray sun; size/brightness track --glow (0 night .. 1 full sun) */
  .sun{width:64px;height:64px;flex:none;
    opacity:calc(.45 + var(--glow,0)*.55);
    transform:scale(calc(.82 + var(--glow,0)*.22));
    filter:drop-shadow(0 0 calc(var(--glow,0)*9px) rgba(252,212,95,.6));
    transition:opacity .8s,transform .8s,filter .8s}
  .sunsvg{width:100%;height:100%;display:block;overflow:visible}
  .sundisk{fill:#fcd45f}
  .face circle{fill:#2b2b2b}
  .face path{stroke:#e0915a}
  .raygroup{fill:#fbe7a1;opacity:calc(.4 + var(--glow,0)*.6);transition:opacity .8s}
  /* per-stage graphics (night moon, sunrise, sunset) */
  .moonsvg,.risesvg,.setsvg{width:100%;height:100%;display:block;overflow:visible}
  .sun.is-night{opacity:.92;transform:scale(1);
    filter:drop-shadow(0 0 5px rgba(210,220,180,.45))}
  .moondark{fill:#5b5f49}
  .moonlit{fill:#ecd9a0}
  .star{fill:var(--sun)}
  .redisk{fill:#f4c44e}
  .sedisk{fill:#ea8d5b}
  .rays line{stroke-width:3;stroke-linecap:round}
  .rerays line{stroke:#f6cf78}
  .serays line{stroke:#eaa06a}
  .reface circle,.seface circle{fill:#2b2b2b}
  .reface path{stroke:#e0915a}
  .seface path{stroke:#b5663b}
  .horizon{stroke:#c2a978;stroke-width:3;stroke-linecap:round}
  .phase b{font-size:20px;display:block}
  .phase span{color:var(--leaf);font-size:14px}
  .pct{margin-left:auto;text-align:right}
  .pct b{font-size:32px;font-family:Georgia,serif;font-weight:600}
  svg{width:100%;height:auto;display:block}
  .grid{display:grid;grid-template-columns:1fr 1fr;gap:10px 18px;font-size:14px}
  .grid dt{color:var(--leaf)}
  .grid dd{font-variant-numeric:tabular-nums;font-weight:600}
  h2{font-family:Georgia,serif;font-size:18px;font-weight:600;margin-bottom:12px}
  summary{cursor:pointer;list-style:none}
  summary::-webkit-details-marker{display:none}
  summary h2{display:inline;margin:0}
  summary::after{content:" \25BE";color:var(--leaf);font-size:15px}
  details[open] summary::after{content:" \25B4"}
  details[open] summary{display:block;margin-bottom:12px}
  #photo,#vframe{width:100%;border-radius:12px;border:1.5px solid var(--line);
    display:block;background:#dfe9d4;min-height:160px}
  .imgwrap{position:relative;line-height:0}
  #gridsvg{position:absolute;inset:0;width:100%;height:100%;touch-action:none;cursor:crosshair}
  #guidesvg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
  #guidesvg line{stroke:rgba(255,255,255,.8);stroke-width:1.5;vector-effect:non-scaling-stroke}
  #alignbtn.on{background:#b23b3b}
  .gh{fill:#e8b04b;stroke:#27432e;stroke-width:3}
  .gc{cursor:pointer}
  .glbl{fill:#fff;font:700 26px Georgia,serif;paint-order:stroke;stroke:#1d3324;stroke-width:4px}
  .gnm{fill:#eafff0;font:600 19px sans-serif;paint-order:stroke;stroke:#1d3324;stroke-width:3px}
  .gridctl{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin-top:10px;
    font-size:13px;color:var(--leaf)}
  .gridctl button{margin-top:0;padding:6px 14px}
  .gridctl input[type=number]{width:54px}
  .gridctl input[type=checkbox]{width:auto}
  #photoinfo{font-size:13px;color:var(--leaf);margin-top:8px}
  .capctl{display:flex;align-items:center;gap:12px;margin-top:10px}  .capctl button{margin-top:0;padding:6px 14px}
  .capctl .muted{font-size:13px;color:var(--leaf)}
  .pctrl{display:flex;align-items:center;gap:12px;margin-top:12px}
  .pctrl button{margin-top:0;padding:8px 16px;white-space:nowrap}
  .pctrl input[type=range]{flex:1;width:auto;margin:0;padding:0;border:0;
    background:transparent;accent-color:var(--leaf)}
  #pframe{font-size:13px;color:var(--leaf);min-width:108px;text-align:right;
    font-variant-numeric:tabular-nums}
  form .frow{display:grid;grid-template-columns:1fr 1fr;gap:10px 14px;margin-bottom:10px}
  label{font-size:13px;color:var(--leaf);display:block}
  input,select{width:100%;padding:8px 10px;margin-top:3px;font-size:15px;color:var(--fir);
    background:#fff;border:1.5px solid var(--line);border-radius:10px}
  input:focus,select:focus{outline:2px solid var(--sprout);border-color:var(--sprout)}
  input[type=checkbox]{width:auto;accent-color:var(--leaf);transform:scale(1.3)}
  .checkrow{display:flex;align-items:center;gap:10px;margin-top:18px;
    font-size:14px;color:var(--fir)}
  .tzrow{grid-column:1 / -1}
  button{margin-top:6px;padding:10px 22px;font-size:15px;font-weight:600;
    color:#fff;background:var(--leaf);border:0;border-radius:12px;cursor:pointer;
    box-shadow:0 2px 0 var(--fir)}
  button:hover{background:#346a3b}
  button:focus-visible{outline:3px solid var(--sun)}
  #msg{margin-left:12px;font-size:14px}
  #msg.ok{color:var(--leaf)} #msg.err{color:#a4452c}
  footer{text-align:center;color:var(--leaf);font-size:12px;margin-top:6px}
  @media (prefers-reduced-motion:no-preference){
    .nowdot{animation:pulse 2.4s ease-in-out infinite}
    @keyframes pulse{50%{opacity:.45}}
  }
  @media (max-width:480px){form .frow{grid-template-columns:1fr}}
  @media (min-width:1100px){
    body{max-width:1150px;padding:16px 24px}
    header{padding-bottom:6px;margin-bottom:4px}
    .vine{margin-bottom:12px}
    .glayout{display:grid;grid-template-columns:0.7fr 1.6fr;gap:14px;
      grid-template-areas:
        "left media"
        "sens sens"
        "tray tray"
        "set  set";
      align-items:start}
    .glayout .card{margin-bottom:0}
    /* left column: cards packed to the top, no stretching into empty space */
    .aleft{grid-area:left;display:flex;flex-direction:column;gap:14px;align-self:start}
    .amedia{grid-area:media;display:flex;flex-direction:column;gap:14px;align-self:start}
    .atrays{grid-area:tray}
    .ahost{grid-area:host}
    .asensors{grid-area:sens}
    .aset{grid-area:set}
    /* report card fills to the media column's bottom; body scrolls for overflow */
    #reportcard{display:flex;flex-direction:column;overflow:hidden}
    #reportcard>h2,#reportcard>.reportctl{flex:0 0 auto}
    #reportbody{flex:1 1 auto;min-height:0;overflow-y:auto;padding-right:6px}
    #reportbody::-webkit-scrollbar{width:8px}
    #reportbody::-webkit-scrollbar-thumb{background:var(--line);border-radius:8px}
    #photo,#vframe{max-height:64vh;object-fit:contain}
  }

/* ---- sensors card ---- */
.sreadout{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:14px}
.sgroup{flex:1 1 200px;min-width:170px}
.sgroup h3{margin:0 0 6px;font-size:13px;color:var(--leaf);font-weight:700;
  text-transform:uppercase;letter-spacing:.04em}
.schip{display:inline-flex;align-items:baseline;gap:5px;background:#f1f6ea;
  border:1px solid var(--line);border-radius:8px;padding:5px 9px;margin:0 6px 6px 0;
  font-size:13px}
.schip b{font-size:15px;color:var(--ink)}
.schip .u{color:var(--leaf);font-size:12px}
.chartctl{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:8px}
.chartctl select{width:auto;min-width:160px;margin-top:0}
#chartinfo{font-size:12px;color:var(--leaf)}
.ranges button{margin:0 0 0 4px;padding:5px 12px;background:#eef4e6;color:var(--leaf);
  border:1px solid var(--line)}
.ranges button.on{background:var(--leaf);color:#fff;border-color:var(--leaf)}
#chartinfo{font-size:12px;color:var(--leaf)}
.gmoist{fill:#1d3324;font:700 17px sans-serif;paint-order:stroke;
  stroke:#eafff0;stroke-width:3px}

.waterctl{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-bottom:14px;
  padding-top:10px;border-top:1px solid var(--line)}
.waterctl button{margin:0;padding:6px 14px}
.waterctl input[type=number]{width:54px}
#pumpinfo{font-size:12px;color:var(--leaf)}

/* ---- auth / read-only ---- */
.hright{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  justify-content:flex-end;margin-left:auto}
#authbox{display:flex;align-items:center;gap:6px;font-size:13px;flex-wrap:wrap}
#authbox input[type=password]{width:120px;margin:0;padding:5px 8px}
#authbox button{margin:0;padding:5px 12px;white-space:nowrap}
.robadge{background:#e9eef3;color:#5a6b7a;border:1px solid #c9d4dd;border-radius:8px;
  padding:3px 9px;font-size:12px;white-space:nowrap}
@media (max-width:560px){
  /* phone, logged out: give the login row the full width under the title
     instead of competing with it for one line */
  .hright{flex:1 1 100%;justify-content:space-between}
  #authbox{flex:1 1 auto}
  #authbox input[type=password]{flex:1 1 auto;width:auto;min-width:0}
}
#loginerr{color:#b23b3b;font-size:12px}
/* AI garden report */
.reportctl{display:flex;align-items:center;flex-wrap:wrap;gap:14px;margin-bottom:14px}
.reportctl button{margin-top:0}
.reportctl label{display:inline-flex;align-items:center;gap:6px;margin:0;white-space:nowrap}
.aihrwrap input{width:auto;margin-top:0;padding:7px 9px}
#reportinfo{font-size:13px;color:var(--leaf)}
#reportbody p{margin:0 0 8px}
.rhead{display:flex;align-items:center;gap:10px;margin-bottom:8px}
.rbadge{font-size:12px;font-weight:700;padding:3px 11px;border-radius:999px;letter-spacing:.02em}
.rbg-good{background:#e3f0d8;color:#2f6b34}
.rbg-watch{background:#fbeccb;color:#9a6b1e}
.rbg-problem{background:#f6dcd0;color:#b1492c}
.rtime{font-size:12px;color:var(--leaf)}
.rsummary{font-size:16px;color:var(--fir);line-height:1.45}
.rfacts{font-size:13px;color:var(--leaf)}
.rreason{font-size:13.5px;color:var(--fir)}
.rsec{margin-top:10px;padding-top:10px;border-top:1px solid var(--line)}
.rsec h4{font-family:Georgia,serif;font-size:14px;font-weight:600;color:var(--fir);margin-bottom:5px}
.rsec ul{margin:0;padding-left:18px}
.rsec li{font-size:13.5px;color:var(--fir);margin-bottom:3px;line-height:1.4}
.rmuted{font-size:13.5px;color:var(--leaf)}
.rmuted code{background:#eef4e6;padding:1px 5px;border-radius:5px;font-size:12.5px}
.rconf{font-size:12px;color:var(--leaf);margin-top:10px}
body.readonly .editonly{display:none !important}

/* ---- gentle plant animations (subtle by design) ---- */
@keyframes sway   {0%,100%{transform:rotate(-7deg)}50%{transform:rotate(7deg)}}
@keyframes breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.05)}}
.sprout{display:inline-block;transform-origin:50% 90%;
  animation:sway 4.5s ease-in-out infinite}

@media (prefers-reduced-motion: reduce){
  .sprout,.sunsvg,.raygroup{animation:none !important}
}

/* sun dances: gentle wiggle + bob; rays turn slowly */
@keyframes sundance{0%,100%{transform:rotate(-6deg) translateY(0)}
  50%{transform:rotate(6deg) translateY(-2px)}}
@keyframes rayspin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
.sunsvg{transform-box:fill-box;transform-origin:center;
  animation:sundance 3.8s ease-in-out infinite}
.raygroup{transform-box:fill-box;transform-origin:center;
  animation:rayspin 28s linear infinite}

/* grid lock: hide geometry editing controls while locked */
body.gridlocked .gridedit{display:none}

/* per-cell canopy growth label on the snapshot overlay */
.ggrow{fill:#2e7d32;font:700 17px sans-serif;paint-order:stroke;
  stroke:#eafff0;stroke-width:3px}
.gdry{fill:#8d6e63;font:700 16px sans-serif;paint-order:stroke;
  stroke:#eafff0;stroke-width:3px}
.drycal{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:8px}
.drycal #calinfo{color:#7a8a72;font-size:.9em}
#charttip{position:fixed;display:none;pointer-events:none;z-index:50;
  background:#2f4030;color:#eafff0;font-size:12px;padding:4px 8px;border-radius:6px;
  white-space:nowrap;box-shadow:0 2px 8px rgba(0,0,0,.25)}

/* manual light override */
.lightctl{display:flex;flex-direction:column;gap:8px;font-size:13px}
.lightctl .lcrow{display:flex;align-items:center;gap:8px}
.lightctl .lclabel{font-weight:600;color:var(--leaf)}
.lightctl button.lcbtn{margin-top:0;padding:5px 14px;font-size:13px;
  background:#eef4e6;color:var(--leaf);
  border:1.5px solid transparent}          /* border always present, so the
                                              selected state can't resize it */
.lightctl button.lcbtn.on{background:var(--leaf);color:#fff;
  border-color:var(--leaf)}
.lightctl .lcslider{display:flex;align-items:center;gap:10px;width:100%}
.lightctl .lcslider input[type=range]{flex:1;accent-color:var(--leaf);margin:0;
  min-width:0}
.lightctl .lcslider b{width:46px;flex:none;text-align:right;color:var(--leaf);
  font-size:13px;font-variant-numeric:tabular-nums}  /* fixed cell + even digit
                                                        widths: no jitter */
.lightctl .lcslider.dim{opacity:.4}
.lightctl .lcslider.dim input[type=range]{cursor:not-allowed}
.lightctl #lightinfo{color:var(--leaf);font-size:12px;
  min-height:15px;line-height:15px}        /* space reserved whether or not the
                                              hold message is showing */

/* ---- chart grid: all sensors at once ---- */
.csection{margin-top:14px}
.csection h3{margin:0 0 8px;font-size:13px;font-weight:700;color:var(--leaf);
  text-transform:uppercase;letter-spacing:.04em}
.cgrid{display:grid;gap:10px;
  /* auto-fit, not auto-fill: with few charts in a group, empty tracks
     collapse and the real charts stretch across the row */
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.ccard{background:#f6faf1;border:1px solid var(--line);border-radius:10px;
  padding:8px 10px 4px}
.chead{display:flex;justify-content:space-between;align-items:baseline;gap:8px;
  font-size:12px;font-weight:600;color:var(--ink);margin-bottom:4px}
.chead .cunit{font-weight:400;color:var(--leaf);margin-left:3px;font-size:11px}
.chead .cstats{font-weight:400;color:var(--leaf);font-size:11px;white-space:nowrap;
  font-variant-numeric:tabular-nums}
.chead .cstats b{color:var(--ink)}
svg.cmini{width:100%;height:96px;display:block;cursor:crosshair}
@media (max-width:700px){
  .cgrid{grid-template-columns:1fr}
  svg.cmini{height:110px}
}

/* ---- planting map ---- */
.tray{margin-top:10px}
.tray-head{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.tray-head b{font-size:14px;color:var(--leaf)}
.tray-head .tsum{font-size:12px;color:var(--leaf);font-weight:400}
.tgrid{display:grid;gap:6px}
.tcell{background:#f6faf1;border:1px solid var(--line);border-radius:8px;
  padding:6px 7px;display:flex;flex-direction:column;gap:4px;min-height:78px}
.tcell.filled{background:#eef6e6;border-color:#cdddc0}
.tcell .tid{font-size:10px;font-weight:700;color:var(--leaf);letter-spacing:.04em;
  display:flex;justify-content:space-between;align-items:center}
.tcell .tage{font-weight:400;font-size:10px;color:#7a8a72}
.tcell input{width:100%;margin:0;padding:3px 5px;font-size:12px;
  border:1px solid var(--line);border-radius:5px;background:#fff}
.tcell input.tseed{font-weight:600}
.tcell input.tequip{font-style:italic}
.tcell input.tdate{font-size:11px;color:var(--leaf)}
body.readonly .tcell input{background:#f6faf1;border-color:transparent;
  pointer-events:none}
.trayctl{margin-top:10px;font-size:12px;color:var(--leaf);min-height:16px}
@media (max-width:700px){ .tcell{min-height:auto} }
.chead .cstats b.hot{color:#b5322f}

/* =================== production polish layer =================== */
/* interactive states: one consistent language for every control */
button{transition:background-color .15s ease,color .15s ease,
  border-color .15s ease,transform .06s ease,box-shadow .15s ease}
button:hover:not(:disabled){filter:brightness(1.05)}
button:active:not(:disabled){transform:translateY(1px)}
button:disabled{opacity:.55;cursor:not-allowed}
input,select{transition:border-color .15s ease,box-shadow .15s ease}
input:focus,select:focus{border-color:var(--leaf);outline:none;
  box-shadow:0 0 0 3px rgba(74,124,89,.15)}
/* keyboard focus is always visible, without doubling up on mouse clicks */
:focus-visible{outline:2px solid var(--leaf);outline-offset:2px}
button:focus:not(:focus-visible){outline:none}

/* cards: subtle lift on the interactive ones only */
.ccard,.tcell{transition:border-color .15s ease,box-shadow .15s ease}
.ccard:hover{border-color:#cdddc0;box-shadow:0 1px 4px rgba(46,64,42,.06)}
.tcell:focus-within{border-color:var(--leaf);
  box-shadow:0 0 0 3px rgba(74,124,89,.12)}

/* status/info lines: consistent size, never cause reflow */
#captureinfo,#probecalinfo,#probetcinfo,#trayinfo,#calinfo,#chartinfo{
  min-height:15px;display:inline-block}

/* report body scrollbar: subtle, matches the palette */
#reportbody{scrollbar-width:thin;scrollbar-color:#cdddc0 transparent}
#reportbody::-webkit-scrollbar{width:8px}
#reportbody::-webkit-scrollbar-thumb{background:#cdddc0;border-radius:4px}

/* small screens: control rows wrap instead of overflowing */
@media (max-width:700px){
  .capctl,.gridctl,.drycal,.chartctl,.lcrow{flex-wrap:wrap}
  .tgrid{gap:5px}
  .tcell{padding:5px 6px}
  button{padding:8px 14px}   /* comfortable touch targets */
}

.chartctl-bottom{margin:12px 0 0;justify-content:center}
.calwrap{margin-top:12px;border-top:1px solid var(--line);padding-top:10px}
.calwrap summary{cursor:pointer;font-size:13px;font-weight:600;color:var(--leaf);
  list-style-position:inside;user-select:none}
.calwrap summary:hover{filter:brightness(1.1)}
.calwrap .drycal{margin-top:10px}

/* =================== flourish layer =================== */
/* moisture chips carry their own fill level as a background bar */
.schip[data-fill]{position:relative;overflow:hidden;isolation:isolate}
.schip[data-fill]::before{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(90deg,rgba(120,170,110,.28) var(--fill,0%),transparent var(--fill,0%));
  transition:background .6s ease}

/* the light card glows with the actual brightness (--lum set from status) */
.aphase{--lum:0;box-shadow:0 0 calc(34px * var(--lum)) calc(2px * var(--lum))
  rgba(246,196,78,calc(.55 * var(--lum)));
  transition:box-shadow 1.2s ease}

/* chart lines draw themselves in when (re)rendered */
@keyframes drawline{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
polyline.cline{stroke-dasharray:1;stroke-dashoffset:0;
  animation:drawline .9s ease-out}

/* cards ease in on first paint, gently staggered down the page */
@keyframes cardin{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.glayout .card{animation:cardin .45s ease backwards}
.glayout .card:nth-child(2){animation-delay:.05s}
.glayout .card:nth-child(3){animation-delay:.1s}
.aleft .card:nth-child(2){animation-delay:.07s}
.aleft .card:nth-child(3){animation-delay:.14s}
.amedia .card:nth-child(2){animation-delay:.1s}
.asensors{animation-delay:.18s}
.atrays{animation-delay:.22s}

/* section headers grow a vine underline on hover */
.card h2{position:relative;display:inline-block}
.card h2::after{content:"";position:absolute;left:0;bottom:-3px;height:2px;width:0;
  background:linear-gradient(90deg,var(--leaf),#9db648);border-radius:2px;
  transition:width .35s ease}
.card:hover h2::after{width:100%}

/* motion off for those who ask for it */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition:none !important;animation:none !important}
}

/* ---- camera / data health ---- */
.camwarn{border-radius:9px;padding:8px 12px;font-size:13px;margin-bottom:10px;line-height:1.45}
.camwarn.err{background:#fdeceb;border:1px solid #e8b6b3;color:#8a2d2a}
.camwarn.amber{background:#fdf3d6;border:1px solid #e8c96a;color:#7a5a13}
.camwarn .camerr{font-family:ui-monospace,monospace;font-size:11px;opacity:.85}
#photo.camdead{filter:grayscale(.7) brightness(.92)}
.schip.stale{opacity:.55}
.schip.stale b::after{content:" \26A0";font-size:10px}
.stalebadge{background:#fdf3d6;border:1px solid #e8c96a;color:#7a5a13;
  border-radius:4px;padding:0 5px;font-size:10px;font-weight:600;vertical-align:1px}
svg.cmini.cstale polyline.cline{stroke:#a8b5a0;opacity:.75}
.waterctl .wtray{font-weight:700;font-size:13px;color:var(--leaf);min-width:52px}
#waterctl{display:flex;flex-direction:column;gap:8px}

.ptrend{font-size:10px;font-weight:600;margin-left:5px;padding:0 5px;border-radius:4px;
  background:#eef4e6;color:var(--leaf);white-space:nowrap}
.ptrend.down{background:#fdeceb;color:#8a2d2a}
.ptrend.up{background:#eaf5e4;color:#2e6b32}
.schip.dli.ok b{color:#2e6b32}
.schip.dli.low b{color:#8a6a13}
.schip.dli.high b{color:#8a2d2a}

/* ---- settings: grouped sections ---- */
.fgroup{margin-bottom:18px;padding-bottom:14px;border-bottom:1px solid var(--line)}
.fgroup:last-of-type{border-bottom:0;padding-bottom:4px}
.fgroup>h3{font-size:12px;font-weight:700;color:var(--leaf);text-transform:uppercase;
  letter-spacing:.05em;margin:0 0 4px}
.fhint{font-size:12px;color:#7a8a72;margin:0 0 10px;line-height:1.45}
.fgroup .frow{margin-bottom:0}
.fgroup .checkrow{margin-top:6px}
.fgroup .checkrow label{font-size:14px;color:var(--fir)}
.tzrow .fhint{margin-top:4px}

/* ---- empty states ---- */
.emptystate{background:#f6faf1;border:1px dashed var(--line);border-radius:10px;
  padding:14px 16px;color:var(--leaf);font-size:13px;line-height:1.5}
.emptystate b{display:block;color:var(--fir);font-size:14px;margin-bottom:4px}
.emptystate p{margin:0}
.emptystate code{background:#eef4e6;padding:1px 5px;border-radius:4px;font-size:12.5px}

/* ---- density: the section you act on gets more room ---- */
.csection.primary .chead{font-size:13px}
@media (max-width:700px){
  .csection.primary .cgrid,.csection:not(.primary) .cgrid{grid-template-columns:1fr}
}

/* left column pulls its weight on wide screens: light + water read as one panel */
@media (min-width:1100px){
  .aleft{gap:12px}
  .aleft .card{padding:16px}
  .aphase .phase{gap:12px}
}


/* ================= top strip: status, control, curve, stats =================
   One row of live state above everything else, on every screen and whether or
   not the camera is on. The media column (report/photo/timelapse) sits below. */
body.nocam .amedia{display:none}
@media (min-width:1100px){
  .glayout{
    grid-template-columns:1fr;
    grid-template-areas:
      "left"
      "media"
      "sens"
      "tray"
      "host"
      "set";
  }
  .aleft{flex-direction:row;flex-wrap:wrap;align-items:stretch;gap:12px}
  .aleft>.card{margin-bottom:0}
  .aleft>.aphase{flex:1 1 280px}
  .aleft>.lightctl{flex:1 1 260px}
  .aleft>.achart{flex:1 1 380px}
  .aleft>.afacts{flex:1 1 100%}
  .achart svg{max-height:150px}

  /* facts read as a stat strip rather than a two-column list */
  .afacts .grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0}
  .afacts .grid dd{grid-row:1;font-size:19px;font-family:Georgia,serif}
  .afacts .grid dt{grid-row:2;font-size:12px}
  .afacts .grid dt,.afacts .grid dd{padding:0 14px;border-left:1px solid var(--line)}
  .afacts .grid dt:first-of-type,.afacts .grid dd:first-of-type{border-left:0;padding-left:0}

  /* media row: report beside the photo, timelapse below. The report spans
     both rows and STRETCHES to the column bottom: a short report otherwise
     leaves a dead hole under its card while snapshot+timelapse run long.
     The card is a flex column with a scrolling body, so stretching only
     adds breathing room, never distortion. */
  .amedia{display:grid;gap:14px;
    grid-template-columns:1.1fr 1fr;
    align-items:start}
  .amedia>.areport{grid-row:span 2;align-self:stretch}
  #reportcard{max-height:none;height:100%}
  #photo,#vframe{max-height:52vh;object-fit:contain}
}

/* ---- light response curve ---- */
.lcurve{margin-top:12px;padding-top:10px;border-top:1px solid var(--line)}
.lcurvehead{display:flex;align-items:center;gap:10px;font-size:12px;
  font-weight:600;color:var(--leaf);margin-bottom:6px}
.lcurvehead button{margin:0;padding:4px 12px;font-size:12px;
  background:#eef4e6;color:var(--leaf);border:1px solid var(--line)}
.lcurvehead #sweepinfo{font-weight:400;font-size:11px;color:#7a8a72;margin-left:auto;
  text-align:right}
#lcurve{width:100%;height:118px;display:block;background:#f6faf1;
  border:1px solid var(--line);border-radius:8px}

/* ---- day progress inside the phase card ---- */
.aphase{display:block}
.phaserow{display:flex;align-items:center;gap:14px}
.dayprog{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.dpbar{position:relative;height:8px;border-radius:5px;background:#eef4e6;
  border:1px solid var(--line);overflow:visible}
.dpbar span{display:block;height:100%;border-radius:5px;
  background:linear-gradient(90deg,#cfe3b8,var(--sun));transition:width .6s ease}
.dpbar i{position:absolute;top:-3px;width:2px;height:14px;background:var(--fir);
  border-radius:1px;transform:translateX(-1px);transition:left .6s ease}
.dprow{display:flex;justify-content:space-between;align-items:baseline;gap:10px;
  margin-top:7px;font-size:12px;color:var(--leaf)}
.dprow>span{white-space:nowrap}
.dprow b{color:var(--fir);font-size:13px}

/* ---- per-cell germination state ---- */
.tcell.sprouted{background:#e8f5e0;border-color:#a8d08a}
.tcell.archived{background:#f2f2ee;border-color:#dcdcd4;opacity:.72}
.tcell.archived input{color:#8a8a80}
.tstat{font-size:10px;color:var(--leaf);line-height:1.3;min-height:13px}
.tcell.sprouted .tstat{color:#2e6b32;font-weight:600}
.tcell.archived .tstat{color:#7a7a72}
.tacts{display:flex;gap:4px;margin-top:2px}
.tacts button{margin:0;flex:1;padding:3px 4px;font-size:10px;font-weight:600;
  background:#eef4e6;color:var(--leaf);border:1px solid var(--line);border-radius:5px;
  box-shadow:none}
.tacts button:hover{background:#e2eed6}
.tcell.sprouted .tage{color:#2e6b32;font-weight:700}
.dlipacemark{position:absolute;top:-3px;width:2px;height:14px;background:var(--fir);
  border-radius:1px;transform:translateX(-1px);transition:left .6s ease;z-index:2}
.dlibar{overflow:visible}
.dlipace{font-size:11px;color:#7a8a72;white-space:nowrap}
.dlipace b{font-size:12px}
.dlipace.ok{color:#2e6b32}
.dlipace.low{color:#8a6a13}
.dlipace.high{color:#8a2d2a}

/* ---- light plan under the day curve ---- */
.lplan{margin-top:10px;padding-top:10px;border-top:1px solid var(--line);font-size:12.5px}
.lphead{display:flex;align-items:center;gap:7px;margin-bottom:5px}
.lphead b{font-size:13px;color:var(--fir)}
.lpdot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--leaf)}
.lpdot.low{background:#e0a13a}
.lpdot.high{background:#b5322f}
.lpdot.ok{background:#4a7c59}
.lplan ul{margin:0;padding-left:16px;color:var(--leaf);line-height:1.5}
.lplan li{margin-bottom:2px}
.modeblock{margin-top:4px}
.modeblock .fhint{margin-bottom:8px}

/* ---- draggable schedule handles on the day curve ---- */
.schandle{touch-action:none}
.schandle rect{transition:opacity .15s ease}
.schandle:hover rect{opacity:.85}
#chart .schandle text{pointer-events:none;user-select:none}

/* ---- bigger charts, expandable ---- */
.cgrid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
svg.cmini{height:120px}
.csection.primary .cgrid{grid-template-columns:repeat(auto-fill,minmax(340px,1fr))}
.csection.primary svg.cmini{height:160px}
.csection:not(.primary) .cgrid{grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}
.csection:not(.primary) svg.cmini{height:120px}

.cexpand{margin:0 0 0 6px;padding:1px 5px;font-size:12px;line-height:1;
  background:transparent;color:var(--leaf);border:1px solid var(--line);
  border-radius:5px;box-shadow:none;flex:none}
.cexpand:hover{background:#eef4e6}
.ccard.expanded{grid-column:1/-1}
.ccard.expanded svg.cmini{height:300px}
.csection.primary .ccard.expanded svg.cmini{height:300px}
.chead{align-items:center}
@media (max-width:700px){
  svg.cmini,.csection.primary svg.cmini,.csection:not(.primary) svg.cmini{height:150px}
  /* expanding on a phone is only worth it if it actually gains height */
  .ccard.expanded svg.cmini{height:62vh;max-height:520px}
}

/* ---- device stats ---- */
.hgrid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}
.htile{background:#f6faf1;border:1px solid var(--line);border-radius:10px;padding:9px 11px}
.hlabel{font-size:10px;font-weight:700;color:var(--leaf);text-transform:uppercase;
  letter-spacing:.06em;margin-bottom:3px}
.hvalue{font-size:19px;font-weight:600;color:var(--ink);line-height:1.15;
  font-variant-numeric:tabular-nums;word-break:break-word}
.hvalue.good{color:#2e6b32}
.hvalue.warn{color:#8a6a13}
.hvalue.bad{color:#b5322f}
.hsub{font-size:11px;color:var(--leaf);margin-top:2px}
.tcell input.tsprdate{color:#2e6b32;border-color:#a8d08a;background:#f4fbef}
.tdrow{display:flex;align-items:center;gap:5px;margin:0}
.tdlbl{font-size:9px;font-weight:700;color:var(--leaf);text-transform:uppercase;
  letter-spacing:.05em;width:30px;flex:none}
.tdlbl.up{color:#2e6b32}
.tdrow .tdate{flex:1;min-width:0}

/* ---- per-cell field menu ---- */
.tcell{position:relative}
.tcell .tid{gap:6px}
.tfields{margin:0;padding:0 3px;font-size:12px;line-height:1;background:transparent;
  color:var(--leaf);border:0;box-shadow:none;cursor:pointer;opacity:.55}
.tcell:hover .tfields,.tfields:focus-visible{opacity:1}
.tmenu{position:absolute;top:20px;right:6px;z-index:20;background:#fff;
  border:1px solid var(--line);border-radius:8px;padding:6px 8px;
  box-shadow:0 4px 14px rgba(46,64,42,.16);display:flex;flex-direction:column;gap:3px}
/* a class rule with display:flex outranks the browser default for [hidden],
   so the attribute needs saying explicitly or every menu renders open */
.tmenu[hidden]{display:none}
.tmenu label{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--fir);
  white-space:nowrap;cursor:pointer}
.tmenu input[type=checkbox]{width:auto;margin:0;transform:scale(1.05)}
.fhidden{display:none !important}
.tcell input.tcount{width:100%}
.tcell input.tsource{font-size:11px;color:var(--leaf)}
.tcell textarea.tnotes{width:100%;margin:0;padding:3px 5px;font-size:11px;
  border:1px solid var(--line);border-radius:5px;background:#fff;color:var(--fir);
  resize:vertical;font-family:inherit;line-height:1.35}
.tcell textarea.tnotes:focus{border-color:var(--leaf);outline:none;
  box-shadow:0 0 0 3px rgba(74,124,89,.15)}
body.readonly .tcell textarea{background:#f6faf1;border-color:transparent;pointer-events:none}
.fanrow{margin-top:8px;padding-top:8px;border-top:1px solid var(--line)}
#faninfo{font-size:12px;color:var(--leaf);margin-left:auto;text-align:right}
.fanslider{margin-top:6px}
.fanslabel{font-size:11px;color:var(--leaf);width:66px;flex:none}

/* ---- tray sizing controls ---- */
.tray-head{flex-wrap:wrap}
.tsize{display:inline-flex;align-items:center;gap:3px;margin-left:auto}
.tsize .tdim{width:44px;margin:0;padding:2px 4px;font-size:12px;text-align:center}
.tsize .tx{font-size:11px;color:var(--leaf)}
.trm{margin:0 0 0 6px;padding:2px 7px;font-size:11px;background:transparent;
  color:var(--leaf);border:1px solid var(--line);border-radius:5px;box-shadow:none}
.trm:hover{background:#fdeceb;color:#8a2d2a;border-color:#e8b6b3}
.trayadd{margin-top:12px;display:flex;align-items:center;gap:10px}
.trayadd button{margin:0;padding:5px 14px;font-size:13px;background:#eef4e6;
  color:var(--leaf);border:1px solid var(--line);box-shadow:none}
.trayadd button:hover{background:#e2eed6}
#trayaddinfo{font-size:12px;color:var(--leaf)}
#traycfg{display:flex;flex-direction:column;gap:7px}
.trayrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.trayrow .tlabelin{flex:1 1 140px;min-width:110px;margin:0;padding:5px 8px;font-size:14px}
.tdims{display:inline-flex;align-items:center;gap:4px;flex:none}
.tmeta{font-size:11px;color:var(--leaf);flex:1 1 auto;white-space:nowrap}

/* ---- planting map on narrow screens ----
   The grid must keep its physical shape (a cell is a real position in the
   tray), so it stays N columns wide and the contents shrink instead. */
.bshort{display:none}
@media (max-width:700px){
  .tgrid{gap:4px}
  .tcell{padding:4px 5px;gap:3px;min-height:0}
  .tcell .tid{font-size:9px}
  .tcell input,.tcell textarea{padding:2px 4px;font-size:11px}
  .tcell input.tseed{font-size:11px}
  .tcell input.tdate{font-size:10px}
  .tdrow{gap:3px}
  .tdlbl{width:22px;font-size:8px}
  .tstat{font-size:9px;line-height:1.25}
  /* icon-only actions: "Un-sprout" cannot fit a third of a phone screen */
  .blong{display:none}
  .bshort{display:inline;font-size:13px;line-height:1}
  .tacts{gap:3px}
  .tacts button{padding:3px 0;min-height:24px}
  /* cells in a row share a height, so the grid reads as a grid */
  .tgrid{align-items:stretch}
  .tray-head{gap:6px}
  .tray-head b{font-size:13px}
  .tray-head .tsum{font-size:11px}
}
@media (max-width:420px){
  .tcell input,.tcell textarea{font-size:10px}
  .tcell input.tdate{font-size:9px}
  .tdlbl{width:18px}
}
#rectwrap{margin-top:10px}
#rectimg{width:100%;border-radius:10px;border:1.5px solid var(--line);display:block}
#rectbtn.on{background:var(--leaf);color:#fff;border-color:var(--leaf)}
.dimrow{display:flex;align-items:center;gap:5px}
.dimrow input{width:100%}

/* ---- per-cell camera readings, laid out as the trays are ---- */
.ccells{margin-top:10px}
.ccname{font-size:12px;font-weight:700;color:var(--leaf);margin-bottom:5px}
.ccgrid{display:grid;gap:5px}
.ccell{background:#f6faf1;border:1px solid var(--line);border-radius:8px;
  padding:5px 6px;display:flex;flex-direction:column;gap:1px;min-height:52px}
.ccell.empty{background:transparent;border-style:dashed;opacity:.45}
.ccid{font-size:9px;font-weight:700;color:var(--leaf);letter-spacing:.04em}
.ccg{font-size:14px;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
.ccd{font-size:10px;color:var(--leaf);font-variant-numeric:tabular-nums}
@media (max-width:700px){
  .ccell{min-height:46px;padding:4px 5px}
  .ccg{font-size:12px}
  .ccd{font-size:9px}
}

/* ---- timelapse controls on narrow screens ----
   Three buttons plus the render summary and download link cannot share one
   row on a phone; without wrapping the text is squeezed into a one-word
   column and overflows the card. */
.pctrl{flex-wrap:wrap}
#renderinfo{font-size:12px;color:var(--leaf);line-height:1.4}
@media (max-width:700px){
  .pctrl{gap:8px}
  .pctrl button{flex:1 1 auto;min-width:0;padding:8px 12px;font-size:13px}
  #renderinfo{flex:1 1 100%;order:10;margin:0}
  #dlbtn{flex:1 1 100%;order:11;margin-left:0 !important;text-align:left}
}
@media (max-width:700px){
  /* every control row that mixes buttons with status text must wrap */
  .lcurvehead,.trayadd,.pctrl,.capctl,.lcrow{flex-wrap:wrap}
  .lcurvehead #sweepinfo{flex:1 1 100%;margin-left:0;text-align:left}
  .trayadd #trayaddinfo{flex:1 1 100%}
}

/* out-of-range probe calibration badge: the reading sits past an anchor, the
   percentage is pegged, and the dry alert is blind until recalibrated */
.calwarn{display:inline-block;margin-left:.35em;padding:0 .4em;border-radius:3px;
  background:#b5322f;color:#fff;font-size:.72em;font-weight:600;vertical-align:1px;
  cursor:help}
.focusrow{display:flex;align-items:center;gap:.6em;margin-top:.3em}
.pctx{color:#7a8a72;font-size:.85em}
#resstate.resbad{color:#b5322f}
#resstate.reswarn{color:#c98a1e}
#resstate.resok{color:#2e7d32}
