/* ══════════════════════════════════════════════════════════════
   DooDurian — AI Service Design System
   Version 5.0 · native AI for Thai service, DooDurian accent

   White surface, thin borders, plain cards, technical layout —
   the platform's visual language — with the DooDurian palette
   carried by CTAs, active states and badges only.

   The platform header sits above this service; everything here is
   service-level chrome, so there is exactly one navigation.

   No inline styles anywhere: production serves `style-src 'self'`.
   ══════════════════════════════════════════════════════════════ */

/* ─── Design Tokens ─────────────────────────────────────────── */
:root {
  /* DooDurian brand — the bright green is the brand and stays as it is.
     Design System v2's ergonomics (16px body, 48px targets, wider radii) are
     applied on top of it; only v2's dark-forest recolour was declined. */
  --green: #00B96B;
  --green-hover: #00a35d;
  --green-press: #008a4f;
  --forest: #153F32;
  /* Same colour, channels only — for the photo scrims and shadows that need
     an alpha. Keeps them tracking --forest instead of a copied rgb() literal. */
  --forest-rgb: 21, 63, 50;
  /* Vivid green for large surfaces that carry white text. The brand --green is
     too light for that: white on #00B96B is 2.58:1 and unreadable in sunlight,
     while white on this is 4.60:1. --forest stays the ink colour for headings. */
  --green-deep: #00874A;
  /* Warm highlight on --green-deep. The gold pair only works on --forest
     (6.26:1); on the vivid green it collapses to 2.45:1, this holds 4.13:1. */
  --cream: #FFF3C4;
  --green-soft: #EFFAF4;
  --green-soft-border: #C9EEDC;

  /* Gold — one CTA per screen, never a surface. Replaces the old flat yellow;
     it is the only v2 hue kept, because it still reads as distinct next to
     the bright green rather than competing with it. */
  --gold: #D4A03C;
  --gold-hover: #C08F2C;
  --gold-press: #A97D22;
  --gold-on-dark: #E3B75C;
  --gold-ink: #8A6414;
  --gold-disabled: #EDE3CB;
  --gold-soft: #FCF6E7;
  --gold-soft-border: rgba(138, 100, 20, .25);

  /* Surfaces — kept cool to sit with the bright green; v2's warm paper was
     tuned for the forest palette and turns muddy against #00B96B. */
  --white: #FFFFFF;
  --surface: #F7FAF8;
  /* A hair off white, for the one surface that has to read as "behind" the
     bubbles sitting on it without becoming a panel of its own. */
  --surface-thread: #FBFCFB;
  --border: #E4EAE7;
  --border-strong: #CFD8D3;

  /* Text */
  --text: #1B2420;
  --text-muted: #667069;
  --text-soft: #8A938D;
  --text-body: #3C443E;

  /* Status */
  /* Footer gradient. Was the main Doo-Durian site's olive; the app design
     pulls it onto the service's own brand greens so the footer closes the
     page in the same hue the CTAs and active tabs use. */
  --footer-from: #00B96B;
  --footer-to: #00874A;

  --danger: #C0342B;
  --danger-hover: #A32A22;
  --danger-press: #85211B;
  --danger-soft: #FDF0EE;
  --danger-border: #F3CFCA;
  --danger-disabled: #EFD9D6;
  --success: #1E6E3A;
  --success-ink: #155C2E;
  --success-soft: #EFFAF4;
  --success-border: #C9EEDC;
  --warn: #A9700B;
  --warn-soft: #FDF6E7;
  --warn-border: #EEDFB4;
  --info: #1D6FA5;
  --info-soft: #EDF5FA;
  --info-border: #C6DEEE;

  /* Focus ring — dark green rather than the brand green, so the ring stays
     visible when it lands on a green button. */
  --ring: #153F32;

  /* Disabled */
  --disabled-bg: #DCE3DC;
  --disabled-ink: #8D948C;
  --disabled-border-ink: #A8AEA4;

  /* Elevation — the platform look is flat; shadows only lift cards */
  --shadow-xs: 0 1px 2px rgba(21, 63, 50, .04);
  --shadow-sm: 0 1px 2px rgba(21, 63, 50, .05), 0 8px 24px rgba(21, 63, 50, .06);
  --shadow-md: 0 2px 4px rgba(21, 63, 50, .07), 0 16px 36px rgba(21, 63, 50, .10);

  /* Radii — sm buttons/chips/inputs, md cards, lg hero/panels */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 20px;
  --r-xl: 20px;
  --r-full: 999px;

  /* Layout */
  --container: 1152px;
  --gutter: 20px;
  --tabs-height: 48px;

  /* Touch — growers work in gloves, so 48px is the floor, not 44px */
  --touch-min: 48px;
  --control-sm: 40px;
  --control-md: 48px;
  --control-lg: 52px;

  /* Looped Thai sans reads better than a loopless face for growers aged
     45–65; the serif is display-only and never below 22px. */
  --font: "IBM Plex Sans Thai Looped", "Sarabun", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: "Noto Serif Thai", Georgia, serif;
  --font-mono: "SFMono-Regular", Menlo, Consolas, "Courier New", monospace;
  /* The landing page's top bar is set in Prompt in the design; it is loaded
     on that page only, so the rest of the site keeps one text face. */
  --font-nav: "Prompt", var(--font);
  /* Two hero faces are Canva-licensed and cannot be served from here, so both
     are stood in for and the sizes below are scaled to hold the design's
     measure. The file Canva labels "League Spartan" is a wide geometric cut,
     nothing like the narrow one Google ships under that name — Poppins Bold
     matches it to 3%. The tagline's TT Fors is closest to Prompt Bold. Inter
     is the design's body face and only has to cover Latin, so Thai falls
     through to --font exactly as Canva does it. */
  --font-hero: "Poppins", var(--font);
  --font-ui: "Inter", var(--font);

  /* ─── Landing-page chrome, straight from the Canva file ──────
     Not derived from the palette above — these are the design's own
     literals, kept here because no raw hex belongs outside this file. */
  --ink-pill: #000000;
  --grad-login: linear-gradient(180deg, #299a01 0%, #c9e265 100%);
  --green-cta: #00c202;   /* the hero's LINE button */
  --green-ink: #299a01;   /* the "AI" in the tagline, and the login gradient's head */
}

/* ─── Reset & Base ──────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font-family: var(--font);
  font-size: 16px;
  line-height: 26px;
  color: var(--text);
  background: var(--white);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body > main { flex: 1 0 auto; }

img { max-width: 100%; display: block; }
a { color: var(--green); text-decoration: none; transition: color .15s; }
a:hover { color: var(--green-hover); }
ul, ol { list-style: none; }
input, select, textarea, button { font-family: inherit; font-size: inherit; color: inherit; }

h1, h2, h3, h4 { color: var(--forest); line-height: 1.35; }
h1 { font-size: 28px; line-height: 38px; font-weight: 700; }
h2 { font-size: 20px; line-height: 28px; font-weight: 600; }
h3 { font-size: 17px; line-height: 26px; font-weight: 600; }
h4 { font-size: 15px; line-height: 24px; font-weight: 600; }

/* One dark ring for every variant on a light surface. A gold ring on the gold
   button reads as part of the button, so it is not used as the default. */
:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; border-radius: 4px; }
:focus:not(:focus-visible) { outline: none; }

/* Components below set an explicit `display`, which would otherwise win over
   the browser default for [hidden]. Scripts toggle `hidden` everywhere. */
[hidden] { display: none !important; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); border: 0;
}

.container { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }

.caption {
  font-size: 12px; line-height: 16px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-soft);
}

/* ═══════════════════════════════════════════════════════════════
   SERVICE HEADER + NAV
   Service-level only. The AI for Thai platform header will sit above
   this once embedded, so nothing here repeats a platform menu.
   ═══════════════════════════════════════════════════════════════ */
/* Brand banner — the service's own header while the AI for Thai platform
   header is not in place yet. Home only: it is 1920×500, so repeating it on
   the working pages would push every tool below the fold. */
.service-banner { position: relative; background: var(--forest); }
.service-banner img {
  width: 100%; height: auto; display: block;
  max-width: 1920px; margin: 0 auto;
}

/* โลโก้กับชื่อวางทับกลางแบนเนอร์
   inset: 0 แล้วจัดกลางทั้งสองแกน ได้ตำแหน่งเดียวกับกลางภาพเสมอ เพราะภาพเองก็
   margin: 0 auto อยู่แล้ว (จอกว้างกว่า 1920 ภาพจะหยุดโตแล้วอยู่กลาง)

   ขนาดคิดเป็น vw เป็นหลัก เพราะภาพย่อตามความกว้างจอแบบคงสัดส่วน (1920×500)
   ถ้าโลโก้ไม่ย่อตามด้วย บนจอแคบมันจะโตเกินแล้วไปทับกล่องผู้สนับสนุนที่อยู่บนกลาง
   ของภาพ แต่มีค่าต่ำสุดกำกับไว้ด้วย: ปล่อยตาม vw ล้วนแล้วที่ 375px ตัวอักษรออกมา
   8.25px ซึ่งไม่ใช่ตัวอักษรแล้ว เป็นแค่รอยเปื้อน — ค่าต่ำสุดที่ตั้งไว้ยังห่างจาก
   กล่องผู้สนับสนุนที่ 375px อยู่ 11px (วัดแล้ว)

   ตัวอักษรสีขาวพร้อมเงาเข้ม เพราะพื้นตรงนั้นเป็นหญ้าสีเขียวกลาง ๆ (วัดได้
   rgb(89,157,76) แต่มีจุดแดดสว่างถึง 255) สีขาวเปล่า ๆ จะหายไปในจุดสว่าง
   เงาเป็นตัวที่ทำให้อ่านออก ไม่ใช่สีตัวอักษร — เป็นวิธีเดียวกับข้อความในตัว
   artwork ของแบนเนอร์เอง */
.service-banner__mark {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: min(0.7vw, 12px);
  pointer-events: none;
}
.service-banner__mark img {
  width: clamp(28px, 6.4vw, 116px); height: auto;
  border-radius: 50%;
  border: clamp(1.5px, 0.22vw, 4px) solid rgba(255, 255, 255, .94);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .38);
}
.service-banner__mark span {
  font-size: clamp(11px, 2.2vw, 40px); line-height: 1.1;
  font-weight: 800; color: var(--white); letter-spacing: -.01em;
  text-shadow: 0 2px 4px rgba(0, 0, 0, .6), 0 0 16px rgba(0, 0, 0, .4);
}

.service-bar { background: var(--white); border-bottom: 1px solid var(--border); }
.service-bar__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 16px 0 12px;
}
.service-brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.service-brand img { width: 40px; height: 40px; border-radius: var(--r-lg); }
.service-brand__name {
  display: block; font-size: 19px; line-height: 26px;
  font-weight: 700; color: var(--forest); letter-spacing: -.01em;
}
.service-brand__sub { display: block; font-size: 12px; line-height: 16px; color: var(--text-soft); }

.service-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
/* The developer entry point lives here rather than in the tab row, so the tabs
   stay the four things a grower actually uses. */
.service-actions .btn.active {
  background: var(--green-soft); border-color: var(--green); color: var(--green);
}

/* Tabs — the service's only navigation */
.service-tabs {
  display: flex; align-items: stretch; gap: 4px;
  overflow-x: auto; scrollbar-width: none;
}
.service-tabs::-webkit-scrollbar { display: none; }
.service-tab {
  display: inline-flex; align-items: center; gap: 8px;
  height: var(--tabs-height); padding: 0 14px;
  font-size: 15px; font-weight: 500; color: var(--text-muted);
  border-bottom: 2px solid transparent;
  white-space: nowrap; text-decoration: none;
  transition: color .15s, border-color .15s, background-color .15s;
}
.service-tab i { font-size: 14px; }
.service-tab:hover { color: var(--forest); background: var(--surface); }
.service-tab.active {
  color: var(--green); font-weight: 600;
  border-bottom-color: var(--green);
}
.service-tab.active i { color: var(--green); }

/* ─── Buttons ───────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px; height: var(--control-md); padding: 0 20px;
  border-radius: var(--r-sm); border: 1px solid transparent;
  font-weight: 600; font-size: 16px; line-height: 1.4;
  cursor: pointer; white-space: nowrap; text-decoration: none;
  transition: background-color .15s, border-color .15s, color .15s;
}
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn--primary { background: var(--green); color: #fff; border-color: var(--green); }
.btn--primary:hover:not(:disabled) { background: var(--green-hover); border-color: var(--green-hover); color: #fff; }
.btn--primary:active:not(:disabled) { background: var(--green-press); border-color: var(--green-press); }

.btn--secondary { background: var(--white); color: var(--forest); border-color: var(--border-strong); }
.btn--secondary:hover:not(:disabled) { background: var(--surface); border-color: var(--green); color: var(--forest); }

.btn--ghost { background: transparent; color: var(--text-muted); border-color: transparent; }
.btn--ghost:hover:not(:disabled) { background: var(--surface); color: var(--forest); }

.btn--outline { background: var(--white); color: var(--green); border-color: var(--green); }
.btn--outline:hover:not(:disabled) { background: var(--green-soft); color: var(--green-hover); }

/* Gold — the one CTA a screen is allowed to shout with. Ink stays --text, not
   white: gold is a light surface and white on it fails contrast. Every page
   set had grown its own copy of this; it lives here now. */
.btn--gold { background: var(--gold); color: var(--text); border-color: var(--gold); }
.btn--gold:hover:not(:disabled) { background: var(--gold-hover); border-color: var(--gold-hover); color: var(--text); }
.btn--gold:active:not(:disabled) { background: var(--gold-press); border-color: var(--gold-press); }
.btn--gold:disabled { background: var(--gold-disabled); border-color: var(--gold-disabled); color: var(--disabled-ink); }

/* sm is the smallest a control may get and still be hit in gloves; anything
   below --control-sm is not a tap target. */
