:root {
  --forest: #1b3a2b;
  --forest-mid: #2e5339;
  --forest-dark: #16301f;
  --cream: #f6f4ee;
  --ink: #222;
  --muted: #6b6b63;
  --line: #e2ded2;
}

* { box-sizing: border-box; }
body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; margin: 0; background: var(--cream); color: var(--ink); }
.container { max-width: 640px; margin: 0 auto; padding: 16px; }
.topnav { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; background: var(--forest-mid); color: #fff; padding: 12px 16px; }
.topnav .brand { font-family: 'Fraunces', serif; font-style: italic; font-weight: 600; font-size: 19px; margin-right: auto; color: #fff; text-decoration: none; }
.topnav a { color: #fff; text-decoration: none; font-size: 14px; }
.topnav a:hover { text-decoration: underline; }

/* Touch devices only: nav links are 19px tall by default, well under the ~44px tap-target
   guideline, and "Đăng xuất" sits next to "Đổi mật khẩu" — easy to mis-tap one-handed in the
   field. Scoped to coarse pointers so the desktop nav keeps its current height. */
@media (pointer: coarse) {
  /* Single scrollable row rather than wrapping: 7 links at a 45px tap height wrap to 3-4 rows
     on a phone (~164px of nav on every page, a fifth of the screen). Swipe to reach the rest;
     the three daily-use links come first so they're always visible without scrolling. */
  .topnav { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; gap: 0 4px; padding: 0 10px; }
  .topnav a { padding: 14px 8px; line-height: 1.2; white-space: nowrap; }
  /* The brand wordmark is a second link to dashboard.php, duplicating "Trang chủ", and it eats
     enough width to push "Thêm ghi chép" off-screen. Hidden on touch: nothing is lost but a
     duplicate, and the three daily-use links all fit without swiping. */
  .topnav .brand { display: none; }
}
h1 { font-family: 'Fraunces', serif; font-weight: 600; font-size: 26px; color: var(--forest); margin-bottom: 6px; }
h2 { font-family: 'Fraunces', serif; font-weight: 600; font-size: 19px; color: var(--forest); margin-top: 32px; }
h3 { font-family: 'Fraunces', serif; font-weight: 600; font-size: 16px; color: var(--forest); }
.brand-hero { font-family: 'Fraunces', serif; font-style: italic; font-weight: 600; font-size: 30px; color: var(--forest); margin-bottom: 20px; }
label { display: block; margin-bottom: 12px; font-size: 14px; }
input, select, textarea { width: 100%; padding: 10px; margin-top: 4px; font-size: 16px; border: 1px solid var(--line); border-radius: 6px; background: #fff; text-transform: none; letter-spacing: normal; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--forest-mid); outline-offset: 1px; }
button { background: var(--forest-mid); color: #fff; border: none; padding: 12px 22px; border-radius: 999px; font-size: 16px; font-family: inherit; cursor: pointer; transition: background 0.15s; }
button:hover { background: var(--forest-dark); }
fieldset { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 16px; padding: 14px; background: #fff; }
legend { font-family: 'Fraunces', serif; font-weight: 600; padding: 0 8px; color: var(--forest); }
.error { color: #b00020; }
.success { color: #1b7d3a; }
/* The -8px top margin exists to tuck a hint under the field it explains. Applying it to every
   .hint made it slide over whatever came before — a button, a picker grid, anything that is
   not a form label. Patched twice for specific cases before doing it properly: the pull-up is
   now opt-in, applied only after a label, so a hint placed anywhere new cannot overlap. */
.hint { font-size: 13px; color: var(--muted); margin: 0 0 12px; }
label + .hint { margin-top: -8px; }
label.checkbox { display: flex; align-items: center; gap: 8px; }
label.checkbox input[type="checkbox"] { width: auto; margin-top: 0; }
ul.error { padding-left: 20px; }
.filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 16px; }
.filters select { width: auto; }
.button-link { display: inline-block; padding: 10px 18px; background: var(--forest-mid); color: #fff; text-decoration: none; border-radius: 999px; font-size: 14px; }
.table-wrap { overflow-x: auto; background: #fff; border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 1px 3px rgba(27,58,43,0.06); }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
th { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: var(--muted); font-weight: 600; }

.container-wide { max-width: 1200px; }
.map-layout { display: flex; gap: 16px; flex-wrap: wrap; }
/* Background is white to match the drone photo's own no-data margin. Leaflet's default grey
   showed as obvious bands whenever the picture did not quite fill the box, which read as a
   broken map; white makes any remaining sliver continuous with the image. Belt and braces
   alongside the aspect cap in map.php, which removes the bands themselves. */
#farmMap { flex: 1 1 640px; height: 70vh; min-height: 420px; border: 1px solid var(--line); border-radius: 10px; background: #fff; }
/* flex-shrink must stay 1: with `0 0 320px` the panel refuses to shrink and overflows the
   page on 320px-wide phones (iPhone SE and older Androids). Basis keeps it 320px when there's room. */
.map-panel { flex: 0 1 320px; min-width: 0; max-height: 70vh; overflow-y: auto; border: 1px solid var(--line); border-radius: 10px; padding: 18px; background: #fff; box-shadow: 0 2px 10px rgba(27,58,43,0.08); }
.map-panel h2 { margin-top: 0; }
.map-panel textarea { width: 100%; font-family: inherit; font-size: 14px; padding: 8px; border: 1px solid var(--line); border-radius: 6px; margin: 6px 0; }
.plot-recent { list-style: none; padding: 0; margin: 8px 0; font-size: 13px; }
.plot-recent li { padding: 8px 0; border-bottom: 1px solid var(--line); }
.field-group { margin: 14px 0; }
.field-group label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--muted); margin-bottom: 4px; font-weight: 600; }
.photo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 8px 0; }
.photo-item img { width: 100%; height: 100px; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); }
.photo-item { font-size: 12px; }

.activity-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 4px 0 16px; }
.activity-btn { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 12px 6px; min-height: 56px; border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink); font-size: 14px; font-family: inherit; cursor: pointer; transition: background 0.15s, border-color 0.15s, color 0.15s; }
.activity-btn.active { background: var(--forest-mid); color: #fff; border-color: var(--forest-mid); font-weight: 600; }
/* display:flex above would otherwise beat the [hidden] attribute used to filter by location type. */
.activity-btn[hidden] { display: none; }
.activity-icon { font-size: 18px; line-height: 1; }

.banner { display: none; padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 14px; }
.banner.success { display: block; background: #e6f4ea; color: #1b7d3a; border: 1px solid #b7e1c3; }
.banner.error { display: block; background: #fdeaea; color: #b00020; border: 1px solid #f3c1c1; }

button:disabled, .btn:disabled { opacity: 0.6; cursor: not-allowed; }

/* ── Login hero ── */
.login-body { margin: 0; }
.login-hero {
  position: fixed; inset: 0; z-index: 0;
  background:
    linear-gradient(115deg, rgba(22,48,31,0.55) 0%, rgba(22,48,31,0.25) 40%, rgba(22,48,31,0.8) 100%),
    url('login-hero.jpg') center/cover no-repeat;
}
.login-container { position: relative; z-index: 1; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.login-card { background: rgba(255,255,255,0.97); border-radius: 16px; padding: 36px 32px; width: 100%; max-width: 380px; box-shadow: 0 1px 3px rgba(27,58,43,0.08), 0 24px 60px -24px rgba(0,0,0,0.5); }
/* The logo already reads "An Lợi Organic", so the wordmark below it drops to a quieter
   subtitle naming the app rather than repeating the brand at display size. */
.login-logo { display: block; width: 150px; height: auto; margin: 0 auto 14px; }
.login-card .brand-hero { text-align: center; margin-bottom: 24px; font-size: 20px; font-style: normal; color: var(--forest-mid); }

/* ── Dashboard stat strip ── */
.stat-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 20px; }
.stat-tile { background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(27,58,43,0.06); }
.stat-value { display: block; font-family: 'Fraunces', serif; font-weight: 600; font-size: 22px; color: var(--forest); }
.stat-tile-wide .stat-value { font-size: 16px; }
.stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--muted); font-weight: 600; }
.stat-tile-link { text-decoration: none; color: inherit; }

/* ── Corrections (append-only: superseded rows stay visible, clearly marked) ── */
tr.superseded td { color: var(--muted); background: #faf8f3; }
tr.superseded td:nth-child(4) { text-decoration: line-through; }
.tag-superseded, .tag-correction {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  font-size: 11px; letter-spacing: 0.3px; text-decoration: none; vertical-align: middle;
}
.tag-superseded { background: #efe9dc; color: var(--muted); }
.tag-correction { background: #e6f4ea; color: #1b7d3a; }

/* ── Map crop legend, which doubles as the layer filter ── */
.map-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin-bottom: 8px; font-size: 13px; color: var(--muted); }
.legend-item { display: flex; align-items: center; gap: 6px; margin-bottom: 0; cursor: pointer; user-select: none; padding: 4px 0; white-space: nowrap; }
.legend-item input[type="checkbox"] { width: auto; margin: 0; accent-color: var(--forest-mid); cursor: pointer; }
.legend-item:has(input:not(:checked)) { opacity: 0.45; }
.legend-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: none; }
.legend-actions { display: flex; gap: 10px; margin-left: auto; }
.legend-link { background: none; border: none; padding: 4px 2px; font-size: 12px; font-family: inherit; color: var(--forest-mid); text-decoration: underline; cursor: pointer; border-radius: 0; }
.legend-link:hover { background: none; color: var(--forest-dark); transform: none; box-shadow: none; }
@media (pointer: coarse) {
  /* The whole row is a <label>, so the horizontal target is already wide; enlarge the checkbox
     and row height rather than forcing 44px rows, which would push the map far down the page. */
  .legend-item { padding: 10px 0; }
  .legend-item input[type="checkbox"] { width: 20px; height: 20px; }
  .legend-actions { margin-left: 0; }
  .legend-link { padding: 10px 2px; font-size: 13px; }
}

/* ── Phone layout ──
   Switched by a `phone-layout` class that map.php puts on <body> from screen size, NOT by a
   width media query. Two reasons: the same screen rule already decides which drone-photo
   resolution to load, so one definition of "phone" governs both; and the rest of this
   stylesheet is deliberately free of width breakpoints (layout is flex-wrap / max-width, i.e.
   continuous), which is what lets a narrow layout be measured by constraining an element
   rather than needing a real handset. A class keeps that property and stays testable —
   add the class and the phone layout appears at any window size. */

/* The legend is a set-once filter that cost 175px of an 844px screen, three rows of it once
   touch targets are applied. Collapsed by default on a phone, behind a count. */
.legend-toggle { display: none; }
.phone-layout .legend-toggle {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: 8px;
  background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 14px; font: inherit; font-size: 13px; color: var(--forest-dark); cursor: pointer;
}
/* .map-legend is display:flex, which silently beats the [hidden] attribute — the same trap
   .activity-btn hit on the entry form. Hide it by rule, not by attribute alone. */
.map-legend[hidden] { display: none; }

/* The detail panel rendered ~930px down the page: tapping a plot appeared to do nothing at all
   because its output was below the fold. On a phone it becomes a bottom sheet over the map. */
.phone-layout .map-panel {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1200;
  max-width: none; flex: none; width: auto;
  max-height: 68vh; overflow-y: auto;
  border: none; border-radius: 16px 16px 0 0;
  box-shadow: 0 -6px 24px rgba(27, 58, 43, 0.22);
  padding: 8px 18px 24px;
  transform: translateY(102%);
  overscroll-behavior: contain;
}
/* Deliberately NOT animated. A slide was built first and then removed: every attempt to verify
   it measured the panel never reaching its target, because a transform change under an active
   `transition` needs frames to be produced and the test browser was not painting. Rather than
   ship an effect that could not be checked, show/hide is instant — pure layout, verifiable,
   and one less thing to go wrong on a cheap Android in a field. Add the transition back only
   with a real device to confirm it on. */
/* Grab handle — signals the sheet can be dismissed, and gives the close button a row to sit in. */
.phone-layout .sheet-bar {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 0 10px; margin-bottom: 4px; background: #fff;
}
.phone-layout .sheet-grip {
  position: absolute; left: 50%; transform: translateX(-50%); top: 6px;
  width: 40px; height: 4px; border-radius: 2px; background: var(--line);
}
.sheet-bar { display: none; }
.phone-layout .sheet-close {
  margin-left: auto; background: none; border: none; font-size: 26px; line-height: 1;
  padding: 4px 10px; color: var(--muted); cursor: pointer; border-radius: 8px;
}
/* With the panel lifted out of the flow, the map can have the column to itself. */
/* 100dvh, not 100vh. On a phone, 100vh is the viewport with the address bar HIDDEN, so the map
   box is taller than what is actually on screen and then changes height the moment the bar
   collapses or reappears — Leaflet measures itself once at startup and is left holding a stale
   size, which shows up as the photo not filling the box, grey margins, or the view sitting off
   where it should. dvh tracks the visible area instead. The vh line stays first as a fallback
   for anything that does not know dvh. */
.phone-layout #farmMap { height: calc(100vh - 220px); min-height: 320px; }
.phone-layout #farmMap { height: calc(100dvh - 220px); }

/* ── Crop picker on the entry form ──
   Buttons, not a dropdown: the same reasoning that made the activity picker a tap grid. A closed
   <select> hides the options, and this choice has to be made deliberately every time — nothing is
   pre-selected, so the form refuses to save until one is pressed. */
.crop-picker { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 2px; }
.crop-btn {
  flex: 0 1 auto; padding: 12px 16px; font-size: 15px; font-family: inherit;
  background: #fff; color: var(--forest-dark);
  border: 1.5px solid var(--line); border-radius: 999px; cursor: pointer;
  transition: none;
}
.crop-btn:hover { background: var(--cream-dark, #f3efe4); transform: none; box-shadow: none; }
.crop-btn.active {
  background: var(--forest-mid); color: #fff; border-color: var(--forest-mid); font-weight: 600;
}
/* "Cả lô" is deliberately larger than the crop buttons: it is the honest answer whenever one
   pass covered the whole block, and on a mixed plot it is the most common one. Making it the
   easiest target is not the same as making it the default — nothing is pre-selected. */
.crop-btn-all {
  flex: 1 1 100%; padding: 16px 20px; font-size: 17px; font-weight: 600;
  border-width: 2px; border-style: dashed;
}
.crop-btn-all.active { border-style: solid; }
.req { color: #b00; font-weight: 700; }

@media (pointer: coarse) {
  .crop-btn { padding: 14px 18px; font-size: 16px; }
  .crop-btn-all { padding: 18px 20px; font-size: 18px; }
}

/* Crop checklist in the map panel. A plot grows several things; a single dropdown could only
   ever say one of them, which is what sent macca into free-text planting notes for months. */
.crop-checklist { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 6px 0 10px; }
.crop-check { display: flex; align-items: center; gap: 6px; font-size: 14px; margin: 0; cursor: pointer; white-space: nowrap; }
.crop-check input[type="checkbox"] { width: auto; margin: 0; accent-color: var(--forest-mid); }
@media (pointer: coarse) {
  .crop-check { padding: 8px 0; }
  .crop-check input[type="checkbox"] { width: 20px; height: 20px; }
}



/* .field-group label carries only 4px of bottom margin, so the 8px tuck above overshoots it and
   the hint's text sits 4px inside the label's. Measured with Range.getClientRects rather than
   element boxes — element boxes hide this, which is why it survived two earlier passes. */
.field-group label + .hint { margin-top: 0; }


/* ── Plot panel: collapsible editing sections ──
   The panel answers questions above the fold and keeps the forms that change things behind a
   heading you press. <details> is used rather than a JS accordion: no state to keep in sync, it
   works with a keyboard, and it cannot break on script load order. */
.panel-section { border-top: 1px solid var(--line); margin-top: 4px; }
.panel-section > summary {
  cursor: pointer; padding: 14px 2px; font-size: 14px; font-weight: 600;
  color: var(--forest); list-style: none; display: flex; align-items: center; gap: 8px;
}
.panel-section > summary::-webkit-details-marker { display: none; }
.panel-section > summary::before { content: '▸'; color: var(--muted); font-size: 12px; }
.panel-section[open] > summary::before { content: '▾'; }
.panel-section > summary:hover { color: var(--forest-dark); }
.panel-section > *:not(summary) { margin-bottom: 12px; }

/* One primary action per panel. Eight identical buttons meant "+ Thêm ghi chép" — the thing a
   manager opened the plot to do — read exactly like "Ngưng dùng". */
.btn-primary {
  background: var(--forest-mid); color: #fff; font-weight: 600;
  box-shadow: 0 1px 3px rgba(27,58,43,0.2);
}
.btn-primary:hover { background: var(--forest-dark); }

/* A manual condition note is something someone wrote on purpose; it should not look like a hint. */
.plot-note {
  background: #fffbe8; border-left: 3px solid var(--facility, #ffd400);
  padding: 8px 10px; margin: 8px 0; font-size: 14px; border-radius: 0 6px 6px 0;
}

/* sales.php — optional plot tags. The [hidden] reset is required, not decoration: label.checkbox is
   display:flex, which beats the hidden attribute — the same trap .activity-btn and .map-legend hit. */
.sale-plots { margin-bottom: 12px; }
.sale-plots summary { cursor: pointer; padding: 8px 0; font-size: 14px; }
.plot-checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 4px 12px; }
.plot-checks label.checkbox { margin: 0; white-space: nowrap; }
.plot-checks label[hidden], #sale-drc-label[hidden] { display: none; }
@media (pointer: coarse) {
  .plot-checks label.checkbox { padding: 8px 0; }
  .plot-checks input[type="checkbox"] { width: 20px; height: 20px; }
}

/* Label photo thumbnails — on the entry form (preview), dashboard and plot panel. */
.entry-photo-preview { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
.entry-photo-preview img { width: 72px; height: 72px; object-fit: cover; border-radius: 6px; border: 1px solid var(--line); }
.entry-photo-links { display: inline-flex; gap: 4px; margin-left: 6px; vertical-align: middle; }
.entry-photo-links img { width: 36px; height: 36px; object-fit: cover; border-radius: 4px; border: 1px solid var(--line); }

/* Big photo button on the spraying and fertilizing forms. Solid dark green with white text is the highest-contrast
   pair in the palette, which is what survives a phone screen in direct sun. 64px tall, full width:
   a thumb target, not a cursor target. */
.photo-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; box-sizing: border-box; min-height: 64px; padding: 14px 16px; margin: 4px 0 8px;
  background: var(--forest); color: #fff; border-radius: 12px;
  font-size: 18px; font-weight: 700; cursor: pointer; user-select: none;
}
.photo-btn:active { background: var(--forest-dark); }
.photo-btn-icon { font-size: 26px; line-height: 1; }
.photo-btn.has-photos { background: #fff; color: var(--forest); border: 2px solid var(--forest); }
/* Keyboard focus lands on the hidden input; show it on the button instead. */
.photo-input:focus-visible + .photo-btn { outline: 3px solid #ffd400; outline-offset: 2px; }
/* Hidden but still in the form and still focusable — display:none would work for the tap on
   current browsers, but this is the form that must never fail to open the camera. */
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ── Sending and unsent drafts (assets/drafts.js) ──
   The result of pressing Lưu must be impossible to misread on a phone in the sun: a solid green
   "đã lưu" (the dashboard banner) or this solid red "CHƯA LƯU". Nothing in between. */
.send-status { display: block; border-radius: 10px; padding: 14px 16px; margin: 0 0 16px; font-size: 15px; line-height: 1.45; }
.send-status strong { display: block; font-size: 17px; margin-bottom: 4px; }
.send-status span { display: block; }
.send-status ul { margin: 6px 0 0; padding-left: 20px; }
.send-progress { background: #eef5ef; border: 2px solid var(--forest-mid); color: var(--forest); }
.send-fail { background: #b00020; color: #fff; }
.send-fail a { color: #fff; font-weight: 700; }
.send-fail .send-retry { margin-top: 10px; background: #fff; color: #b00020; font-weight: 700; width: 100%; min-height: 52px; font-size: 17px; }
.send-bar { height: 10px; background: #fff; border-radius: 5px; overflow: hidden; margin: 8px 0; border: 1px solid var(--line); }
.send-bar-fill { height: 100%; width: 0; background: var(--forest-mid); transition: width 0.2s; }

.draft-banner { background: #fff4d6; border: 2px solid #e0a800; border-radius: 10px; padding: 12px 14px; margin: 0 0 18px; }
.draft-banner > strong { display: block; font-size: 16px; color: #6b4e00; }
.draft-banner > .hint { margin: 2px 0 8px; }
.draft-row { border-top: 1px solid #f0d78a; padding: 10px 0 4px; }
.draft-desc { font-weight: 600; }
.draft-msg { font-size: 13px; color: #6b4e00; margin: 2px 0 6px; }
.draft-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.draft-actions button { min-height: 44px; }
.draft-actions span { font-size: 13px; flex-basis: 100%; }
.draft-done .draft-msg { color: #1b7d3a; font-weight: 600; }
.button-secondary { background: #fff; color: var(--forest-mid); border: 1px solid var(--forest-mid); }
.button-secondary:hover { background: #f2f0ea; }