.btn--sm { height: var(--control-sm); padding: 0 16px; font-size: 14px; }
.btn--lg { height: var(--control-lg); padding: 0 24px; font-size: 17px; }
.btn--block { width: 100%; }
.btn--icon { width: var(--control-md); padding: 0; }
.btn--icon.btn--sm { width: var(--control-sm); }

/* ─── Badge ─────────────────────────────────────────────────── */
/* Every badge carries an icon next to its text. Colour alone is not allowed to
   carry meaning here — in direct sun on a phone screen the hues wash out. */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: var(--r-full);
  font-size: 13px; line-height: 18px; font-weight: 600;
  border: 1px solid transparent;
}
.badge--api { background: var(--green-soft); color: #067A4A; border-color: var(--green-soft-border); }
.badge--service { background: var(--surface); color: var(--text-muted); border-color: var(--border); }
.badge--gold { background: var(--gold-soft); color: var(--gold-ink); border-color: var(--gold-soft-border); }
.badge--info { background: var(--info-soft); color: var(--info); border-color: var(--info-border); }
.badge--success { background: var(--success-soft); color: var(--success-ink); border-color: var(--success-border); }
.badge--warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-border); }
.badge--danger { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-border); }
/* On a dark photo or hero the badge inverts and the gold does the talking. */
/* Vivid green rather than the near-black forest — this badge means "in season /
   ready to cut", so the fresh green carries the meaning. White reads 4.60:1 on
   it; the old gold pair only worked because the surface was almost black. */
.badge--season { background: var(--green-deep); color: #fff; border-color: transparent; }

/* ─── Card ──────────────────────────────────────────────────── */
.card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: 20px;
}
.card--flush { padding: 0; overflow: hidden; }
/* Three levels of importance. Flat is the default; a card only earns a shadow
   or a tint by being the thing on the screen that matters most. */
.card--raised { box-shadow: var(--shadow-sm); }
.card--feature { background: var(--green-soft); border-color: var(--green-soft-border); }

/* ─── SectionHeader ─────────────────────────────────────────── */
/* Serif title, sans subtitle, and the "see all" control is always ghost so it
   never competes with the section it labels. */
.section-header {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 14px; padding-bottom: 14px; margin-bottom: 18px;
  border-bottom: 1px solid var(--border);
}
/* หัวข้อเป็นเขียวเข้ม ไม่ใช่เกือบดำ ตามชุดออกแบบ — --green-deep ผ่าน 4.5:1
   บนพื้นขาว จึงใช้กับหัวข้อขนาดนี้ได้โดยไม่ต้องพึ่งขนาดตัวอักษรช่วย */
.section-header__title {
  font-family: var(--font-display);
  font-size: 26px; line-height: 1.25; font-weight: 700;
  color: var(--green-deep); margin: 0;
}
.section-header__sub { font-size: 15px; color: var(--text-muted); margin: 6px 0 0; }
.section-header__link {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  height: var(--touch-min); padding: 0 14px; border-radius: var(--r-md);
  color: var(--green); font-size: 15px; font-weight: 600;
}
.section-header__link:hover { background: var(--green-soft); color: var(--green-hover); }
.section-header__link i { font-size: 12px; }

/* ─── Directory ─────────────────────────────────────────────── */
/* The tab row holds seven entries; three of them are hubs. Each hub closes
   with a directory of every screen in its area, which is what keeps the whole
   site two clicks from the homepage without a twenty-item tab row. */
.dir-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
}
.dir-grid + .dir-head { margin-top: 26px; }
.dir-head {
  font-size: 13px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 10px;
}
.dir-link {
  display: flex; align-items: center; gap: 12px;
  min-height: var(--touch-min); padding: 9px 14px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--white); color: var(--text);
  font-size: 15px; line-height: 20px; font-weight: 600; text-decoration: none;
  transition: border-color .15s, background-color .15s;
}
.dir-link:hover { border-color: var(--green); background: var(--green-soft); color: var(--forest); }
.dir-link i { flex: none; width: 20px; text-align: center; font-size: 15px; color: var(--green); }
.dir-link__sub { display: block; font-size: 12px; line-height: 17px; font-weight: 400; color: var(--text-muted); }

/* ─── FilterChip ────────────────────────────────────────────── */
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  height: 46px; padding: 0 17px; border-radius: var(--r-full);
  border: 1.5px solid var(--border-strong); background: var(--white);
  font-size: 15px; font-weight: 500; color: var(--text-body);
  cursor: pointer; white-space: nowrap;
  transition: background-color .15s, border-color .15s, color .15s;
}
.chip:hover { border-color: var(--green); }
.chip--on {
  border-color: var(--green); background: var(--green-soft);
  color: var(--forest); font-weight: 600; padding: 0 8px 0 17px;
}
.chip--on .chip__clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%; border: 0;
  background: transparent; color: inherit; cursor: pointer;
}
.chip--on .chip__clear:hover { background: var(--green-soft-border); }
.chip:disabled, .chip--off {
  border-color: var(--border); background: var(--surface);
  color: var(--disabled-border-ink); cursor: not-allowed;
}

/* ─── Rating ────────────────────────────────────────────────── */
.rating { display: inline-flex; align-items: center; gap: 9px; font-size: 17px; }
.rating__stars { color: var(--gold); }
.rating__value { font-weight: 700; color: var(--text); }
.rating__count { color: var(--text-muted); font-size: 15px; }

/* ─── PriceDisplay ──────────────────────────────────────────── */
.price { display: inline-flex; align-items: baseline; gap: 14px; }
.price__now { font-size: 24px; font-weight: 700; color: var(--forest); }
.price__unit { font-size: 15px; font-weight: 500; color: var(--text-muted); }
.price__was { font-size: 16px; color: var(--text-muted); text-decoration: line-through; }

/* ─── FarmAvatar ────────────────────────────────────────────── */
/* Initials rather than a photo: most orchards have no logo yet. The verified
   tick is only legible from md up, so smaller sizes never show it. */
.avatar {
  position: relative; display: inline-flex;
  align-items: center; justify-content: center;
  border-radius: 50%; background: var(--green-soft); color: var(--forest);
  font-weight: 700; flex: none;
}
.avatar--xs { width: 32px; height: 32px; font-size: 13px; }
.avatar--sm { width: 40px; height: 40px; font-size: 15px; }
.avatar--md { width: 56px; height: 56px; font-size: 20px; }
.avatar--lg { width: 80px; height: 80px; font-size: 28px; }
.avatar__tick {
  position: absolute; right: -2px; bottom: -2px;
  display: none; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--success); color: #fff;
  border: 2px solid var(--white);
}
.avatar--md .avatar__tick { display: flex; width: 20px; height: 20px; font-size: 10px; }
.avatar--lg .avatar__tick { display: flex; width: 26px; height: 26px; font-size: 13px; }

/* ─── ProgressBar ───────────────────────────────────────────── */
.progress__top { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.progress__label { font-size: 15px; font-weight: 600; color: var(--text); }
.progress__value { margin-left: auto; font-size: 15px; font-weight: 700; color: var(--green); }
.progress__track {
  height: 12px; background: var(--surface);
  border-radius: var(--r-full); overflow: hidden;
}
.progress__fill { height: 100%; background: var(--green); border-radius: var(--r-full); }
.progress__note { font-size: 14px; color: var(--text-muted); margin: 7px 0 0; }
.progress--warn .progress__value, .progress--warn .progress__fill { color: var(--warn); background-color: var(--warn); }
.progress--warn .progress__value { background: none; }

/* ─── Countdown ─────────────────────────────────────────────── */
/* Pairs with ProgressBar in a group buy: how full, and how long left. */
.countdown { display: flex; gap: 9px; }
.countdown__cell {
  flex: 1; background: var(--surface); border-radius: 14px;
  padding: 12px 8px; text-align: center;
}
.countdown__n {
  font-size: 26px; font-weight: 700; color: var(--forest);
  margin: 0; line-height: 1; font-variant-numeric: tabular-nums;
}
.countdown__unit { font-size: 12px; color: var(--text-muted); margin: 6px 0 0; }
/* Under six hours the whole thing turns red. */
.countdown--urgent .countdown__cell { background: var(--danger-soft); }
.countdown--urgent .countdown__n { color: var(--danger); }

/* ─── ImageGallery ──────────────────────────────────────────── */
/* First shot always largest; the rest stack to its right. */
.gallery { display: flex; gap: 9px; }
.gallery__lead { flex: 2; aspect-ratio: 4/3; border-radius: 14px; overflow: hidden; background: var(--surface); }
.gallery__rest { flex: 1; display: flex; flex-direction: column; gap: 9px; }
.gallery__thumb { flex: 1; border-radius: var(--r-md); overflow: hidden; background: var(--surface); }
.gallery img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery__more {
  flex: 1; border-radius: var(--r-md); border: 0; cursor: pointer;
  background: var(--forest); color: #fff; font-size: 16px; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
}

/* ─── Page scaffolding ──────────────────────────────────────── */
.page { padding: 32px 0 56px; }

.page-head { margin-bottom: 24px; }
.page-head h1 { margin-bottom: 8px; }
.page-head p { color: var(--text-muted); max-width: 620px; }
.page-head .badge { margin-top: 12px; }

.section-title {
  font-size: 15px; font-weight: 600; color: var(--forest);
  margin-bottom: 12px;
}
/* The line that qualifies a heading — "forecast, not a measurement" — is now
   .section-header__sub, which every page-level heading carries. .section-title
   is left for headings inside a card, where the card's own frame is the
   boundary and a second rule under the heading would double it up. */

/* ─── Notices ───────────────────────────────────────────────── */
.notice {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 14px 16px;
  margin-top: 20px;
}
.notice h2, .notice h3 {
  font-size: 15px; line-height: 24px; font-weight: 600;
  margin-bottom: 6px; display: flex; align-items: center; gap: 8px;
}
.notice ul { padding-left: 18px; list-style: disc; }
.notice li { font-size: 13px; line-height: 21px; color: var(--text-muted); margin-bottom: 4px; }
.notice li:last-child { margin-bottom: 0; }
.notice p { font-size: 13px; line-height: 21px; color: var(--text-muted); }
.notice__footnote { font-size: 12px; color: var(--text-soft); margin-top: 8px; }

.notice--warning { background: var(--warn-soft); border-color: var(--warn-border); }
.notice--warning h2, .notice--warning h3 { color: var(--warn); }
.notice--info { background: var(--info-soft); border-color: var(--info-border); }
.notice--info h2 { color: var(--info); }
.notice--error { background: var(--danger-soft); border-color: var(--danger-border); text-align: center; }
.notice--error h2 { color: var(--danger); justify-content: center; }
.notice--error .btn { margin-top: 10px; }

.state-block { text-align: center; padding: 36px 0; }
.state-block p { color: var(--text-muted); font-size: 13px; margin-top: 10px; }

/* ═══════════════════════════════════════════════════════════════
   HOME — service picker
   ═══════════════════════════════════════════════════════════════ */
/* One rhythm for the whole page: every block below the hero is a
   .home-section, so the gaps between them never drift apart. */
.home-section { margin-bottom: 52px; }

/* ─── Hero ──────────────────────────────────────────────────────
   The banner above is a photograph and the body used to start as bare
   text on white, which read as two unrelated pages stacked. The tinted
   panel is the bridge: it carries the banner's green down into the
   content and gives the first screen something to look at. */
/* คอลัมน์ขวาได้ 1.02fr คือกว้างกว่าคอลัมน์ข้อความนิดเดียว ตามชุดออกแบบเดสก์ท็อป
   รูปโปรโมตเป็นสิ่งที่คนเห็นก่อนบนจอกว้าง จึงไม่ถูกบีบให้เล็กกว่าตัวหนังสือ
   เส้นเขียว 3px ด้านบนเป็นตัวเดียวที่ให้สีกับการ์ด พื้นหลังจึงไล่จากเทาอ่อน
   ขึ้นขาวเฉย ๆ ไม่มีเขียวเจือ — ที่ 1152px การ์ดนี้กินพื้นที่เกือบเต็มจอ
   พื้นเขียวทั้งใบเลยหนักเกินไป */
.home-hero {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: 40px; align-items: center;
  border: 1px solid var(--border);
  border-top: 3px solid var(--green);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--surface) 0%, var(--white) 58%);
  padding: 44px 40px;
  margin-bottom: 52px;
}
.home-hero > * { position: relative; }

.home-hero__eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: var(--r-full);
  background: var(--white); border: 1px solid var(--green-soft-border);
  color: #067A4A; box-shadow: var(--shadow-xs);
  font-size: 13px; line-height: 18px; font-weight: 600;
  margin-bottom: 22px;
}

.home-hero__title {
  font-size: clamp(36px, 4.8vw, 56px); line-height: 1.08;
  font-weight: 700; color: var(--green-deep);
}
/* ชื่อแบรนด์เป็นอักษรละติน ไม่ใช่ไทย และ Noto Serif Thai ออกแบบชุดไทยเป็นหลัก
   ละตินเป็นชุดรอง — ที่ 16px ไม่มีใครเห็น แต่พอขยายเป็น 52px มันเห็น
   IBM Plex Sans ตัวหนาเป็นชุดที่ละตินคือของหลัก และเป็นฟอนต์เดียวกับเนื้อความ
   ทั้งเว็บ หน้านี้เลยเหลือระบบตัวอักษรชุดเดียว

   ใช้ --font ไม่ใช่ --font-hero ทั้งที่ token นั้นตั้งไว้สำหรับคำนี้ เพราะ
   --font-hero ขึ้นต้นด้วย Poppins ซึ่งยังไม่ได้โฮสต์เอง เครื่องที่บังเอิญมี
   Poppins ติดอยู่จะเห็นโลโก้คนละแบบกับคนอื่น ซึ่งเป็นสิ่งที่ชื่อแบรนด์รับไม่ได้
   ถ้าวันหนึ่งเอา Poppins มาไว้ใน assets/fonts เหมือนอีกสองตระกูล ค่อยเปลี่ยน
   บรรทัดนี้เป็น var(--font-hero) แล้วมันจะตรงกันทุกเครื่อง

   ไล่ระยะเข้าหากันเพราะระยะปกติของ Plex ตั้งมาเพื่อข้อความยาว ๆ พอเอามาทำคำ
   เดียวที่ 52px มันหลวม — หัวข้อภาษาไทยยังเป็น serif เหมือนเดิม ตรงนั้น
   Noto Serif Thai ทำงานที่มันถูกออกแบบมาทำ */
.home-hero__brand {
  display: block;
  font-family: var(--font);
  letter-spacing: -.035em;
}
.home-hero__tagline {
  display: block; margin-top: 10px;
  font-family: var(--font);
  font-size: clamp(18px, 2.2vw, 25px); line-height: 1.45;
  font-weight: 600; letter-spacing: 0; color: var(--text-body);
}
/* Not italics — em is here to mark the one word the service is about. */
.home-hero__tagline em { font-style: normal; color: var(--green-deep); }

.home-hero__lead {
  margin-top: 18px; max-width: 50ch;
  font-size: 16.5px; line-height: 1.8; color: var(--text-body);
}
.home-hero__actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 28px; }
/* ปุ่มคู่นี้เตี้ยกว่า .btn--lg ปกติ 4px และมุมโค้งกว่า 2px ตามชุดออกแบบ
   เดสก์ท็อป — ผูกไว้กับฮีโร่อย่างเดียว ไม่แตะ .btn--lg ที่หน้าอื่นใช้ร่วมกัน */
.home-hero__actions .btn {
  height: 48px; padding: 0 22px;
  border-radius: 10px; font-size: 15.5px;
}

/* เส้นคั่นเหนือแถวติ๊กถูก: สามข้อนี้เป็นคำรับรอง ไม่ใช่ส่วนหนึ่งของปุ่ม
   เส้นบางกับระยะหายใจจึงแยกมันออกจาก call to action ด้านบน */
.home-hero__points {
  display: flex; flex-wrap: wrap; gap: 8px 24px;
  margin-top: 26px; padding-top: 22px;
  border-top: 1px solid var(--border);
}
.home-hero__points li {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; color: var(--text-muted);
}
.home-hero__points i { color: var(--green); font-size: 13px; }

/* LINE promo — the channel the banner points at, kept in the hero so it
   stays reachable without scrolling past the whole page. */
.home-hero__promo { display: flex; flex-direction: column; gap: 14px; }
.home-hero__promo-art {
  display: block; overflow: hidden;
  border: 1px solid var(--green-soft-border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
  transition: box-shadow .18s, transform .18s;
}
.home-hero__promo-art:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
/* width/height อยู่บน img แล้ว เบราว์เซอร์จึงกันที่ตามสัดส่วนไว้ก่อนรูปมาถึง
   หน้าไม่กระโดดตอนโหลดเสร็จ */
.home-hero__promo-art img { width: 100%; height: auto; }
.home-hero__promo-foot { display: flex; flex-direction: column; align-items: center; gap: 4px; }
/* ปุ่มมีสามส่วน (ไอคอน LINE / ข้อความ / ไอคอนลิงก์ออก) พอจอแคบลงมันจะเบียดกัน
   ให้ห่อบรรทัดได้และสูงตามเนื้อหา แทนที่จะบีบตัวอักษรจนล้นออกนอกปุ่ม */
.home-hero__promo-foot .btn {
  height: auto; min-height: var(--control-md);
  padding-top: 10px; padding-bottom: 10px;
  white-space: normal; flex-wrap: wrap;
}
.home-hero__promo-more {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: var(--control-sm);
  font-size: 13px; font-weight: 600; color: var(--text-muted);
}
.home-hero__promo-more:hover { color: var(--green-hover); }
.home-hero__promo-more i { font-size: 10px; }

/* ─── Service cards ─────────────────────────────────────────────
   Three cards, three hues, all from the palette already in use: brand
   green, gold and the info blue that carries weather everywhere else.
   --card-accent is decorative only (rule, border, chip fill); text takes
   --card-ink, which is the darkened pair that actually holds 4.5:1. */
/* Explicit tracks, not auto-fit. Three cards in a two-column auto-fit grid
   leave a hole in the bottom-right corner, and auto-fit cannot be told to
   close it — the breakpoints below hand the odd card the whole row instead.
   Columns are 1fr, so no track can ever be wider than the container. */
.service-grid {
  display: grid; gap: 18px;
  grid-template-columns: repeat(3, 1fr);
}
.service-card {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: flex-start;
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--r-xl); padding: 22px;
  color: inherit; text-decoration: none;
  transition: border-color .18s, box-shadow .18s, transform .18s;
}
.service-card::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px;
  background: var(--card-accent, var(--green));
  transform: scaleX(0); transform-origin: left;
  transition: transform .28s ease;
}
.service-card:hover {
  border-color: var(--card-accent, var(--green));
  box-shadow: var(--shadow-md);
  transform: translateY(-3px);
  color: inherit;
}
.service-card:hover::before, .service-card:focus-visible::before { transform: scaleX(1); }

.service-card--scan {
  --card-accent: var(--green); --card-ink: #067A4A;
  --card-tint: var(--green-soft);
}
.service-card--chat {
  --card-accent: var(--gold); --card-ink: var(--gold-ink);
  --card-tint: var(--gold-soft);
}
.service-card--weather {
  --card-accent: var(--info); --card-ink: var(--info);
  --card-tint: var(--info-soft);
}

.service-card__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; width: 100%; margin-bottom: 18px;
}
.service-card__icon {
  width: 50px; height: 50px; flex: none;
  border-radius: var(--r-md);
  background: var(--card-tint, var(--green-soft));
  color: var(--card-ink, var(--green-deep));
  font-size: 21px;
  display: inline-flex; align-items: center; justify-content: center;
}
.service-card h3 { font-size: 19px; line-height: 28px; margin-bottom: 8px; }
.service-card p {
  color: var(--text-muted); font-size: 14.5px; line-height: 23px;
  margin-bottom: 18px; flex-grow: 1;
}
.service-card__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; width: 100%;
  padding-top: 14px; border-top: 1px solid var(--border);
  color: var(--card-ink, var(--green-deep));
  font-size: 14.5px; font-weight: 600;
}
.service-card__go {
  width: 34px; height: 34px; flex: none;
  border-radius: var(--r-full);
  background: var(--card-tint, var(--green-soft));
  color: var(--card-ink, var(--green-deep));
  font-size: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background-color .18s, color .18s, transform .18s;
}
/* Fills with the *ink*, not the accent: white on the gold accent fails
   contrast, while all three inks clear it. */
.service-card:hover .service-card__go {
  background: var(--card-ink, var(--green-deep)); color: #fff;
  transform: translateX(3px);
}

/* ─── How it works ──────────────────────────────────────────── */
.how-steps {
  display: grid; gap: 18px;
  grid-template-columns: repeat(3, 1fr);
}
.how-step {
  position: relative;
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--r-xl); padding: 22px;
}
.how-step__no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; margin-bottom: 14px;
  border-radius: var(--r-full);
  background: var(--green-deep); color: #fff;
  font-family: var(--font-display); font-size: 17px; font-weight: 700;
  line-height: 1;
}
.how-step h3 { font-size: 16px; line-height: 25px; margin-bottom: 6px; }
.how-step p { font-size: 14px; line-height: 23px; color: var(--text-muted); }

/* ─── Recent activity ───────────────────────────────────────── */
.recent__empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  background: linear-gradient(180deg, var(--surface) 0%, var(--white) 100%);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-xl); padding: 32px 24px;
}
.recent__empty-icon {
  width: 54px; height: 54px; margin-bottom: 14px;
  border-radius: var(--r-full);
  background: var(--white); border: 1px solid var(--border);
  color: var(--text-soft); font-size: 20px;
  display: flex; align-items: center; justify-content: center;
}
.recent__empty p {
  color: var(--text-muted); font-size: 14.5px; line-height: 23px;
  max-width: 52ch; margin-bottom: 16px;
}
.recent__empty-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }

.recent__grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
}
.recent-card {
  display: flex; gap: 16px; align-items: flex-start;
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--r-xl); padding: 20px;
  transition: border-color .18s, box-shadow .18s;
}
.recent-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.recent-card__icon {
  width: 42px; height: 42px; flex: none;
  border-radius: var(--r-md); font-size: 17px;
  display: flex; align-items: center; justify-content: center;
}
.recent-card--scan .recent-card__icon { background: var(--green-soft); color: var(--green-deep); }
.recent-card--weather .recent-card__icon { background: var(--info-soft); color: var(--info); }
.recent-card__body { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.recent-card__kind {
  font-size: 12px; line-height: 16px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-soft);
}
.recent-card__title { font-size: 17px; line-height: 25px; font-weight: 600; color: var(--forest); }
.recent-card__meta { font-size: 13px; line-height: 20px; color: var(--text-muted); }
.recent-card__actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

/* ─── Developer strip ───────────────────────────────────────── */
.dev-strip {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: 1fr 1fr; gap: 32px;
  align-items: center;
  background:
    radial-gradient(88% 130% at 100% 0%, rgba(0, 185, 107, .26) 0%, transparent 58%),
    linear-gradient(140deg, #1C5142 0%, var(--forest) 52%, #0E2C23 100%);
  color: #fff;
  border-radius: var(--r-xl); padding: 32px;
  margin-bottom: 28px;
}
.dev-strip::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: radial-gradient(circle, rgba(255,255,255,.09) 1px, transparent 1px);
  background-size: 24px 24px;
}
.dev-strip > * { position: relative; }
.dev-strip__eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 13px; border-radius: var(--r-full);
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.22);
  color: rgba(255,255,255,.92);
  font-size: 12px; line-height: 18px; font-weight: 600; letter-spacing: .04em;
  margin-bottom: 14px;
}
.dev-strip h2 { color: #fff; font-size: 21px; line-height: 30px; margin-bottom: 8px; }
.dev-strip p { color: rgba(255,255,255,.78); font-size: 14.5px; line-height: 23px; margin-bottom: 20px; }
.dev-strip__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.btn--on-dark { background: var(--green); color: #fff; border-color: var(--green); }
.btn--on-dark:hover:not(:disabled) { background: var(--green-hover); border-color: var(--green-hover); color: #fff; }
.btn--outline-white { background: transparent; color: #fff; border-color: rgba(255,255,255,.38); }
.btn--outline-white:hover:not(:disabled) { background: rgba(255,255,255,.1); border-color: #fff; color: #fff; }

/* The snippet is a request, so it is framed as an editor pane — the bar
   is what says "this is a file", not a caption above it. */
.code-window {
  border: 1px solid rgba(255,255,255,.14); border-radius: var(--r-md);
  background: rgba(0,0,0,.3); overflow: hidden;
  box-shadow: 0 18px 40px rgba(0,0,0,.26);
}
.code-window__bar {
  display: flex; align-items: center; gap: 7px;
  padding: 10px 14px;
  background: rgba(255,255,255,.07);
  border-bottom: 1px solid rgba(255,255,255,.1);
}
.code-window__bar > span {
  width: 10px; height: 10px; border-radius: 50%;
  background: rgba(255,255,255,.26);
}
.code-window__bar em {
  margin-left: auto; font-style: normal;
  font-family: var(--font-mono); font-size: 11.5px; color: rgba(255,255,255,.58);
}
.code-window .code-block { border: 0; border-radius: 0; background: transparent; }

.code-block {
  background: rgba(0,0,0,.26); border: 1px solid rgba(255,255,255,.1);
  border-radius: var(--r-md); padding: 16px;
  font-family: var(--font-mono); font-size: 12.5px;
  color: rgba(255,255,255,.92); line-height: 1.75;
  overflow-x: auto; white-space: pre;
}
.code-block .hl-key { color: #7fe3b0; }
.code-block .hl-str { color: #f6c945; }
.code-block .hl-flag { color: #9fd0ea; }

/* ─── Closing disclaimer ────────────────────────────────────────
   Same .notice--warning as everywhere else; home only moves the icon out
   of the heading and onto a plate, because this list is four items long
   and needs a shape that reads at a glance. */
.home-notice {
  display: flex; gap: 18px; align-items: flex-start;
  border-radius: var(--r-xl); padding: 20px 22px;
}
.home-notice__icon {
  width: 44px; height: 44px; flex: none;
  border-radius: var(--r-full);
  background: var(--white); border: 1px solid var(--warn-border);
  color: var(--warn); font-size: 18px;
  display: flex; align-items: center; justify-content: center;
}
.home-notice__body { min-width: 0; flex: 1; }
.home-notice h2 { font-size: 16px; line-height: 25px; margin-bottom: 10px; }
.home-notice ul {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 2px 28px;
}
.home-notice li { font-size: 13.5px; line-height: 22px; }

/* ═══════════════════════════════════════════════════════════════
   IMAGE PROCESSING
   ═══════════════════════════════════════════════════════════════ */
.upload-panel { padding: 0; }
.drop-zone {
  border: 2px dashed var(--border-strong);
  border-radius: var(--r-xl);
  padding: 52px 24px; text-align: center;
  background: var(--surface); cursor: pointer;
  transition: border-color .18s, background-color .18s;
}
.drop-zone:hover, .drop-zone.dragging {
  border-color: var(--green); background: var(--green-soft);
}
.drop-zone__icon {
  width: 56px; height: 56px; margin: 0 auto 16px;
  border-radius: var(--r-full); background: var(--white);
  border: 1px solid var(--border);
  color: var(--green); font-size: 22px;
  display: flex; align-items: center; justify-content: center;
}
.drop-zone h3 { font-size: 17px; margin-bottom: 6px; }
.drop-zone p { color: var(--text-muted); font-size: 14px; margin-bottom: 18px; }
.drop-zone__actions { display: flex; gap: 8px; justify-content: center; margin-bottom: 14px; flex-wrap: wrap; }
.drop-zone__hint { font-size: 12px; color: var(--text-soft); }

.photo-tips { margin-top: 16px; }
.photo-tips h4 { margin-bottom: 12px; }
.photo-tips ol { counter-reset: tip; display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px 24px; }
.photo-tips li {
  counter-increment: tip; display: flex; gap: 10px;
  font-size: 13px; line-height: 20px; color: var(--text-muted);
}
.photo-tips li::before {
  content: counter(tip);
  min-width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  background: var(--green-soft); color: var(--green);
  border-radius: var(--r-full); font-size: 11px; font-weight: 700;
  flex-shrink: 0;
}

/* Selected: preview on the left, file facts + action on the right */
.selected-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.selected-grid__image {
  background: var(--surface); border-right: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  padding: 20px; min-height: 260px;
}
.selected-grid__image img { max-height: 300px; width: auto; border-radius: var(--r-md); object-fit: contain; }
.selected-grid__body { padding: 24px; display: flex; flex-direction: column; }
.selected-grid__body h3 { margin-bottom: 4px; }
.file-facts { margin: 16px 0 24px; }
.file-facts dt {
  font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-soft); font-weight: 600; margin-bottom: 2px;
}
.file-facts dd { font-size: 15px; color: var(--text); margin-bottom: 12px; word-break: break-all; }
.selected-grid__actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: auto; }

/* Result — appended below, never replaces the upload area */
.result-section { margin-top: 32px; }
.result-hero {
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--r-xl); padding: 24px;
}
.result-hero__kicker { margin-bottom: 10px; }
.result-hero__label {
  font-size: 26px; line-height: 34px; font-weight: 700;
  color: var(--forest); margin-bottom: 4px;
}
.result-hero__sub { color: var(--text-muted); font-size: 14px; margin-bottom: 18px; }
.confidence-row { display: flex; align-items: center; gap: 16px; }
.confidence-bar {
  flex: 1; height: 12px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-full); overflow: hidden;
}
.confidence-bar__fill {
  height: 100%; background: var(--green);
  border-radius: var(--r-full); transition: width .6s ease;
}
.confidence-value { font-size: 24px; font-weight: 700; color: var(--green); min-width: 76px; text-align: right; }
.confidence-label { font-size: 12px; color: var(--text-soft); margin-top: 8px; }

.result-list { margin-top: 16px; }
.result-row {
  display: grid; grid-template-columns: 1fr 90px 56px;
  gap: 12px; align-items: center;
  padding: 10px 0; border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.result-row:last-child { border-bottom: none; }
.result-row__name { color: var(--text); }
.result-row__bar { height: 6px; background: var(--surface); border-radius: var(--r-full); overflow: hidden; }
.result-row__fill { height: 100%; background: var(--border-strong); border-radius: var(--r-full); }
.result-row--top .result-row__fill { background: var(--green); }
.result-row--top .result-row__name { font-weight: 600; color: var(--forest); }
.result-row__pct { text-align: right; color: var(--text-muted); font-size: 13px; }

.guidance-box {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 16px; margin-top: 16px;
}
.guidance-box h4 { margin-bottom: 6px; }
.guidance-box p { font-size: 14px; line-height: 22px; color: var(--text-muted); }
.guidance-box details { margin-top: 8px; }
.guidance-box summary { cursor: pointer; font-size: 13px; color: var(--green); font-weight: 600; }
.guidance-box details p { font-size: 13px; margin-top: 6px; }
.guidance-box__notice { font-size: 12px; color: var(--warn); margin-top: 8px; }

.result-section .section-title { margin-top: 24px; }
.result-section .result-hero + .section-title,
.result-section > .section-title:first-child { margin-top: 0; }

.guidance-actions {
  padding-left: 20px; list-style: decimal; margin-top: 12px;
  color: var(--text-muted); font-size: 14px; line-height: 23px;
}
.guidance-actions strong { color: var(--text); }

.result-cta {
  display: flex; gap: 10px; flex-wrap: wrap;
  margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--border);
}
.result-disclaimer { font-size: 12px; color: var(--text-soft); margin-top: 14px; }

/* ═══════════════════════════════════════════════════════════════
   DOODURIAN AI (chat) — no sidebar; ask straight away
   ═══════════════════════════════════════════════════════════════ */
.chat-wrap { max-width: 880px; margin: 0 auto; }

.chat-head {
  display: flex; align-items: center; gap: 14px; margin-bottom: 20px;
  flex-wrap: wrap;
}
/* Takes the slack so the clear button lands on the right, and drops to its own
   row rather than squeezing the title on a narrow phone. */
.chat-head__text { flex: 1 1 220px; }
.chat-head #chatClearBtn { flex: none; }
.chat-head__avatar {
  position: relative; flex-shrink: 0;
  width: 46px; height: 46px;
}
.chat-head__avatar img {
  width: 100%; height: 100%; display: block;
  border-radius: var(--r-full);
  border: 1.5px solid var(--green-soft-border);
  /* โลโก้เป็นพื้นโปร่ง ตัวการ์ตูนยืนอยู่บนใบไม้ของตัวเอง — วางบนพื้นขาวถึงจะไม่ขุ่น */
  background: var(--white);
}
.chat-head__dot {
  position: absolute; right: 0; bottom: 0;
  width: 13px; height: 13px; border-radius: var(--r-full);
  background: var(--green); border: 2px solid var(--white);
}
.chat-head h1 { font-size: 22px; line-height: 30px; margin-bottom: 2px; }
.chat-head p { font-size: 13px; color: var(--text-muted); }

/* Context bar */
.context-bar {
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 14px 16px; margin-bottom: 12px;
}
.context-bar__header {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin-bottom: 10px;
}
.context-bar__title {
  font-size: 12px; line-height: 16px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-soft);
}
.context-bar__hint { font-size: 12px; line-height: 18px; color: var(--text-soft); margin-top: 10px; }
.context-bar__hint--warn { color: var(--warn); font-weight: 600; }

.context-add { display: flex; flex-wrap: wrap; gap: 8px; }
.context-add__btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: var(--r-md);
  border: 1px dashed var(--border-strong); background: var(--white);
  font-size: 13px; font-weight: 500; color: var(--text-muted);
  cursor: pointer; text-decoration: none;
  transition: border-color .15s, background-color .15s, color .15s;
}
.context-add__btn:hover { border-color: var(--green); background: var(--green-soft); color: var(--forest); }
.context-add__btn i { color: var(--green); }
.context-add__btn small { color: var(--text-soft); font-size: 12px; }

.context-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.context-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px; border-radius: var(--r-md);
  font-size: 13px; font-weight: 500; border: 1px solid transparent;
}
.context-chip i { font-size: 12px; }
.context-chip--image { background: var(--green-soft); color: #067A4A; border-color: var(--green-soft-border); }
.context-chip--weather { background: var(--info-soft); color: var(--info); border-color: var(--info-border); }
.context-chip__remove {
  background: transparent; border: none; cursor: pointer;
  font-size: 13px; line-height: 1; padding: 0 2px;
  color: inherit; opacity: .65;
}
.context-chip__remove:hover { opacity: 1; }

/* Conversation */
.chat-panel {
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--r-xl); display: flex; flex-direction: column;
  overflow: hidden;
}
.chat-messages {
  flex: 1; overflow-y: auto; padding: 20px;
  min-height: 320px; max-height: 52vh;
}
.chat-msg {
  margin-bottom: 14px; padding: 12px 16px;
  border-radius: var(--r-lg); max-width: 86%;
  font-size: 15px; line-height: 24px;
}
.chat-msg--user {
  background: var(--green); color: #fff;
  margin-left: auto; border-bottom-right-radius: 4px;
}
.chat-msg--bot {
  background: var(--surface); border: 1px solid var(--border);
  margin-right: auto; border-bottom-left-radius: 4px;
}
.chat-msg__sender { font-weight: 600; font-size: 12px; margin-bottom: 4px; display: block; }
.chat-msg--user .chat-msg__sender { color: rgba(255,255,255,.82); }
.chat-msg--bot .chat-msg__sender { color: var(--green); }

/* A failed turn must not read as an answer. */
.chat-msg--failed { background: var(--danger-soft); border-color: var(--danger-border); }
.chat-msg--failed .chat-msg__sender { color: var(--danger); }
.chat-msg__body { margin: 0; white-space: pre-wrap; }

/* ─── Waiting for the answer ────────────────────────────────────
   Pathumma can take several seconds. Empty space during that reads as "the
   send failed" as easily as "it is thinking", so the wait gets a shape. */
.chat-msg--thinking .chat-msg__body {
  display: flex; align-items: center; gap: 10px;
}
.thinking-dots { display: inline-flex; gap: 5px; align-items: center; }
.thinking-dots i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--green);
  animation: ddr-thinking 1.1s ease-in-out infinite;
}
/* Staggered, so it reads as a wave rather than three lights blinking together. */
.thinking-dots i:nth-child(2) { animation-delay: .16s; }
.thinking-dots i:nth-child(3) { animation-delay: .32s; }
@keyframes ddr-thinking {
  0%, 80%, 100% { opacity: .28; transform: translateY(0); }
  40%           { opacity: 1;   transform: translateY(-3px); }
}
.thinking-label { font-size: 13.5px; color: var(--text-muted); }

/* The global reduced-motion rule collapses animation *duration*, which would
   freeze the dots mid-wave at whatever opacity they happened to hold. Give them
   a resting state that still reads as "waiting" without moving. */
@media (prefers-reduced-motion: reduce) {
  .thinking-dots i { animation: none !important; opacity: .55; transform: none; }
}
.chat-msg__meta { font-size: 12px; color: var(--text-soft); margin-top: 8px; display: block; }
.chat-msg--user .chat-msg__meta { color: rgba(255,255,255,.7); }
.chat-msg__actions { display: flex; gap: 4px; margin-top: 10px; }

.chat-suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 14px; }
.suggestion {
  padding: 8px 14px; border-radius: var(--r-full);
  border: 1px solid var(--border-strong); background: var(--white);
  font-size: 13px; color: var(--text-muted); cursor: pointer;
  transition: border-color .15s, background-color .15s, color .15s;
}
.suggestion:hover { border-color: var(--green); background: var(--green-soft); color: var(--forest); }

.context-attribution {
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--border); font-size: 12px;
}
.context-attribution__title {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 600; color: var(--text-soft); margin-bottom: 6px;
}
.context-attribution__row {
  display: flex; align-items: baseline; gap: 8px;
  color: var(--text-muted); line-height: 18px;
}
.context-attribution__row + .context-attribution__row { margin-top: 4px; }
.context-attribution__row i { color: var(--green); font-size: 11px; }
.context-attribution__row--weather i { color: var(--info); }

/* Composer */
.chat-input-row {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 14px 16px; border-top: 1px solid var(--border);
  background: var(--white);
}
.chat-input-row textarea {
  flex: 1; padding: 10px 14px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  resize: none; min-height: 42px; max-height: 140px;
  font-size: 15px; line-height: 22px;
  transition: border-color .15s;
}
.chat-input-row textarea:focus { outline: none; border-color: var(--green); }
.chat-input-row .btn { flex-shrink: 0; }
.chat-disclaimer { font-size: 12px; color: var(--text-soft); margin-top: 10px; text-align: center; }

/* Context manager */
.context-modal {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(21,36,32,.45);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.context-modal[hidden] { display: none; }
.context-modal__content {
  background: var(--white); border-radius: var(--r-xl);
  padding: 22px; max-width: 480px; width: 100%;
  max-height: 86vh; overflow-y: auto;
  box-shadow: var(--shadow-md); border: 1px solid var(--border);
}
.context-modal__header {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 18px; border-bottom: 1px solid var(--border); padding-bottom: 12px;
}
.context-modal__header h3 { font-size: 17px; }
.context-modal__group { margin-bottom: 18px; }
.context-modal__group h4 {
  font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-soft); margin-bottom: 8px;
}
.context-modal__footer { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.context-option {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--r-md); margin-bottom: 6px;
  cursor: pointer; font-size: 14px; line-height: 21px;
  transition: background-color .15s, border-color .15s;
}
.context-option:hover { background: var(--surface); border-color: var(--green); }
.context-option__empty { font-size: 13px; color: var(--text-soft); padding: 8px 0; }

/* ═══════════════════════════════════════════════════════════════
   WEATHER
   ═══════════════════════════════════════════════════════════════ */
/* ─── Map pin ───────────────────────────────────────────────── */
.map-pick { margin-bottom: 14px; }
.map-pick__canvas {
  height: 320px; width: 100%;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface);
  /* Leaflet panes must not paint over the sticky service bar. */
  z-index: 0;
}
.map-pick__hint { font-size: 12px; color: var(--text-soft); margin: 0; }
.map-pick__down {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 14px; line-height: 1.55; color: var(--text);
  background: var(--warn-soft); border: 1px solid var(--warn-border);
  border-radius: var(--r-md); padding: 10px 12px; margin: 8px 0 0;
}

/* ── Radar readout ─────────────────────────────────────────────
   Sits under the radar map: the frame time, its age, and the scale. */
.radar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-top: 10px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 10px 12px; background: var(--surface);
}
.radar__status { font-size: 13px; line-height: 1.55; color: var(--text-muted); margin: 0; }
.radar__legend { min-width: 180px; }
.radar__legend-title {
  display: block; font-size: 11px; font-weight: 600;
  color: var(--text-soft); margin-bottom: 4px;
}
/* กรมอุตุนิยมวิทยา's own scale, read off the colour bar it draws on the plate —
   14 classes from 0.0 to >80 มม./ชม. The image is not recoloured, so the strip
   has to be TMD's colours or the legend lies about the map. */
.radar__scale {
  height: 10px; border-radius: 3px; border: 1px solid var(--border);
  background: linear-gradient(to right,
    #00ffff 0 7.14%, #0077c6 7.14% 14.29%, #003c6c 14.29% 21.43%,
    #0006f0 21.43% 28.57%, #009375 28.57% 35.71%, #00b347 35.71% 42.86%,
    #00e10c 42.86% 50%,    #46ff09 50% 57.14%,    #7cce02 57.14% 64.29%,
    #ffff00 64.29% 71.43%, #ff8000 71.43% 78.57%, #ff86ff 78.57% 85.71%,
    #fe45a2 85.71% 92.86%, #dd0000 92.86% 100%);
}
.radar__ticks {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--text-soft); margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
/* Radar cells are ~1 km squares. Zoomed in past the source resolution the
   browser would blur them into gradients that suggest detail the radar never
   measured, so the blocks stay blocks. */
.radar-overlay { image-rendering: pixelated; }

/* ชื่อจังหวัดบนแผนที่สำรอง
   Leaflet ตั้ง .leaflet-div-icon เป็นกล่องขอบขาวมาให้ ต้องล้างออกก่อน ไม่งั้นจะได้
   กรอบสี่เหลี่ยมรอบทุกชื่อ

   เงาขาวรอบตัวอักษรสี่ทิศ ไม่ใช่พื้นทึบ เพราะป้ายพวกนี้อยู่ทับชั้นฝน พื้นทึบจะบัง
   ข้อมูลที่คนมาดู ส่วนตัวอักษรสีเข้มเปล่า ๆ จะจมหายในสีน้ำเงินเข้มของฝนหนัก
   transform: translate(-50%, -50%) เพราะ iconSize: null ทำให้ Leaflet ไม่จัดจุด
   กึ่งกลางให้เอง จุดที่ปักคือมุมซ้ายบนของป้าย */
.radar-label {
  background: none; border: 0; width: auto !important; height: auto !important;
  white-space: nowrap; pointer-events: none;
}
.radar-label__text {
  display: inline-block; transform: translate(-50%, -50%);
  font-size: 11px; font-weight: 600; line-height: 1;
  color: #14351C;
  text-shadow:
     1px  0 0 rgba(255, 255, 255, .92), -1px  0 0 rgba(255, 255, 255, .92),
     0  1px 0 rgba(255, 255, 255, .92),  0 -1px 0 rgba(255, 255, 255, .92);
}
.map-pick__readout {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  background: var(--green-soft); border: 1px solid var(--green-soft-border);
  border-radius: var(--r-md); padding: 12px 14px; margin-bottom: 16px;
}
.map-pick__readout strong {
  display: block; font-size: 16px; color: var(--forest);
  font-variant-numeric: tabular-nums;
}
.map-pick__place { font-size: 13px; color: var(--text-muted); }

.weather-form { display: grid; gap: 12px; max-width: 520px; }
.field label {
  display: block; font-size: 16px; font-weight: 600;
  color: var(--text); margin-bottom: 8px;
}
.field__req { color: var(--danger); }
.field select, .field input, .field textarea {
  width: 100%; height: var(--control-lg); padding: 0 15px;
  border: 1.5px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--white); font-size: 16px; color: var(--text);
}
.field textarea { height: auto; min-height: 96px; padding: 12px 15px; line-height: 1.6; }
.field select:focus, .field input:focus, .field textarea:focus {
  outline: none; border-color: var(--green); border-width: 2px;
  box-shadow: 0 0 0 3px var(--green-soft-border);
}
.field__hint { font-size: 14px; line-height: 1.55; color: var(--text-muted); margin: 8px 0 0; }
/* Errors say what is wrong in words and repeat it in the border; the red on
   its own is not the message. */
.field--error select, .field--error input, .field--error textarea {
  border-color: var(--danger); background: var(--danger-soft);
}
.field--error .field__hint { color: var(--danger); }
.field--ok select, .field--ok input { border-color: var(--success); }
.field--ok .field__hint { color: var(--success-ink); }
.field :disabled {
  background: var(--surface); color: var(--disabled-border-ink);
  border-color: var(--border); cursor: not-allowed;
}

/* ─── EmptyState ────────────────────────────────────────────── */
/* One component for empty, error, offline, no-results and success. Only the
   icon, the words and the next action change. */
.empty {
  padding: 28px 24px; text-align: center;
  display: flex; flex-direction: column; justify-content: center;
}
.empty__icon {
  width: 58px; height: 58px; border-radius: 50%;
  background: var(--green-soft); color: var(--forest);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 22px; margin: 0 auto 14px;
}
.empty--error .empty__icon { background: var(--danger-soft); color: var(--danger); }
.empty--offline .empty__icon { background: var(--warn-soft); color: var(--warn); }
.empty--done .empty__icon { background: var(--success-soft); color: var(--success-ink); }
.empty__head { font-size: 18px; font-weight: 600; color: var(--text); margin: 0 0 7px; }
.empty__body { font-size: 15px; line-height: 1.65; color: var(--text-muted); margin: 0 0 18px; }
.empty .btn { margin: 0 auto; }

/* ─── Skeleton ──────────────────────────────────────────────── */
/* Blocks sit where the real content will land — no spinner in the middle of
   the screen, so the layout never jumps when data arrives. */
@keyframes ddr-shimmer { 0%, 100% { opacity: .55 } 50% { opacity: 1 } }
.sk {
  background: var(--surface); border-radius: var(--r-sm);
  animation: ddr-shimmer 1.4s ease-in-out infinite;
}
.sk-row { display: flex; gap: 13px; }
.sk-row__thumb { width: 72px; height: 72px; flex: none; }
.sk-row__lines { flex: 1; display: flex; flex-direction: column; gap: 9px; }
.weather-form__or {
  display: flex; align-items: center; gap: 12px;
  color: var(--text-soft); font-size: 13px;
}
.weather-form__or::before, .weather-form__or::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.weather-form__actions { display: flex; gap: 8px; flex-wrap: wrap; }

.weather-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.weather-metric {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 16px; text-align: center;
}
.weather-metric__label {
  font-size: 12px; letter-spacing: .04em; color: var(--text-soft);
  margin-bottom: 4px; display: flex; align-items: center; justify-content: center; gap: 6px;
}
.weather-metric__value { font-size: 22px; line-height: 30px; font-weight: 700; color: var(--forest); }
.weather-metric__sub { font-size: 12px; color: var(--text-soft); margin-top: 2px; }

.weather-grid-point {
  font-size: 12px; color: var(--text-soft); margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

/* ─── Forecast strip — one card per hour or per day ──────────────
   Hours and days use the same card so a grower reads them the same way; only
   the top line differs (a clock time vs a weekday). Scrolls sideways rather
   than wrapping, so the sequence stays a sequence. */
.wx-strip {
  display: flex; gap: 10px;
  overflow-x: auto; scrollbar-width: thin;
  padding: 4px 2px 12px;
  scroll-snap-type: x proximity;
}
.wx-card {
  flex: 0 0 132px;
  scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 14px 10px 16px;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--white); text-align: center;
}
/* The card for right now, or for today, is the one being acted on. */
.wx-card--now {
  border-color: var(--green); background: var(--green-soft);
  box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
}
.wx-card__head { font-size: 15px; font-weight: 600; color: var(--forest); line-height: 1.3; }
.wx-card__sub {
  font-size: 12px; color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}
.wx-card__icon { font-size: 30px; line-height: 1; margin: 2px 0; }
/* Colour carries the same meaning as the wording, never instead of it. */
.wx-card__icon--sun { color: #E8A33D; }
.wx-card__icon--cloud { color: #8A98A5; }
.wx-card__icon--rain { color: #2E7CC4; }
.wx-card__icon--storm { color: #6A4FB6; }
.wx-card__icon--cold { color: #4A9BC4; }
.wx-card__icon--hot { color: #D9663D; }

/* Temperature: high then low, the way every forecast in Thailand prints it. */
.wx-card__temps {
  display: flex; align-items: baseline; justify-content: center; gap: 8px;
  font-variant-numeric: tabular-nums;
}
.wx-card__temp { font-size: 20px; font-weight: 700; color: var(--forest); }
.wx-card__temp--max { color: #C6552F; }
.wx-card__temp--min { color: #2E7CC4; font-size: 17px; }
.wx-card__temp-sep { color: var(--border-strong); font-weight: 400; }

.wx-card__desc { font-size: 13px; color: var(--text); line-height: 1.45; min-height: 2.9em; }
.wx-card__facts {
  display: flex; flex-direction: column; gap: 3px;
  font-size: 12px; color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.wx-card__fact { display: flex; align-items: center; justify-content: center; gap: 5px; }
.wx-card__fact i { width: 13px; color: var(--text-soft); }
.wx-card__fact--rain { color: var(--info); font-weight: 600; }
.wx-card__fact--humid-high { color: var(--warn); font-weight: 600; }
/* TMD reports the direction the wind comes from; the arrow is rotated to
   point the way the wind is going, which is that bearing plus 180°. */
.wx-card__wind-arrow { display: inline-block; }

.wx-strip__legend { font-size: 12px; color: var(--text-soft); margin: 4px 0 0; line-height: 1.6; }
.wx-strip__warn { color: var(--warn); }

/* Nothing to draw, with the reason in words rather than an empty frame. */
.wx-empty {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 16px; margin-top: 4px;
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background: var(--surface);
}
.wx-empty i { color: var(--text-soft); margin-top: 2px; }
.wx-empty p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--text-muted); }

/* ─── Radar card ────────────────────────────────────────────────
   Separate from the pin picker: it answers "is it raining on me now" rather
   than "where is my orchard", and it must survive the picker collapsing. */
.radar-card__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}
.radar-card__head .section-title { margin-bottom: 4px; }
.radar-card__sub { font-size: 13px; color: var(--text-muted); margin: 0; line-height: 1.55; }
.radar-card__canvas { margin-top: 14px; }
#rainToggle[aria-pressed="true"] {
  background: var(--green-soft); border-color: var(--green); color: var(--forest);
}

.weather-updated { font-size: 12px; color: var(--text-soft); margin-top: 16px; }
.weather-note { font-size: 12px; color: var(--text-soft); margin-top: 6px; }

/* ─── Collapsed location bar ────────────────────────────────── */
/* Once a forecast is loaded the map has done its job; a returning grower
   should land on their data, not on the setup again. */
.pin-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--green-soft); border: 1px solid var(--green-soft-border);
  border-radius: var(--r-lg); padding: 10px 14px; margin-bottom: 16px;
}
.pin-bar__icon {
  width: 30px; height: 30px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-full); background: var(--white); color: var(--green);
}
.pin-bar__text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.pin-bar__text strong { font-size: 15px; color: var(--forest); }
.pin-bar__text span {
  font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.pin-bar__actions { display: flex; gap: 6px; flex-shrink: 0; }

/* ─── Section jump nav ──────────────────────────────────────── */
.section-nav {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
  position: sticky; top: 0; z-index: 20;
  background: var(--white);
  padding: 10px 0; margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.section-nav::-webkit-scrollbar { display: none; }
.section-nav__item {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px; border-radius: var(--r-full);
  border: 1px solid var(--border-strong); background: var(--white);
  font-size: 13px; font-weight: 600; color: var(--text-muted);
  white-space: nowrap; text-decoration: none;
  transition: background-color .15s, border-color .15s, color .15s;
}
.section-nav__item:hover { border-color: var(--green); color: var(--forest); }
.section-nav__item.active {
  background: var(--green); border-color: var(--green); color: #fff;
}
.section-nav__item.active i { color: #fff; }
.section-nav__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--r-full); background: var(--danger); color: #fff;
  font-size: 11px; font-weight: 700;
}
.section-nav__item.active .section-nav__count { background: rgba(255,255,255,.28); }

/* Each block gets its own heading so the page has landmarks. */
.wsection { margin-top: 28px; scroll-margin-top: 72px; }
/* จุดยึดของลิงก์ในเมนู วางเป็น span ขนาดศูนย์ไว้หัวการ์ด เพื่อไม่ต้องยุ่งกับ id
   เดิมของการ์ด และเผื่อระยะให้แถบด้านบนไม่ทับหัวข้อตอนกระโดดมา */
.anchor { display: block; height: 0; scroll-margin-top: 84px; }
/* หัวข้อของ .wsection กับ .advisory ใช้ .section-header เหมือนหน้าแรก — เส้นคาด
   ใต้หัวข้อคือสิ่งที่บอกว่าบล็อกใหม่เริ่มที่ไหน ซึ่งหน้านี้ต้องการมากกว่าหน้าอื่น
   เพราะมีแถบเมนูกระโดดมาลงตรงหัวข้อพอดี
   ต่างจากหัวข้อในการ์ด (ปักหมุด, เรดาร์) ที่ยังเป็น .section-title เพราะการ์ด
   มีกรอบของตัวเองอยู่แล้ว ใส่เส้นคาดซ้ำจะได้กรอบสองชั้น */
.advisory__head { scroll-margin-top: 72px; }

.result-cta--center { justify-content: center; border-top: none; margin-top: 32px; }

/* A one-line read of the current numbers, so the data and its meaning sit
   together rather than the explanation living only further down. */
.weather-readout {
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
  font-size: 14px; line-height: 23px; color: var(--text-muted);
  display: flex; gap: 10px; align-items: flex-start;
}
.weather-readout i { color: var(--green); margin-top: 4px; flex-shrink: 0; }

/* ─── Advisory cards ────────────────────────────────────────── */
.advisory { margin-top: 24px; }
.advisory__list { display: flex; flex-direction: column; gap: 12px; }

/* Three levels of text, three different jobs: the heading is the finding, the
   body is why, the list is what to do. They used to be the same 14px/23 on one
   tinted block, which read as a wall — nothing told the eye where to start. */
.advice {
  --advice-accent: var(--border-strong);
  border: 1px solid var(--border);
  border-left: 4px solid var(--advice-accent);
  border-radius: var(--r-lg);
  /* White body, tint only behind the header. A whole card of danger-soft with
     dense Thai text on it is heavy to read and shouts on every card equally. */
  background: var(--white);
  overflow: hidden;
}
.advice--act { --advice-accent: var(--danger); --advice-tint: var(--danger-soft); }
.advice--watch { --advice-accent: var(--warn); --advice-tint: var(--warn-soft); }
.advice--info { --advice-accent: #067A4A; --advice-tint: var(--green-soft); }
.advice--scan { --advice-accent: var(--info); --advice-tint: var(--info-soft); }

.advice__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 10px 14px 10px 16px;
  background: var(--advice-tint, var(--surface));
  border-bottom: 1px solid rgb(0 0 0 / 5%);
}
.advice__tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 700;
  color: var(--advice-accent);
}
/* No uppercase and no letter-spacing: Thai has no case, and tracking pulls
   apart the marks that sit above and below the base characters. */
.advice__tag i { font-size: 14px; }

.advice__dismiss {
  background: transparent; border: none; cursor: pointer;
  color: var(--text-soft); font-size: 14px; line-height: 1;
  /* 32px is a real tap target; 13px of glyph was not. */
  width: 32px; height: 32px; border-radius: var(--r-full);
  display: grid; place-items: center; flex-shrink: 0;
}
.advice__dismiss:hover { background: rgb(0 0 0 / 6%); color: var(--text); }

.advice__main { padding: 14px 18px 16px; }
/* Long Thai prose is hard to track past about 70 characters a line. */
.advice__main > * { max-width: 62ch; }

.advice h3 {
  font-size: 17px; line-height: 1.5; margin: 0 0 8px;
  color: var(--forest);
}
.advice__body { font-size: 14px; line-height: 1.7; color: var(--text-muted); margin: 0; }
.advice__scan-note {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; line-height: 1.5; color: var(--info);
  font-weight: 600; margin: 0 0 8px;
}

/* The list is the part a grower acts on, so it gets a label and the most
   space. Check marks rather than discs: it is a checklist, not prose. */
.advice__actions-label {
  display: block; font-size: 12px; font-weight: 700;
  color: var(--advice-accent); margin: 16px 0 8px;
}
.advice__actions { list-style: none; padding: 0; margin: 0; }
.advice__actions li {
  position: relative;
  padding: 7px 0 7px 28px;
  font-size: 14.5px; line-height: 1.65; color: var(--text);
  border-top: 1px solid var(--border);
}
.advice__actions li:first-child { border-top: none; padding-top: 0; }
.advice__actions li:last-child { padding-bottom: 0; }
.advice__actions li::before {
  content: "\f00c";  /* fa-check */
  font-family: "Font Awesome 6 Free"; font-weight: 900;
  position: absolute; left: 2px; top: 9px;
  font-size: 11px; color: var(--advice-accent);
}
.advice__actions li:first-child::before { top: 2px; }

.advice__basis { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 10px; }
.advice__basis summary {
  cursor: pointer; font-size: 12.5px; color: var(--text-muted); font-weight: 600;
  list-style: none; display: inline-flex; align-items: center; gap: 6px;
}
.advice__basis summary::-webkit-details-marker { display: none; }
.advice__basis summary::before {
  content: "\f105";  /* fa-chevron-right */
  font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 11px;
  transition: transform .15s ease;
}
.advice__basis[open] summary::before { transform: rotate(90deg); }
.advice__basis summary:hover { color: var(--text); }
.advice__basis p {
  font-size: 13px; line-height: 1.7; color: var(--text-muted);
  margin: 8px 0 0; max-width: 62ch;
}

.advisory__notes {
  list-style: disc; padding-left: 20px; margin-top: 14px;
}
.advisory__notes li {
  font-size: 12px; line-height: 19px; color: var(--text-soft); margin-bottom: 4px;
}

#advisoryMoreBtn { margin-top: 12px; }

/* ═══════════════════════════════════════════════════════════════
   ABOUT (reachable from the footer, not the service tabs)
   ═══════════════════════════════════════════════════════════════ */
/* ─── About page: คอลัมน์เดียวกลางหน้า ──────────────────────────
   ทุกบล็อกในหน้านี้กว้าง 760px (.story, .story-row, .about-section) แต่ .container
   กว้าง 1152px บนจอ 1680px จึงเหลือที่ว่างซ้าย 284px ขวา 636px — เนื้อหากองอยู่
   ซีกซ้ายของหน้า

   บีบ container ที่เดียว ไม่ใส่ margin auto ให้แต่ละบล็อก เพราะสองอย่าง: หัวเรื่อง
   ของหน้า (.page-head) กว้างเต็ม container ถ้าจัดกลางแค่บล็อกเนื้อหา หัวเรื่องจะ
   ไม่ตรงแนวกับเนื้อหาที่มันครอบอยู่ — เคยเป็นแบบนั้นมาแล้ว — และเพิ่มบล็อกใหม่
   ในหน้านี้ทีหลังจะได้แนวเดียวกันเองโดยไม่ต้องจำว่าต้องใส่ margin

   +gutter สองข้างเพื่อให้ตัวคอลัมน์กว้าง 760px พอดีเท่าที่บล็อกข้างในตั้งไว้ */
[data-page="about"] .page > .container {
  max-width: calc(760px + var(--gutter) * 2);
}

/* ─── Project story (about page) ────────────────────────────────
   Ported from the main site's about page. Wider than .about-section because
   these blocks carry images; the prose inside still caps at a readable measure. */
/* margin ซ้ายขวาเป็น 0 ไม่ใช่ auto — บล็อกนี้เต็มความกว้างคอลัมน์ของหน้าอยู่แล้ว
   คนที่จัดกลางคือ container ข้างบน ใส่ auto ที่นี่ซ้ำก็ไม่ได้อะไรเพิ่ม แต่ถ้าวันหนึ่ง
   คอลัมน์กว้างขึ้น บล็อกนี้จะลอยกลางอยู่ตัวเดียวขณะที่หัวเรื่องชิดซ้าย
   text-align กลางยังอยู่ เพราะโปสเตอร์กับหัวเรื่องเป็น hero ที่ควรกลางในคอลัมน์ */
.story { max-width: 760px; margin: 0 0 32px; text-align: center; }
.story__title {
  font-family: "Noto Serif Thai", serif;
  font-size: 26px; line-height: 1.45; color: var(--forest); margin-bottom: 18px;
}
.story__poster,
.awards__image {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-lg); border: 1px solid var(--border);
  margin: 0 auto 20px;
}
.awards__image { max-width: 560px; }
/* Padding-ratio box: reserves the height before the iframe loads, so the page
   below it does not jump once YouTube's frame arrives. */
.story__video {
  position: relative; width: 100%; padding-top: 56.25%;
  margin: 0 auto 22px; border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid var(--border); background: var(--surface);
}
.story__video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.story__lead {
  max-width: 62ch; margin: 0 auto;
  font-size: 15px; line-height: 1.8; color: var(--text-muted); text-align: left;
}

/* Image beside text, alternating sides down the feature list. */
.story-row {
  display: flex; gap: 22px; align-items: center;
  max-width: 760px; margin: 0 0 28px;
}
.story-row--feature { margin-bottom: 22px; }
.story-row--flip { flex-direction: row-reverse; }
.story-row h2 {
  font-size: 19px; line-height: 1.5; color: var(--forest); margin-bottom: 10px;
}
.story-row h3 { font-size: 17px; line-height: 1.5; color: var(--forest); margin-bottom: 8px; }
.story-row p {
  font-size: 14.5px; line-height: 1.75; color: var(--text-muted);
  max-width: 56ch; margin-bottom: 10px;
}
.story-row p:last-child { margin-bottom: 0; }
.story-row__image {
  flex: 0 0 230px; width: 230px; height: auto;
  border-radius: var(--r-lg); border: 1px solid var(--border);
}
/* Phone screenshots are tall; a narrower column keeps the row from towering. */
.story-row__image--phone { flex-basis: 170px; width: 170px; }

.story-quote {
  margin: 18px 0 0; padding: 2px 0 2px 16px;
  border-left: 3px solid var(--green);
}
.story-quote p {
  font-size: 15px; line-height: 1.8; color: var(--text); font-style: italic; margin: 0;
}
.story-quote cite {
  display: block; margin-top: 8px;
  font-style: normal; font-weight: 600; color: var(--forest); font-size: 14px;
}
.story-quote cite::before { content: "— "; }

.people { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.person {
  display: flex; gap: 14px; align-items: center;
  padding: 12px; border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface);
}
.person img {
  width: 74px; height: 74px; flex-shrink: 0;
  border-radius: var(--r-md); object-fit: cover;
}
.person strong { display: block; font-size: 15px; color: var(--text); }
.person span { display: block; font-size: 13px; line-height: 1.55; color: var(--text-muted); }

.awards__head {
  font-size: 16px; color: var(--forest); margin: 22px 0 10px;
  padding-left: 10px; border-left: 3px solid var(--green);
}

@media (max-width: 620px) {
  .story__title { font-size: 22px; }
  /* Stacked, and never reversed: on one column the image belongs above its text
     regardless of which side it took on a wide screen. */
  .story-row, .story-row--flip { flex-direction: column; align-items: flex-start; }
  .story-row__image, .story-row__image--phone { flex-basis: auto; width: 62%; align-self: center; }
  .person { align-items: flex-start; }
}

.about-section { margin-bottom: 32px; max-width: 760px; }
.about-section h2 {
  font-size: 18px; margin-bottom: 10px;
  padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.about-section > p { color: var(--text-muted); font-size: 15px; line-height: 26px; }
.about-list { display: flex; flex-direction: column; gap: 16px; margin-top: 14px; }
.about-item { display: flex; gap: 14px; align-items: flex-start; }
/* The 404 page reuses these rows as links. Keeping the heading's own colour so a
   row of five does not read as a wall of link-blue. */
a.about-item { color: inherit; text-decoration: none; padding: 8px; margin: -8px; border-radius: var(--r-md); }
a.about-item:hover { background: var(--surface); }
a.about-item:hover h3 { color: var(--forest); }
.about-item__icon {
  width: 38px; height: 38px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--green-soft); color: var(--green);
  border-radius: var(--r-md); font-size: 15px;
}
.about-item h3 { font-size: 15px; margin-bottom: 2px; }
.about-item p { color: var(--text-muted); font-size: 13px; line-height: 21px; }
.about-rules { list-style: disc; padding-left: 20px; margin-top: 12px; }
.about-rules li { color: var(--text-muted); font-size: 14px; line-height: 24px; margin-bottom: 6px; }
.tech-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 14px; }
.tech-item {
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 12px 14px; display: flex; flex-direction: column; gap: 2px;
}
.tech-item strong {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-soft); font-weight: 600;
}
.tech-item span { font-weight: 600; color: var(--forest); font-size: 14px; }
.about-cta { display: flex; gap: 8px; margin-top: 18px; flex-wrap: wrap; }

/* ═══════════════════════════════════════════════════════════════
   SERVICE FOOTER — full green footer, matching the main Doo-Durian site
   ═══════════════════════════════════════════════════════════════ */
.service-footer {
  background: linear-gradient(100deg, var(--footer-from) 0%, var(--footer-to) 100%);
  color: #fff;
  margin-top: 56px;
}
.service-footer .container { padding-top: 48px; padding-bottom: 32px; }

/* Five columns: the brand, the three areas the tab row leads to, and contact.
   The footer is the second way into every hub, so it carries the same map. */
.footer-grid {
  display: grid; grid-template-columns: 1.5fr repeat(3, 1fr) 1.15fr;
  gap: 32px 36px;
}

/* The mark, then the name under it, both centred. It used to be a Font Awesome
   leaf beside the name — a different symbol for the same brand as the top bar,
   on the same page. */
.footer-brand { text-align: center; }
.footer-brand h2 {
  color: #fff; font-size: 20px; line-height: 28px; font-weight: 700;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin-bottom: 12px;
}
/* Round, on a white plate: the logo art is transparent around a character that
   sits on its own foliage, which reads as muddy directly on the footer green. */
.footer-brand h2 img {
  width: 72px; height: 72px; border-radius: 50%;
  background: #fff; flex-shrink: 0;
}
.footer-brand p { color: rgba(255,255,255,.82); font-size: 14px; line-height: 24px; }

.service-footer h3 {
  color: #fff; font-size: 15px; font-weight: 700;
  margin-bottom: 14px;
}
.service-footer ul { display: flex; flex-direction: column; }
.service-footer li { font-size: 14px; line-height: 22px; }
/* 40px rows rather than the text's own 22px: on a phone the footer is where a
   grower reaches for a section, and a 22px row is not a target. */
.service-footer li a {
  color: rgba(255,255,255,.88);
  display: inline-flex; align-items: center; gap: 10px;
  min-height: var(--control-sm);
  transition: color .15s;
}
.service-footer li a:hover { color: #fff; text-decoration: underline; }
.service-footer li a i { width: 16px; text-align: center; opacity: .9; }

.footer-social { display: flex; gap: 10px; margin-top: 18px; }
.footer-social a {
  width: var(--touch-min); height: var(--touch-min); border-radius: var(--r-full);
  border: 1px solid rgba(255,255,255,.55); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px;
  transition: background-color .15s, border-color .15s;
}
.footer-social a:hover { background: rgba(255,255,255,.16); border-color: #fff; color: #fff; }

.footer-bottom {
  text-align: center; margin-top: 36px; padding-top: 22px;
  border-top: 1px solid rgba(255,255,255,.28);
  font-size: 13px; color: rgba(255,255,255,.85);
}

/* ─── Loading / toast ───────────────────────────────────────── */
.loading-spinner {
  display: inline-block; width: 20px; height: 20px;
  border: 2.5px solid var(--border); border-top-color: var(--green);
  border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
  background: var(--forest); color: #fff;
  padding: 10px 18px; border-radius: var(--r-md);
  font-size: 13px; font-weight: 500;
  box-shadow: var(--shadow-md); z-index: 400;
}

/* ═══════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════ */
/* The connector between the how-it-works cards is drawn only where all three
   share a row — the same 981px the grid switches at, so a card at the end of
   a two-up row can never trail a dash off the side of the container. It spans
   exactly the grid gap: left starts at the padding box's right edge, +1px
   clears the border. */
@media (min-width: 981px) {
  .how-step::after {
    content: ""; position: absolute;
    top: 41px; left: calc(100% + 1px); width: 18px;
    border-top: 2px dashed var(--green-soft-border);
  }
  .how-step:last-child::after { content: none; }
}

/* Two up, and the third card takes the whole row rather than sitting alone
   next to an empty cell. Three items in a two-column grid always leave that
   hole, and at this width a full-row card reads as the deliberate close of
   the set. */
@media (max-width: 980px) {
  .service-grid, .how-steps { grid-template-columns: repeat(2, 1fr); }
  .service-grid > :last-child, .how-steps > :last-child { grid-column: 1 / -1; }
}

/* The hero's side card needs its own width; below this it stops being a
   column and becomes the block that closes the hero. */
/* รูปโปรโมตหยุดเป็นคอลัมน์ตรงนี้ ต่ำกว่านี้สองคอลัมน์จะบีบทั้งพาดหัวและรูป
   จนแย่ทั้งคู่ พอวางเป็นแถวเดียวรูปได้เต็มความกว้าง ตัวหนังสือในรูปกลับมาอ่านออก
   แต่ไม่ปล่อยให้กว้างเกิน 560px เพราะบนแท็บเล็ตรูป 16:9 เต็มความกว้างจะสูงจน
   ดันปุ่มลงไปพ้นจอ */
@media (max-width: 1000px) {
  .home-hero { grid-template-columns: 1fr; gap: 28px; padding: 32px 28px; }
  .home-hero__promo { max-width: 560px; }
}

@media (max-width: 900px) {
  .dev-strip { grid-template-columns: 1fr; padding: 22px; }
  /* Two columns of Thai at this width turns every item into three lines. */
  .home-notice ul { grid-template-columns: 1fr; }
  .selected-grid { grid-template-columns: 1fr; }
  .selected-grid__image { border-right: none; border-bottom: 1px solid var(--border); min-height: 0; }
  .photo-tips ol { grid-template-columns: 1fr; }
  .tech-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 620px) {
  :root { --gutter: 16px; }

  /* Two and a bit cards at 375px, so the strip reads as scrollable without a
     scrollbar being visible: a card cut off at the edge is the affordance. */
  .wx-card { flex-basis: 124px; }
  .radar-card__head { flex-direction: column; align-items: stretch; }
  .radar-card__head .btn { justify-content: center; }

    /* Wrap rather than force a column: the brand plus a single action button fit
     one row at 375px, and only the pages carrying two buttons drop to a second
     row. Stacking unconditionally cost every page a row of header it not need. */
  .service-bar__top { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 12px; }
  .service-actions { justify-content: flex-start; flex-wrap: wrap; }
  .service-tab { padding: 0 10px; font-size: 14px; }
  .service-tab span { display: inline; }

  .page { padding: 24px 0 40px; }
  h1 { font-size: 23px; line-height: 32px; }
  .card { padding: 16px; }
  .drop-zone { padding: 36px 16px; }

  /* Home: the hero panel loses its inset, and both hero CTAs go full width —
     at 375px they wrap to two rows anyway, and a ragged pair reads worse
     than a stack. */
  .home-hero { padding: 26px 20px; margin-bottom: 40px; }
  .home-hero__actions { flex-direction: column; align-items: stretch; }
  .home-hero__promo { max-width: none; }
  /* ที่ 375px ป้ายเต็มยาวเกินปุ่มไปนิดเดียว พอห่อบรรทัดแล้วเหลือไอคอนลิงก์ออก
     ห้อยอยู่บรรทัดล่างตัวเดียว ตัด @doo_durian ออกดีกว่า — คนที่กดบนมือถือ
     ไม่ได้ต้องการชื่อบัญชีไว้พิมพ์ตาม เขากดแล้วเข้า LINE เลย */
  .home-hero__promo-handle { display: none; }
  .service-grid, .how-steps { grid-template-columns: 1fr; }
  .home-section { margin-bottom: 40px; }
  .home-notice { padding: 18px; gap: 14px; }

  .weather-grid { grid-template-columns: 1fr; }

  /* The place name and coordinates get a row of their own rather than
     wrapping into two lines each next to the buttons. */
  .pin-bar { align-items: flex-start; }
  .pin-bar__text { flex-basis: calc(100% - 42px); }
  .pin-bar__actions { width: 100%; }
  .pin-bar__actions .btn { flex: 1; }

  .section-nav { padding: 8px 0; }
  .section-nav__item { padding: 6px 12px; font-size: 12px; }
  .wsection { margin-top: 22px; }

  .chat-messages { max-height: none; min-height: 240px; padding: 14px; }
  .chat-msg { max-width: 94%; }

  .tech-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
}

@media (hover: none) and (pointer: coarse) {
  /* On touch the floor is the full 48px — growers tap this with gloves on. */
  .btn, button { min-height: var(--touch-min); }
  input, select, textarea { font-size: 16px; min-height: var(--touch-min); }
  /* Footer links sit stacked with no gap between them, so a 40px row means the
     tap target of the row above ends exactly where the next begins. */
  .service-footer li a { min-height: var(--touch-min); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ─── Danger button ─────────────────────────────────────────── */
.btn--danger { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn--danger:hover:not(:disabled) { background: var(--danger-hover); border-color: var(--danger-hover); color: #fff; }
.btn--danger:active:not(:disabled) { background: var(--danger-press); border-color: var(--danger-press); }

/* Loading keeps the button's size so the row never reflows mid-submit. */
.btn.is-loading { opacity: .75; pointer-events: none; }

/* ─── Sizing utilities ──────────────────────────────────────── */
/* Production serves `style-src 'self'`, so a width that varies per instance
   cannot be an inline style — these stand in for the handful of cases.
   One scale for the whole site: seven page stylesheets had each started a
   private `.xx-wNN` ladder for their own progress and range bars. */
.ddr-w24 { width: 24%; }
.ddr-w33 { width: 33%; }
.ddr-w34 { width: 34%; }
.ddr-w40 { width: 40%; }
.ddr-w41 { width: 41%; }
.ddr-w45 { width: 45%; }
.ddr-w60 { width: 60%; }
.ddr-w66 { width: 66%; }
.ddr-w67 { width: 67%; }
.ddr-w70 { width: 70%; }
.ddr-w72 { width: 72%; }
.ddr-w80 { width: 80%; }
.ddr-w88 { width: 88%; }
.ddr-w100 { width: 100%; }
.ddr-h13 { height: 13px; }
.ddr-h16 { height: 16px; }
.ddr-h20 { height: 20px; }

/* ─── Component gallery layout ──────────────────────────────── */
.cmp-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-bottom: 20px; }
.cmp-row:last-child { margin-bottom: 0; }
.cmp-row--baseline { align-items: flex-end; }
.cmp-grid {
  display: grid; gap: 16px; margin-bottom: 20px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.cmp-grid:last-child { margin-bottom: 0; }
body[data-page="components"] .page > .container > .card { margin-bottom: 24px; }

/* ═══════════════════════════════════════════════════════════════
   FIELD MODE — the mobile app design applied to the web front end

   The design canvas ships three phone layouts and one desktop layout;
   the confirmed pair is "Field Mode" on the phone and the existing web
   structure on desktop. Everything below is that difference, and only
   that difference: the desktop rules here are the three layouts the
   canvas changed (scan, weather, chat avatars), and the phone rules
   live in the media query at the end.

   Colours, type and spacing are the tokens above — the canvas was drawn
   from this stylesheet, so nothing new is introduced here.
   ═══════════════════════════════════════════════════════════════ */

:root {
  /* ความสูงจริงของแถบล่าง = ปุ่ม 56 + ระยะล่าง 22 + พื้นที่ปลอดภัยของเครื่อง
     ต้องรวม env() ไว้ในตัวแปรตัวนี้ ไม่ใช่ไปบวกซ้ำที่แต่ละที่ — เพราะทั้ง
     padding ท้ายหน้าและขอบล่างของช่องมองภาพต่างก็อิงค่านี้ ถ้าตัวแปรบอก 79
     แต่แถบจริงสูง 113 บนเครื่องที่มีแถบโฮม บรรทัดสุดท้ายของทุกหน้าจะหายไป
     ใต้แถบพอดี 34px

     79 = เส้นขอบบน 1 + แถวปุ่ม 56 + ระยะล่าง 22 */
  --tabbar-h: calc(79px + env(safe-area-inset-bottom, 0px));
}

@keyframes ddrSheetUp {
  from { transform: translateY(40px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

/* ─── Bottom tab bar ────────────────────────────────────────────
   Phone only; the top tab row stays the navigation everywhere else.
   Four destinations plus a raised centre button for the assistant —
   which is the one thing a grower opens from anywhere, and the only
   screen that is a conversation rather than a tool. */
.tabbar { display: none; }

.tabbar__item {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; height: 56px; padding: 0 2px;
  border-top: 2px solid transparent;
  font-size: 10.5px; font-weight: 600; line-height: 1.2;
  color: var(--text-muted); text-decoration: none;
  transition: color .15s, border-color .15s;
}
.tabbar__item i { font-size: 19px; }
.tabbar__item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.tabbar__item.active { color: var(--green); border-top-color: var(--green); }

/* The centre column is not a tab: the button overhangs the bar, so it needs
   its own track rather than sharing the flex row's baseline.
   กว้างกว่าแท็บอื่นเล็กน้อยเพราะป้าย "DooDurian AI" ยาวกว่าคำเดียวของแท็บอื่น —
   ถ้าแบ่งเท่ากันมันจะห่อเป็นสองบรรทัด แล้วแถบทั้งแถบสูงขึ้น 6px เกินค่าที่
   --tabbar-h บอกไว้ ซึ่งเป็นค่าที่ทุกหน้าใช้กันเนื้อหาไม่ให้มุดใต้แถบ */
.tabbar__centre {
  flex: 1.3; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.tabbar__fab {
  width: 58px; height: 58px; margin-top: -24px;
  border-radius: var(--r-full);
  /* The ring is the page behind it, not a stroke: it is what separates the
     button from the bar when the two greens meet. */
  border: 4px solid var(--white);
  background: var(--green); color: #fff;
  font-size: 21px; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 20px rgba(0, 185, 107, .35);
  transition: background-color .15s;
}
.tabbar__fab:active { background: var(--green-press); color: #fff; }
/* line-height ต้องกำหนดเอง — ป้ายนี้อยู่นอก .tabbar__item จึงรับค่าของ body ซึ่ง
   ตั้งไว้เป็นความสูงคงที่สำหรับเนื้อความ พอเอามาใช้กับตัวอักษร 10.5px มันสูงกว่า
   ตัวอักษรเท่าตัว แล้วดันคอลัมน์กลางให้สูงเกินแถว 56px ที่ --tabbar-h อิงอยู่ */
.tabbar__label {
  font-size: 10.5px; line-height: 1.2; font-weight: 600;
  color: var(--text-muted); white-space: nowrap;
}
.tabbar__centre .tabbar__fab.active + .tabbar__label { color: var(--green); }

/* ─── Chat avatars ──────────────────────────────────────────────
   Both sides get one, which is what makes the column read as a
   conversation rather than as two stacks of tinted boxes. Drawn as a
   pseudo-element so every kind of bubble the script builds — answer,
   thinking, failed turn — gets it without the script knowing. */
.chat-msg { position: relative; }
.chat-msg::before {
  content: ""; position: absolute; bottom: 0;
  width: 32px; height: 32px; border-radius: var(--r-full);
  border: 1px solid var(--green-soft-border); background-color: var(--white);
  background-repeat: no-repeat; background-position: center; background-size: cover;
}
.chat-msg--bot {
  margin-left: 42px;
  /* The bubble's own corner is squared off towards its avatar. */
  border-bottom-left-radius: 4px;
}
.chat-msg--bot::before {
  left: -42px;
  background-image: url("./assets/doodurian-logo.png");
}
.chat-msg--user { margin-right: 42px; border-bottom-right-radius: 4px; }
.chat-msg--user::before {
  right: -42px;
  background-color: var(--green-soft);
  /* Font Awesome's user glyph — the person on the other side of the thread
     has no picture, and a tinted initial would imply an account. */
  content: "\f007";
  font-family: "Font Awesome 6 Free"; font-weight: 900;
  font-size: 13px; color: var(--green-deep);
  display: flex; align-items: center; justify-content: center;
}

/* Desktop: scan in two columns — the rule lives in css/ai-tools.css,
   which loads after this file and would otherwise win with .ait-stack's
   own `display: flex`. */

/* ─── Desktop: weather map beside the readout ───────────────────
   The picker is a map plus everything you do with it. Stacked, the button
   that loads the forecast sat a full map-height below the map you just
   clicked; side by side the whole choice is one screen. Laid out on the
   card itself so the markup stays a plain sequence of blocks on a phone. */
@media (min-width: 901px) {
  #pinCard {
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    column-gap: 20px; align-items: start;
  }
  #pinCard > .section-title { grid-column: 1 / -1; }
  #pinCard > .map-pick { grid-column: 1; grid-row: 2 / span 2; margin-bottom: 0; }
  #pinCard > .map-pick__readout { grid-column: 2; grid-row: 2; }
  #pinCard > .weather-form { grid-column: 2; grid-row: 3; }
}

/* ═══════════════════════════════════════════════════════════════
   FIELD MODE — phone
   Below this width the service stops being a page with a tab row and
   becomes the app in the design: navigation at the thumb, the home page
   led by a photograph, and scanning done through the camera rather than
   a file picker.
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 620px) {

  /* One navigation, not two: the tab row is what moved to the bottom. */
  .service-tabs { display: none; }

  /* หัวแถวเดียวตามดีไซน์ — ที่ 375px แบรนด์กับปุ่มสองปุ่มไม่มีทางอยู่แถวเดียวกันได้
     ปุ่มจึงห่อลงไปกินความสูงอีกแถวเต็ม ๆ ก่อนถึงเนื้อหาจริง
     "นักพัฒนา" เป็นทางเข้าเอกสาร API ไม่ใช่สิ่งที่เปิดจากมือถือกลางสวน และยังอยู่
     ครบทั้งใน footer และหน้าเกี่ยวกับ ส่วนลิงก์เว็บหลักคือปุ่มที่ดีไซน์เก็บไว้ */
  .service-bar__top { flex-wrap: nowrap; gap: 10px; padding: 10px 0 12px; }
  .service-actions [data-nav="docs"] { display: none; }
  .service-actions .btn { font-size: 13.5px; padding: 0 12px; }

  .tabbar {
    display: flex; align-items: stretch;
    /* เหนือ .cam (45) เพราะปุ่ม AI ตรงกลางล้ำขึ้นไป 24px เหนือขอบบนของแถบ —
       ถ้าอยู่ใต้ ช่องมองภาพจะกินครึ่งบนของปุ่มไปเฉย ๆ ยังต่ำกว่า scrim ของ
       sheet ผลลัพธ์ (50) ซึ่งต้องคลุมทุกอย่างตอนอ่านผล */
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 46;
    padding: 0 6px calc(22px + env(safe-area-inset-bottom, 0px));
    background: var(--white);
    border-top: 1px solid var(--border);
  }
  /* The bar floats over the document, so every page has to end above it. */
  body { padding-bottom: var(--tabbar-h); }

  /* The banner is 1920×500 of brand that the bar directly under it already
     says in words, and the home page now opens on a photograph instead. */
  .service-banner { display: none; }

  /* ─── Home: led by the image ───────────────────────────────────
     Full-bleed out of the container gutter — a photograph with a page
     margin down each side reads as an illustration in an article, and
     this one is the top of the app. */
  .home-hero {
    display: block; padding: 0; gap: 0;
    margin-inline: calc(var(--gutter) * -1);
    margin-bottom: 34px;
    border: 0; border-radius: 0; background: none; overflow: visible;
  }
  .home-hero::before { content: none; }

  /* Scrim first, photo second: white type over a sunlit canopy needs the
     dark ramp to hold, and the ramp is what carries the contrast, not the
     text colour. image-set for WebP with the JPEG kept as the fallback
     declaration above it. */
  .home-hero__intro {
    padding: 104px 20px 30px;
    background-image:
      linear-gradient(180deg, rgba(0, 52, 29, .10) 0%, rgba(0, 52, 29, .55) 52%, rgba(0, 90, 50, .88) 100%),
      url("./assets/hero-durian.jpg");
    background-image:
      linear-gradient(180deg, rgba(0, 52, 29, .10) 0%, rgba(0, 52, 29, .55) 52%, rgba(0, 90, 50, .88) 100%),
      image-set(url("./assets/hero-durian.webp") type("image/webp"), url("./assets/hero-durian.jpg") type("image/jpeg"));
    background-size: cover; background-position: center 38%;
  }
  .home-hero__eyebrow {
    margin-bottom: 10px; padding: 4px 11px;
    background: rgba(255, 255, 255, .2); border-color: rgba(255, 255, 255, .35);
    color: var(--cream); box-shadow: none; font-size: 11.5px;
  }
  /* The name is in the bar 60px above and in the banner's place; on a phone
     the headline is the sentence that says what it does. */
  .home-hero__brand { display: none; }
  .home-hero__tagline {
    margin-top: 0; font-size: 26px; line-height: 1.2; font-weight: 700;
    color: #fff; text-shadow: 0 2px 8px rgba(0, 0, 0, .45);
  }
  .home-hero__tagline em { color: var(--cream); }
  .home-hero__lead {
    margin-top: 8px; font-size: 13.5px; line-height: 1.6;
    color: rgba(255, 255, 255, .92); text-shadow: 0 1px 6px rgba(0, 0, 0, .4);
  }

  /* The card that lifts off the photo's bottom edge — the one place on the
     page where a shadow is doing work rather than decorating. */
  .home-hero__cta {
    position: relative; margin: -18px 16px 0; padding: 14px;
    background: var(--white); border: 1px solid var(--border);
    border-radius: 16px; box-shadow: 0 8px 24px rgba(21, 63, 50, .10);
  }
  .home-hero__actions { flex-direction: column; align-items: stretch; margin-top: 0; gap: 8px; }
  /* ปุ่มฮีโร่ย่อลงเหลือ 48px เฉพาะเดสก์ท็อป — บนมือถือปุ่มคือเป้านิ้วโป้ง
     จึงคืนขนาด .btn--lg เต็ม ๆ ตามชุดออกแบบจอเล็กที่ใช้ 50–52px */
  .home-hero__actions .btn {
    width: 100%;
    height: var(--control-lg); padding: 0 24px;
    border-radius: var(--r-sm); font-size: 17px;
  }
  /* เส้นคั่นเหนือแถวติ๊กถูกเป็นของเดสก์ท็อป — บนมือถือสามข้อนี้อยู่ในการ์ดขาว
     ที่มีขอบของตัวเองอยู่แล้ว เส้นอีกเส้นจะกลายเป็นขีดซ้อนขีด */
  .home-hero__points {
    justify-content: center; gap: 6px 16px;
    margin-top: 10px; padding-top: 0; border-top: 0;
  }
  .home-hero__points li { font-size: 11.5px; }
  .home-hero__points i { font-size: 10px; }

  /* LINE, as a card on a phone. The artwork used to be hidden here because the
     crop that was in its place was 1040×880 — nearly square, and at this width
     that is most of a screen of scrolling to say what the button says. The full
     poster is 16:9, so it lands at about 200px tall and stays. */
  .home-hero__promo {
    align-items: stretch; gap: 10px;
    margin: 18px 16px 0; padding: 12px 14px;
    border: 1px solid var(--green-soft-border); border-radius: 14px;
    background: var(--green-soft);
  }
  .home-hero__promo-foot { flex-direction: row; align-items: center; gap: 10px; }
  .home-hero__promo-foot .btn { flex: 1; min-width: 0; font-size: 13.5px; padding: 0 10px; }
  .home-hero__promo-more { flex: none; font-size: 12px; }

  /* Tools as a list. Three tall cards is three screens of scrolling to
     reach a page that is one tap away from the bar at the bottom. */
  .service-card {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 0 13px;
    padding: 13px 14px; border-radius: 14px;
  }
  .service-card::before { content: none; }
  .service-card:hover { transform: none; box-shadow: none; }
  .service-card__top { grid-column: 1; grid-row: 1 / span 2; width: auto; margin: 0; }
  .service-card__top .badge { display: none; }
  .service-card__icon { width: 42px; height: 42px; font-size: 17px; }
  .service-card h3 { grid-column: 2; grid-row: 1; font-size: 15px; line-height: 21px; margin: 0; }
  /* line-height ต้องกว้างพอให้สระบนกับวรรณยุกต์อยู่ในกล่องบรรทัดของตัวเอง —
     ถ้าบีบกว่านี้ มาร์กของบรรทัดที่สามจะล้นขึ้นมาโผล่เหนือรอยตัดของ line-clamp
     กลายเป็นรอยเปื้อนลอยอยู่ท้ายบรรทัดที่สอง */
  .service-card p {
    grid-column: 2; grid-row: 2; margin: 0; flex-grow: 0;
    font-size: 12px; line-height: 19px;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  /* Only the arrow survives: at this width the words of the call to action
     and the title of the card it belongs to cannot both fit on the row.
     font-size drops the text node — it has no element to hide it by. */
  .service-card__foot {
    grid-column: 3; grid-row: 1 / span 2;
    width: auto; padding: 0; border-top: 0; font-size: 0;
  }
  .service-card__go { font-size: 13px; }

  /* Avatars cost 42px of the row, so the bubble's ceiling comes off the
     space that is left rather than off the column. */
  .chat-msg { max-width: calc(100% - 42px); }
}

/* ═══════════════════════════════════════════════════════════════
   FIELD MODE — camera scanning
   The design's scan screen is the camera itself, not a form that opens
   one. It is offered only where it can actually work: a phone-width
   viewport, a secure context, and a device that grants a rear camera.
   Everywhere else the upload flow above stays exactly as it was, and
   the gallery button here falls back into it.
   ═══════════════════════════════════════════════════════════════ */
.cam { display: none; }

body[data-cam] { overflow: hidden; }
body[data-cam] .cam {
  display: flex; flex-direction: column;
  position: fixed; left: 0; right: 0; top: 0; bottom: var(--tabbar-h);
  z-index: 45; background: #0C1F18;
}

.cam__stage { flex: 1; position: relative; overflow: hidden; }
.cam__video { width: 100%; height: 100%; object-fit: cover; display: block; }

.cam__hint {
  position: absolute; top: 14px; left: 12px; right: 12px;
  text-align: center; font-size: 12px; line-height: 18px;
  color: rgba(255, 255, 255, .82);
}
.cam__hint span {
  display: inline-block; padding: 6px 14px; border-radius: var(--r-full);
  background: rgba(0, 0, 0, .35);
}

.cam__close {
  position: absolute; top: 10px; right: 10px;
  width: var(--touch-min); height: var(--touch-min); border-radius: var(--r-full);
  border: 1px solid rgba(255, 255, 255, .3); background: rgba(0, 0, 0, .38);
  color: #fff; font-size: 16px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Corner brackets rather than a full rectangle: the frame has to say where
   to put the leaf without covering the leaf while you put it there. */
.cam__frame {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(230px, 62vw); aspect-ratio: 1; pointer-events: none;
}
.cam__frame span { position: absolute; width: 34px; height: 34px; }
.cam__frame span:nth-child(1) { top: 0; left: 0; border-top: 3px solid var(--green); border-left: 3px solid var(--green); border-top-left-radius: 10px; }
.cam__frame span:nth-child(2) { top: 0; right: 0; border-top: 3px solid var(--green); border-right: 3px solid var(--green); border-top-right-radius: 10px; }
.cam__frame span:nth-child(3) { bottom: 0; left: 0; border-bottom: 3px solid var(--green); border-left: 3px solid var(--green); border-bottom-left-radius: 10px; }
.cam__frame span:nth-child(4) { bottom: 0; right: 0; border-bottom: 3px solid var(--green); border-right: 3px solid var(--green); border-bottom-right-radius: 10px; }

.cam__busy {
  position: absolute; inset: 0; background: rgba(12, 31, 24, .78);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  color: rgba(255, 255, 255, .85); font-size: 14px;
}
.cam__spinner {
  width: 26px; height: 26px; border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, .25); border-top-color: var(--green);
  animation: spin .7s linear infinite;
}

/* ไม่ต้องเผื่อ safe-area ที่นี่ — .cam จบที่ขอบบนของแถบล่าง ซึ่งเป็นตัวที่เผื่อ
   พื้นที่แถบโฮมไว้แล้ว บวกอีกรอบจะกลายเป็นช่องว่างดำเปล่า ๆ เหนือแถบ */
.cam__bar {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 12px 20px 16px;
  background: #0C1F18;
}
.cam__side {
  flex: none; width: 48px; height: 52px; border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, .25); background: rgba(255, 255, 255, .08);
  color: rgba(255, 255, 255, .85); font-size: 17px; cursor: pointer;
}
.cam__side:hover { background: rgba(255, 255, 255, .16); }
.cam__shutter {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  height: 52px; border-radius: 14px; border: none;
  background: var(--green); color: #fff;
  font-family: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 185, 107, .35);
}
.cam__shutter:hover:not(:disabled) { background: var(--green-hover); }
.cam__shutter:active:not(:disabled) { background: var(--green-press); }
.cam__shutter:disabled { opacity: .55; cursor: not-allowed; box-shadow: none; }

/* ─── Result as a sheet over the viewfinder ─────────────────────
   Coming back to a scrolled page loses where the result sits relative to
   what was in front of the lens. The sheet keeps both. */
.cam__scrim { display: none; }
body[data-cam="result"] .cam__scrim {
  display: block; position: fixed; inset: 0; z-index: 50;
  background: rgba(12, 31, 24, .5);
}
body[data-cam="result"] #scanResultState {
  display: block; position: fixed; left: 0; right: 0; bottom: 0; top: auto;
  z-index: 51; max-height: 86vh; overflow-y: auto;
  margin: 0; padding: 10px 16px calc(36px + env(safe-area-inset-bottom, 0px));
  background: var(--white); border-radius: 24px 24px 0 0;
  animation: ddrSheetUp .3s ease;
}
/* The grabber — it is what says the panel is a sheet and can be dragged
   down, before anything in it has been read. */
body[data-cam="result"] #scanResultState::before {
  content: ""; display: block;
  width: 44px; height: 5px; margin: 4px auto 12px;
  border-radius: var(--r-full); background: var(--border-strong);
}
body[data-cam="result"] #scanResultState .card--raised { border: 0; padding: 0; box-shadow: none; }

/* A laptop webcam pointed at a leaf is not the design's use case, and the
   drop zone is better on a machine with files on it. */
@media (min-width: 621px) {
  body[data-cam] { overflow: auto; }
  body[data-cam] .cam { display: none; }
  .cam__scrim { display: none !important; }
}
